Metadata Signature Creation
To ensure the declaration is verifiable, all metadata must be signed using a private key, which generates a cryptographic signature proving its authenticity. Liccium supports two approaches for signing: X.509 Certificate-based signing and Keypair-based signing with embedded credentials.
Overview
Metadata signatures in Liccium serve multiple purposes:
- Authentication: Proves the identity of the declarer
- Integrity: Ensures the metadata hasn't been tampered with
- Non-repudiation: Provides proof that the declaration was made by the certificate holder
Two Signing Approaches
Approach 1: X.509 Certificate
- ✓ Uses X.509 certificate chain
- ✓ Certificate embedded in JWT header (x5c)
- ✓ Requires certificate authority validation
- ✓ Best for enterprise/organizational use
Approach 2: Generated Keypair
- ✓ Uses self-generated EC keypair
- ✓ Public key embedded as JWK in JWT header
- ✓ Credentials included in publicMetadata
- ✓ Best for flexible, self-managed identities
Prerequisites
Before creating signatures, ensure you have:
Required Components
- ✓ EC (P-256) private key
- ✓ Node.js environment
- ✓ Structured public metadata
- ✓ Verifiable credentials (for keypair approach)
Setup Requirements
- ✓ JWT signing library (jsonwebtoken)
- ✓ Crypto module for key operations
- ✓ Valid declarerId (DID format)
- ✓ Complete metadata structure
Approach 1: X.509 Certificate-Based Signing
This approach uses X.509 certificates to establish identity. The certificate chain is embedded in the JWT header using the x5c field.
Step 1: Prepare Your Public Metadata
Structure your public metadata according to Liccium requirements:
Code
Metadata Requirements: Ensure your metadata includes all required fields: $schema, @context, iscc, name, timestamp, declarerId, and credentials. Missing required fields will cause signature validation to fail.
Step 2: Load Your Certificate
Load your X.509 certificate and private key:
Code
Step 3: Create the JWT Signature
Generate a JWT signature using your certificate and private key:
Code
Step 4: Complete Certificate-Based Example
Full example with validation:
Code
Approach 2: Keypair-Based Signing with Embedded JWK
This approach uses a self-generated EC keypair. The public key is embedded in the JWT header as a JSON Web Key (JWK), and verifiable credentials are included in the publicMetadata.
Step 1: Generate an EC Keypair
Code
Step 2: Convert Public Key to JWK
Code
Step 3: Create JWT Signature with JWK in Header
Code
Step 5: Complete Keypair-Based Example
Code
Signature Verification
Decode the JWT (without verification) to inspect structure:
Code
Certificate-based (x5c): header contains x5c with base64-encoded certificate.
Keypair-based (jwk): header contains jwk with EC P-256 public key.
Security Best Practices
Security Considerations:
- Never log or expose private keys
- Use secure key storage in production (HSM, key vaults)
- Implement proper error handling for signing failures
- Validate all input data before signing
- Store credentials securely and validate before inclusion
Key Management
- Secure Storage – Store private keys in HSM or key vaults
- Access Control – Limit access to private keys to authorized systems
- Key Rotation – Regularly rotate keys/certificates and update DID documents
- Monitoring – Monitor key usage and detect unauthorized signing
Common Issues and Solutions
| Issue | Cause | Solution |
|---|---|---|
| Invalid signature format | Incorrect algorithm or header | Use ES256 and proper x5c or jwk header |
| Certificate/JWK not found | Missing x5c or jwk in JWT header | Include base64 certificate or JWK in header |
| Validation fails | Metadata missing required fields | Validate structure before signing ($schema, @context, iscc, name, timestamp, declarerId, credentials) |
| Key mismatch | Private key doesn't match certificate/public key | Verify key pair correspondence |
| Credentials validation fails | Missing or invalid credentials array | Ensure credentials is a non-empty array with valid VC structure |
Integration with Liccium
Include your signature in the declaration payload:
Code
For complete declaration examples, see the Declaration API.
Signing Registry Objects
The following is an example for the TDMAI registry for AI preferences.
If your declaration includes a tdmaiRegistry payload (aggregated opt-out data from plugins such as TDMrep and FAIA), the same signing process must be applied to it independently, using the same private key and approach chosen above.
The registry object requires two additional fields in the declaration payload:
tdmaiRegistrySignature– a JWT signature of thetdmaiRegistrystring, produced identically to the mainsignaturetdmaiRegistryTsaSignature– a TSA timestamp signature over thetdmaiRegistrypayload, produced identically to the maintsaSignature
Example
Code
The tdmaiRegistrySignature and tdmaiRegistryTsaSignature fields are only required when a tdmaiRegistry value is present in the declaration. If no registry payload is provided, these fields can be omitted entirely.
Next Steps
- TSA Signature Creation – Add timestamp signatures for temporal verification
- Test your signatures with the Declaration API
- Implement error handling for production use
- Monitor certificate/key expiration and plan for renewal