License XML v1.0 Protocol Specification
Specification for the software license file protocol.
| Property | Value |
|---|---|
| Protocol name | License XML |
| Protocol version | 1.0 |
| File format | XML 1.0 |
| Character encoding | UTF-8 |
| Signature algorithm | Ed25519 |
| XML canonicalization | Canonical XML 1.0 (without comments) |
| Digest algorithm | SHA-256 (for machine fingerprints) |
| Signature encoding | Base64 |
| Design goals: Human-readable, editable, tamper-evident, and verifiable offline |
1. Protocol Goals
License XML v1.0 defines a software license file format.
A license must have the following properties:
- License content is stored as plain-text XML.
- Users can open a license and read all authorization information.
- Users can edit the XML directly.
- If a user changes authorization information that is part of the protected canonical content, verification of the original signature must fail.
- Only the licensing server holds the license-issuing private key.
- Clients hold only public keys.
- Clients can verify license signatures without accessing a server.
- Licenses support validity periods, feature permissions, quantity limits, customer information, and machine binding.
- Licenses support signing-key rotation.
- License files do not provide confidentiality.
- This protocol defines only the offline license format and signature. Revocation services, hardware fingerprint collection, and usage metering belong to the application layer.
2. Security Model
License XML v1.0 uses asymmetric digital signatures.
The server has:
Private KeyThe client has:
Public KeyIssuance:
License Payload
│
▼
XML Canonicalization
│
▼
Ed25519 Sign
│
▼
SignatureVerification:
License XML
│
▼
Read Payload
│
▼
XML Canonicalization
│
▼
Ed25519 Verify
│
▼
Valid / InvalidThe client must not contain the license-issuing private key.
3. Security Boundary
License XML v1.0 guarantees:
When signature verification succeeds, the Payload content is identical to the content at issuance, and the signature was produced by the corresponding private key.
License XML v1.0 does not guarantee that:
- Users cannot modify the client program.
- Users cannot patch the client.
- Users cannot debug the client.
- Users cannot change the system clock.
- Users cannot simulate the client runtime environment.
The license system protects:
License Integritynot:
Application Integrity4. File Extension
The license file extension is fixed as:
.license.xmlExample:
my-product.license.xml5. XML Document Structure
The license root element is fixed as:
<license>The complete structure is:
<?xml version="1.0" encoding="UTF-8"?>
<license version="1">
<payload>
<licenseId>...</licenseId>
<product>
<id>...</id>
<version>...</version>
</product>
<type>...</type>
<customer>
<id>...</id>
<name>...</name>
<email>...</email>
</customer>
<validity>
<issuedAt>...</issuedAt>
<expiresAt>...</expiresAt>
</validity>
<features>
...
</features>
<limits>
...
</limits>
<machine>
...
</machine>
</payload>
<signature
algorithm="Ed25519"
keyId="...">...</signature>
</license>6. Root Element
The root element is:
<license version="1">version is the license file protocol version. In v1.0:
version = "1"Any other value is an unknown protocol version.
7. Payload
All authorization information must be inside the <payload> element.
The Payload is the complete signed object. Its child elements must appear in the order shown in Section 5. Each single-value element must appear exactly once. The only optional elements are customer/email and validity/expiresAt, with expiresAt required or omitted according to the license type.
All of the following are within the signature's protection:
licenseId
product
type
customer
validity
features
limits
machineChanging the canonical content of any field must cause signature verification to fail. XML comments are not signed. Changes only to XML lexical form, such as attribute order, quote style, and character references, may not affect the signature.
Version 1 documents must not use XML namespaces, DTDs, external entities, or internal entity declarations. Undefined elements, duplicate singleton elements, duplicate attributes, and any other content that does not conform to the structure below are forbidden. Parsers must reject DTDs and entity declarations and must not attempt to load external resources.
8. License ID
Structure:
<licenseId>LIC-20260924-00123</licenseId>A License ID must be:
- Globally unique.
- UTF-8.
- 1–128 bytes long.
- Case-sensitive.
The License ID is only a unique license identifier; it does not participate in authorization decisions.
9. Product
Product structure:
<product>
<id>my-product</id>
<version>5</version>
</product>id identifies the product. version identifies the major product version to which the license applies.
id must be 1–128 UTF-8 bytes. version must be a non-negative decimal integer without leading zeroes.
For example:
<product>
<id>my-editor</id>
<version>5</version>
</product>authorizes version 5.x of my-editor.
10. License Type
License type:
<type>trial</type>The protocol supports exactly:
trial
subscription
perpetualtrial
A trial license. <expiresAt> is required.
subscription
A subscription license. <expiresAt> is required.
perpetual
A perpetual license. <expiresAt> must be omitted.
11. Customer
Structure:
<customer>
<id>CUSTOMER-001</id>
<name>Acme Corporation</name>
<email>admin@example.com</email>
</customer>| Field | Required | Meaning |
|---|---|---|
id | Yes | Unique customer ID |
name | Yes | Customer name |
email | No | Customer email |
Customer information is within the signature's protection. Therefore, changing:
<name>Acme Corporation</name>to:
<name>Other Corporation</name>must cause license verification to fail.
12. Validity
Structure:
<validity>
<issuedAt>2026-09-24T08:00:00Z</issuedAt>
<expiresAt>2027-09-24T08:00:00Z</expiresAt>
</validity>issuedAt is required. expiresAt is required for trial and subscription licenses and must be omitted for perpetual licenses. All times must use ISO 8601 UTC in this format:
YYYY-MM-DDTHH:mm:ssZFor example:
2026-09-24T08:00:00ZTimes must be valid calendar times. Fractional seconds, timezone offsets, and leap seconds are not accepted. expiresAt is an exclusive boundary and must be later than issuedAt. A verifier must not add clock tolerance that is not defined here.
13. Features
Feature permissions use:
<features>
<feature id="api" enabled="true"/>
<feature id="export" enabled="true"/>
<feature id="advanced" enabled="true"/>
<feature id="sso" enabled="false"/>
</features>A feature ID must match:
[a-zA-Z0-9._-]+Each feature must have exactly two attributes, id and enabled. IDs must be unique within the same <features> element. enabled accepts only the strings true and false.
The enabled value determines whether a feature is authorized: enabled="true" means authorized, and enabled="false" means not authorized.
The entire <features> element is within the signature's protection.
14. Limits
Quantity limits use:
<limits>
<limit id="users">50</limit>
<limit id="projects">100</limit>
<limit id="apiRequestsPerMonth">100000</limit>
</limits>id identifies the limit type. It must match [a-zA-Z0-9._-]+ and be unique within the same <limits> element.
The element content must be a non-negative decimal integer without leading zeroes (0 or [1-9][0-9]*).
For example:
<limit id="users">50</limit>means a maximum of 50 users.
15. Machine Binding
Machine binding structure:
<machine>
<type>fingerprint</type>
<value>...</value>
</machine>type supports exactly:
none
fingerprintWhen <type>none</type> is used, <value> must be absent. When <type>fingerprint</type> is used, <value> must be present.
value is a machine fingerprint digest calculated by the application and provided to the verifier. The protocol does not define which hardware or system identifiers to collect or how to normalize them, and hardware information must not be collected implicitly. The client should compare it with the current fingerprint supplied by the caller; hexadecimal letters are compared case-insensitively. This comparison rule does not change the signature's protection of the original text in the payload. The digest must use the SHA-256 hexadecimal form and contain exactly 64 characters:
64 hexadecimal charactersFor example:
8A71F4D2...16. Signature
Signature structure:
<signature
algorithm="Ed25519"
keyId="production-2026">BASE64_SIGNATURE</signature>algorithm is fixed as:
Ed25519keyId selects the public key used for verification. It must be 1–128 ASCII characters and may contain only letters, digits, periods, underscores, and hyphens.
The text content of the signature element is the Ed25519 signature encoded as Base64. An Ed25519 signature must be exactly 64 bytes; the Base64 content must not contain whitespace.
17. Signature Scope
The signature applies only to the <payload> element. The <license> and <signature> elements are not signed.
The signature input is:
CanonicalXML1.0(payload)The canonicalization algorithm URI is fixed as http://www.w3.org/TR/2001/REC-xml-c14n-20010315 (without comments).
payload
↓
Canonical XML 1.0 (without comments)
↓
UTF-8 bytes
↓
Ed25519.Sign()18. Signature Verification
The client performs these steps:
Read product.license.xml
↓
Parse XML
↓
Validate protocol version
↓
Read signature.keyId
↓
Obtain corresponding Public Key
↓
Canonicalize(payload)
↓
Base64-decode(signature)
↓
Ed25519 VerifyIf verification returns false, the license status must immediately be set to:
INVALID_SIGNATURELicense authorization checks must not continue.
19. Key System
Each signing key has a unique keyId, for example:
production-2026
production-2027The server stores the private key. The client stores public keys, for example:
production-2026 → Public Key A
production-2027 → Public Key BThe private key must never be included in the client, license, installation package, source code, or frontend code.
20. Key Rotation
The server generates a new key, for example production-2027. The client publishes a new version that adds:
production-2027 → Public Key BThe server then issues new licenses using production-2027. Existing licenses continue to be verified with production-2026.
Whether old keys continue to be accepted is determined by the client version.
21. XML Comments
XML comments are allowed:
<!-- License generated by License Server -->Comments are not Payload data and do not affect license business content. Clients must not execute authorization logic based on XML comments.
22. XML Formatting
Element text values must be non-empty and must not contain leading or trailing whitespace. Customer email is optional; if present, it must be non-empty. The feature and limit collections may be empty.
The following XML:
<limit id="users">50</limit>and:
<limit id="users">
50
</limit>do not represent the same Payload data. Therefore, license generators must emit canonical formatting.
Payloads must not contain insignificant text whitespace. Canonical XML does not remove whitespace text between elements. If indentation is parsed as text nodes inside a Payload, that whitespace is part of the signed content; re-indentation may cause signature verification to fail. Comments are not signed. For example, 50 is the complete element value in:
<users>50</users>23. User Modification Rules
Users may edit the XML file. However, any field change that alters the canonical content of the Payload causes signature verification to fail:
Signature Verification = FAILFor example, changing:
<limit id="users">50</limit>to:
<limit id="users">5000</limit>results in INVALID_SIGNATURE. The same applies to changes to <expiresAt>, a feature such as <feature id="enterprise" enabled="false"/>, or customer information.
24. License Status
An offline verifier produces only the following statuses:
VALID
INVALID_FORMAT
INVALID_SIGNATURE
PRODUCT_MISMATCH
NOT_YET_VALID
EXPIRED
MACHINE_MISMATCHREVOKED may only be returned by an online revocation service; it is not a signature or offline verification status. This protocol does not define a revocation API. If the verifier cannot connect to or does not integrate with a revocation service, it must not claim that revocation status was checked.
Status checks occur in this fixed order:
1. XML Parse
2. Schema Validation
3. Signature Verification
4. Product Validation
5. Time Validation
6. Machine Validation
7. Pass the signed Feature / Limit data to application-layer authorization logicXML parsing or structural validation failures return INVALID_FORMAT. An invalid signature, unsupported algorithm, or unknown keyId returns INVALID_SIGNATURE. Product matching parameters may be omitted by the caller; if provided, all must match. Expiration uses an exclusive boundary: a license is expired when the current time is equal to expiresAt.
25. Complete License Example
Example license file:
<?xml version="1.0" encoding="UTF-8"?>
<license version="1">
<!--
License XML v1.0
-->
<payload>
<licenseId>LIC-20260924-00123</licenseId>
<product>
<id>my-product</id>
<version>5</version>
</product>
<type>subscription</type>
<customer>
<id>CUSTOMER-001</id>
<name>Acme Corporation</name>
<email>admin@example.com</email>
</customer>
<validity>
<issuedAt>2026-09-24T08:00:00Z</issuedAt>
<expiresAt>2027-09-24T08:00:00Z</expiresAt>
</validity>
<features>
<feature id="api" enabled="true"/>
<feature id="export" enabled="true"/>
<feature id="advanced" enabled="true"/>
<feature id="sso" enabled="false"/>
</features>
<limits>
<limit id="users">50</limit>
<limit id="projects">100</limit>
<limit id="apiRequestsPerMonth">100000</limit>
</limits>
<machine>
<type>fingerprint</type>
<value>8A71F4D2E39F0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123</value>
</machine>
</payload>
<signature
algorithm="Ed25519"
keyId="production-2026">V3J0dGVuU2lnbmF0dXJl...</signature>
</license>Note: The example signature is illustrative and is not a verifiable license. The example machine fingerprint is not the real fingerprint of a specific machine.
26. Issuer
The server performs:
Create Payload
↓
Validate structure
↓
Canonical XML 1.0 (without comments)
↓
Ed25519 Private Key
↓
Base64
↓
Create Signature
↓
Generate product.license.xmlThe server stores the private key and the license database.
27. Client
The client embeds public keys and reads:
product.license.xmlIt then performs:
Parse
↓
Schema validation
↓
Canonicalization
↓
Ed25519 verification
↓
Business validation28. License Database
The following is only a server implementation suggestion. It is not part of the protocol's data format or a mandatory server behavior. An application that provides online revocation may record:
licenseId
customerId
productId
licenseType
issuedAt
expiresAt
status
keyId
machineFingerprintstatus is one of:
active
revokedThe license file does not contain an active status. A signature proves the license content and its origin, not that the license has not been revoked.
The revocation service API, authentication, caching strategy, and failure behavior must be defined separately by the application.
29. Offline Licenses
In fully offline mode:
product.license.xml
↓
Signature
↓
Local validationLocal validation can check:
Whether the content has been modified
Whether the signature is authentic
Whether the product matches
Whether the license has expired
Whether the machine matches
The signed feature permissions and quantity limitsLocal validation reads only these signed authorization claims. Enforcing feature flags and metering usage is the application's responsibility. The caller must provide the machine fingerprint; the protocol and verifier do not collect it.
Local validation cannot determine whether the server has just revoked a license because the client has no server connection.
30. Online Licenses
An online license system may add a custom revocation service. The following is an illustrative, non-normative flow; this protocol does not define its interface or online verification statuses:
License ServerThe client may:
Read product.license.xml
↓
Verify local signature
↓
Query the server with licenseId
↓
Check status
↓
ACTIVE / REVOKEDAfter the server revokes a license:
status = revokedAn online client, as defined by its application, should reject a license after the revocation service successfully reports revoked. The application defines its own failure and caching policies.
31. Core Security Property
The core guarantee of License XML v1.0 can be summarized as:
The server has the private key
↓
The server signs the Payload
↓
The user can see the Payload
↓
The user can modify the Payload
↓
But the user does not have the private key
↓
The user cannot create a valid new Signature
↓
The modified License cannot pass verificationTherefore:
A license does not need to be hidden. Its security comes from its signature, not from content confidentiality.
32. Final Protocol Definition
License XML v1.0 is defined as follows:
File format XML 1.0
Encoding UTF-8
Root element license
Payload payload
Signature Ed25519
Signature scope payload
Canonicalization Canonical XML 1.0 (without comments)
Signature encoding Base64
Key identifier keyId
Time format ISO 8601 UTC
Machine fingerprint SHA-256The central relationship of the protocol is:
Signature = Ed25519.Sign(
PrivateKey,
CanonicalXML1.0(Payload)
)Verification:
Ed25519.Verify(
PublicKey,
CanonicalXML1.0(Payload),
Signature
)Any change that alters the canonical content of the Payload causes verification to fail.
This definition can serve as the protocol basis for License XML v1.0. Implementations need only implement the issuer and verifier according to this specification; no further design of license fields or signature mechanisms is needed.