CIP-8 — Message signing
CIP8 (Cardano Improvement Proposal 8) defines a standard for message signing using COSE (CBOR Object Signing and Encryption) signatures. The Cardano Client Library exposes builders and models for COSE Sign1 and COSE Sign so you can create and verify signatures that follow the specification. The focus is on providing small, composable pieces: you assemble headers, build a SigStructure, sign with your own signing provider, and serialize/deserialise with the COSE models.
Key Features
Section titled “Key Features”- COSE Sign1 and Sign builders
- Payload hashing toggle (Blake2b-224)
- Header helpers for protected and unprotected maps
- COSE key utilities for crv/x headers and serialization
Dependencies
Section titled “Dependencies”- Group ID: com.bloxbean.cardano
- Artifact ID: cardano-client-cip8
- Dependencies: common, crypto
Usage Examples
Section titled “Usage Examples”Creating COSE Sign1 Signatures
Section titled “Creating COSE Sign1 Signatures”// Protected headers (algorithm + optional content type or key id)HeaderMap protectedHeaders = new HeaderMap() .algorithmId(-8) // EdDSA .contentType(-1000); // Optional content type
// Unprotected headers for custom valuesHeaderMap unprotectedHeaders = new HeaderMap() .addOtherHeader(-100, "Some header value");
Headers headers = new Headers() ._protected(new ProtectedHeaderMap(protectedHeaders)) .unprotected(unprotectedHeaders);
byte[] payload = "Hello World".getBytes(StandardCharsets.UTF_8);
COSESign1Builder builder = new COSESign1Builder(headers, payload, false) .hashed(true); // Blake2b-224 hashing of payload
// For a 32-byte key. For an HD key's 64-byte extended key, use signExtended.SigningProvider signingProvider = CryptoConfiguration.INSTANCE.getSigningProvider();
SigStructure sigStructure = builder.makeDataToSign();byte[] signature = signingProvider.sign(sigStructure.serializeAsBytes(), privateKeyBytes);
COSESign1 coseSign1 = builder.build(signature);byte[] serialized = coseSign1.serializeAsBytes();String hexSignature = HexUtil.encodeHexString(serialized);Creating COSE Sign Signatures (Multiple Signers)
Section titled “Creating COSE Sign Signatures (Multiple Signers)”Headers headers = new Headers() ._protected(new ProtectedHeaderMap(new HeaderMap().algorithmId(-8))) .unprotected(new HeaderMap());
byte[] payload = "Multi-signer message".getBytes(StandardCharsets.UTF_8);
COSESignBuilder builder = new COSESignBuilder(headers, payload, false) .hashed(true);
SigStructure sigStructure = builder.makeDataToSign();List<COSESignature> signatures = new ArrayList<>();for (byte[] pvtKey : signerPrivateKeys) { byte[] sig = signingProvider.sign(sigStructure.serializeAsBytes(), pvtKey); signatures.add(new COSESignature() .headers(new Headers() ._protected(new ProtectedHeaderMap(new HeaderMap().algorithmId(-8))) .unprotected(new HeaderMap())) .signature(sig));}
COSESign coseSign = builder.build(signatures);byte[] serialized = coseSign.serializeAsBytes();Signature Verification
Section titled “Signature Verification”COSESign1 exposes signedData() to reconstruct the SigStructure for verification.
COSESign1 coseSign1 = COSESign1.deserialize(signatureBytes);SigStructure signedData = coseSign1.signedData();
boolean isValid = signingProvider.verify( coseSign1.signature(), signedData.serializeAsBytes(), publicKeyBytes);
byte[] payload = coseSign1.payload();Working with Headers
Section titled “Working with Headers”Headers headers = coseSign1.headers();HeaderMap protectedHeaders = headers._protected().getAsHeaderMap();HeaderMap unprotectedHeaders = headers.unprotected();
Object algorithmId = protectedHeaders.algorithmId();Object contentType = protectedHeaders.contentType();byte[] keyId = protectedHeaders.keyId();String customValue = unprotectedHeaders.otherHeaderAsString(-100);Creating COSE Keys
Section titled “Creating COSE Keys”COSEKey stores standard fields plus arbitrary other headers (e.g., crv, x).
COSEKey coseKey = new COSEKey() .keyType(1) // Octet Key Pair .algorithmId(-8) // EdDSA .addOtherHeader(-1, new UnsignedInteger(6)) // crv (Ed25519) .addOtherHeader(-2, new ByteString(publicKeyBytes));
byte[] keyBytes = coseKey.serializeAsBytes();COSEKey deserializedKey = COSEKey.deserialize(keyBytes);byte[] extractedPublicKey = deserializedKey.otherHeaderAsBytes(-2);API Reference
Section titled “API Reference”Core Classes
Section titled “Core Classes”COSESign1Builder
Section titled “COSESign1Builder”Builder class for creating COSE Sign1 signatures.
// ConstructorCOSESign1Builder(Headers headers, byte[] payload, boolean isPayloadExternal)
// MethodsSigStructure makeDataToSign() // Create signature structureCOSESign1 build(byte[] signature) // Build final signatureCOSESign1Builder hashed(boolean hashed) // Enable Blake2b-224 hashingCOSESignBuilder
Section titled “COSESignBuilder”Builder class for creating COSE Sign signatures with multiple signers.
// ConstructorCOSESignBuilder(Headers headers, byte[] payload, boolean isPayloadExternal)
// MethodsSigStructure makeDataToSign() // Create signature structureCOSESign build(List<COSESignature> signatures) // Build final signatureCOSESignBuilder hashed(boolean hashed) // Enable Blake2b-224 hashingHeaders
Section titled “Headers”Manages protected and unprotected headers.
Headers() // use fluent setters
// MethodsHeaders _protected(ProtectedHeaderMap protectedHeaders)Headers unprotected(HeaderMap unprotectedHeaders)HeaderMap _protected().getAsHeaderMap() // Decode protected mapHeaderMap unprotected() // Get unprotected headersHeaders copy() // Create a copyHeaderMap
Section titled “HeaderMap”Manages individual header maps.
// MethodsHeaderMap algorithmId(long or String algorithmId)HeaderMap contentType(long or String contentType)HeaderMap keyId(byte[] keyId)HeaderMap addOtherHeader(long key, String value) // Custom header; also DataItem, BigInteger, long and byte[] valuesObject algorithmId()Object contentType()byte[] keyId()byte[] otherHeaderAsBytes(long key)long otherHeaderAsLong(long key)String otherHeaderAsString(long key)COSEKey
Section titled “COSEKey”Represents a COSE key map.
COSEKey keyType(long or String)COSEKey algorithmId(long or String)COSEKey addOtherHeader(long key, DataItem value)COSEKey addOtherHeader(long key, long/String/BigInteger/byte[] value)byte[] serializeAsBytes()COSEKey deserialize(byte[] bytes)byte[] otherHeaderAsBytes(long key)long otherHeaderAsLong(long key)String otherHeaderAsString(long key)CIP8 Specification Details
Section titled “CIP8 Specification Details”COSE Sign1 Structure
Section titled “COSE Sign1 Structure”Components describe exactly what is signed and carried in the CBOR array:
- Protected Headers: Algorithm ID, content type, key ID
- Unprotected Headers: Custom headers
- Payload: Message data (optionally hashed with Blake2b-224)
- Signature: EdDSA signature over the structure
Signature Process
Section titled “Signature Process”- Header Creation: Set protected/unprotected headers with algorithm, optional content type, and custom keys.
- Payload Preparation: Optionally hash payload with Blake2b-224 (
hashed(true)). - Structure Building:
makeDataToSign()yields the Sig_Structure the signer must sign. - Signing: Sign the serialized Sig_Structure using EdDSA with your signing provider.
- Serialization: Build
COSESign1and encode viaserializeAsBytes().
Algorithm Identifiers
Section titled “Algorithm Identifiers”- -8: EdDSA (Edwards Curve Digital Signature Algorithm), the COSE algorithm id
- 1: Octet Key Pair key type
Advanced CIP8 Features
Section titled “Advanced CIP8 Features”COSE Encryption
Section titled “COSE Encryption”Encryption helpers are available as low-level containers:
COSEEncryptholds headers, ciphertext, and recipients.PasswordEncryptionwraps aCOSEEncrypt0instance (tag 16).PubKeyEncryptionwraps aCOSEEncryptinstance (tag 96).
You construct the underlying COSEEncrypt/COSEEncrypt0 yourself and then wrap it if you need the tagged form.
Integration with Other CIPs
Section titled “Integration with Other CIPs”- CIP30: Uses CIP8 for dApp wallet data signing
For more information about CIP8, refer to the official CIP8 specification.