1. Signature Support
The CBOR library contains support forCreating
and Validating
signatures using a specific scheme called
"CBOR Signature Format" (CSF).
Note that unlike COSE [RFC 9052], CSF leverages Deterministic Encoding, enabling constructs like the following:
{
1: {
1: "Space Shop",
2: "435.00",
3: "USD"
},
2: "spaceshop.com",
3: "FR7630002111110020050014382",
4: "https://banknet2.org",
5: "05768401",
6: "2024-08-22T09:34:08-05:00",
7: {
1: 38.8882,
2: 77.0199
},
/ Enveloped signature object /
-1: {
/ Signature algorithm = ESP256 /
1: -9,
/ Public key descriptor in COSE format /
4: {
/ kty = EC /
1: 2,
/ crv = P-256 /
-1: 1,
/ x /
-2: h'e812b1a6dcbc708f9ec43cc2921fa0a14e9d5eadcc6dc63471dd4b680c6236b5',
/ y /
-3: h'9826dcbd4ce6e388f72edd9be413f2425a10f75b5fd83d95fa0cde53159a51d8'
},
/ Signature value /
6: h'fdfe515197a79f1ebf149ee8a107c7578a7ec90bb7452df972f11f6105479b05963f2fe1f5a79fa9515af9bd5eece61a90b346527fe34b59b18732361be55746'
}
}
1: {
1: "Space Shop",
2: "435.00",
3: "USD"
},
2: "spaceshop.com",
3: "FR7630002111110020050014382",
4: "https://banknet2.org",
5: "05768401",
6: "2024-08-22T09:34:08-05:00",
7: {
1: 38.8882,
2: 77.0199
},
/ Enveloped signature object /
-1: {
/ Signature algorithm = ESP256 /
1: -9,
/ Public key descriptor in COSE format /
4: {
/ kty = EC /
1: 2,
/ crv = P-256 /
-1: 1,
/ x /
-2: h'e812b1a6dcbc708f9ec43cc2921fa0a14e9d5eadcc6dc63471dd4b680c6236b5',
/ y /
-3: h'9826dcbd4ce6e388f72edd9be413f2425a10f75b5fd83d95fa0cde53159a51d8'
},
/ Signature value /
6: h'fdfe515197a79f1ebf149ee8a107c7578a7ec90bb7452df972f11f6105479b05963f2fe1f5a79fa9515af9bd5eece61a90b346527fe34b59b18732361be55746'
}
}
The same object expressed as hex-encoded CBOR:
a801a3016a53706163652053686f7002663433352e30300363555344026d737061636573686f702e636f6d03781b465237363330303032313131313130303230303530303134333832047468747470733a2f2f62616e6b6e6574322e6f726705683035373638343031067819323032342d30382d32325430393a33343a30382d30353a303007a201fb404371b089a0275202fb405341460aa64c3020a3012804a401022001215820e812b1a6dcbc708f9ec43cc2921fa0a14e9d5eadcc6dc63471dd4b680c6236b52258209826dcbd4ce6e388f72edd9be413f2425a10f75b5fd83d95fa0cde53159a51d8065840fdfe515197a79f1ebf149ee8a107c7578a7ec90bb7452df972f11f6105479b05963f2fe1f5a79fa9515af9bd5eece61a90b346527fe34b59b18732361be55746
The sample was signed using the following COSE private key (here in diagnostic notation):
{
1: 2,
-1: 1,
-2: h'e812b1a6dcbc708f9ec43cc2921fa0a14e9d5eadcc6dc63471dd4b680c6236b5',
-3: h'9826dcbd4ce6e388f72edd9be413f2425a10f75b5fd83d95fa0cde53159a51d8',
-4: h'e97c4c15785c613e5037dc394c88366922ac6dc8fea63e019d990aed93ade01f'
}
1: 2,
-1: 1,
-2: h'e812b1a6dcbc708f9ec43cc2921fa0a14e9d5eadcc6dc63471dd4b680c6236b5',
-3: h'9826dcbd4ce6e388f72edd9be413f2425a10f75b5fd83d95fa0cde53159a51d8',
-4: h'e97c4c15785c613e5037dc394c88366922ac6dc8fea63e019d990aed93ade01f'
}
1
-7
represent application data
(which for compatibility with CSF must be supplied in a CBOR map
),
while the application-specific label -1
holds an associated enveloped signature.
Note that the application-specific labels (including the one holding the signature object), may be of any CBOR type. The only requirement is that they are unique.
2. Signature Validation
Signatures must for compatibility with CSF, be provided as a CBORmap
having a fixed set of labels
according to the following table:
Name | Label | Argument | Description |
---|---|---|---|
customData | 0 |
"Any" | Optional: data included in the CSF container. Also see Crypto Options. |
algorithm | 1 |
int | Signature algorithm using COSE identifiers. |
keyId | 3 |
"Any" | Optional: key identifier using any valid CBOR object.
Note that keyId must not be used together with
publicKey or certificatePath .
A compliant keyId must uniquely identify a specific signature key.
|
publicKey | 4 |
map |
Optional: public key in COSE format. Note that public key objects must not contain additional information like key identifiers or preferred signature algorithms. |
certificatePath | 5 |
array | Optional: certificate path supplied
as an array of byte string objects holding X.509 certificates in DER format,
where the first object must be the signature certificate.
Signature objects must not contain both certificatePath and publicKey elements.
|
signatureValue | 6 |
bstr | Signature value. |
- Signatures are validated by running the signature validation
algorithm
, over the CBOR binary representation of themap
object holding the application data including the enveloped signature object, with thesignatureValue
and its associated argument as the sole exception (highlighted in the example). Note that the length of the signature objectmap
must be updated to reflect the removal of thesignatureValue
attribute during validation. - Signature validation keys are either implicit, located via
keyId
attributes, or supplied inpublicKey
orcertificatePath
attributes. - If
publicKey
orcertificatePath
attributes are present, they must be used as signature validation keys. Note though that such keys must also be verified to be known and trusted by the signature-using application. - Signature validation keys must be checked for compatibility with
the
algorithm
attribute.
3. Tagged and Custom Signature Data
CSF containers can be further enhanced through a couple of options described in Crypto Options.4. Signature Algorithms
Currently supported COSE signature algorithms:
Name | Identifier | Notes |
---|---|---|
HS256 | 5 | |
HS384 | 6 | |
HS512 | 7 | |
Ed25519 | -50 | 1 |
Ed448 | -51 | 1 |
ESP256 | -9 | 1 |
ESP384 | -48 | 1 |
ESP512 | -49 | 1 |
PS256 | -37 | |
PS384 | -38 | |
PS512 | -39 | |
RS256 | -257 | |
RS384 | -258 | |
RS512 | -259 |
1] Updated for compliance with https://datatracker.ietf.org/doc/draft-ietf-jose-fully-specified-algorithms/.
5. Test Vectors
An extensive set of test vectors is currently available at: https://github.com/cyberphone/openkeystore/tree/master/testdata/cbor-signatures .
Use CBORPrinter
to list contents in a human-friendly way.
6. Using the Signature API
The following section outlines how the signature API is supposed to be used.Sample program:
package cbor_api_demo;
import org.webpki.cbor.CBORArray;
import org.webpki.cbor.CBORBoolean;
import org.webpki.cbor.CBORDecoder;
import org.webpki.cbor.CBORFloat;
import org.webpki.cbor.CBORHmacSigner;
import org.webpki.cbor.CBORHmacValidator;
import org.webpki.cbor.CBORInt;
import org.webpki.cbor.CBORMap;
import org.webpki.cbor.CBORString;
import org.webpki.crypto.HmacAlgorithms;
import org.webpki.util.HexaDecimal;
public class SignatureDemo {
static final byte[] HMAC_KEY = HexaDecimal.decode(
"7fdd851a3b9d2dafc5f0d00030e22b9343900cd42ede4948568a4a2ee655291a");
static final CBORInt GREETINGS_LABEL = new CBORInt(1);
static final CBORInt OTHER_DATA_LABEL = new CBORInt(2);
static final CBORInt SIGNATURE_LABEL = new CBORInt(-1);
public static void main(String[] args) {
// Create CBOR data to be signed.
CBORMap dataToBeSigned = new CBORMap()
.set(GREETINGS_LABEL, new CBORString("Hello Signed CBOR World!"))
.set(OTHER_DATA_LABEL, new CBORArray()
.add(new CBORFloat(-4.5))
.add(new CBORBoolean(true)));
// Sign data using CSF.
byte[] signatureObject = new CBORHmacSigner(HMAC_KEY, HmacAlgorithms.HMAC_SHA256)
.sign(SIGNATURE_LABEL, dataToBeSigned).encode();
// Validate CSF object.
CBORMap decodedCbor = new CBORHmacValidator(HMAC_KEY)
.validate(SIGNATURE_LABEL, CBORDecoder.decode(signatureObject)).getMap();
// Fetch a map item.
System.out.println(decodedCbor.get(GREETINGS_LABEL).getString());
}
}
import org.webpki.cbor.CBORArray;
import org.webpki.cbor.CBORBoolean;
import org.webpki.cbor.CBORDecoder;
import org.webpki.cbor.CBORFloat;
import org.webpki.cbor.CBORHmacSigner;
import org.webpki.cbor.CBORHmacValidator;
import org.webpki.cbor.CBORInt;
import org.webpki.cbor.CBORMap;
import org.webpki.cbor.CBORString;
import org.webpki.crypto.HmacAlgorithms;
import org.webpki.util.HexaDecimal;
public class SignatureDemo {
static final byte[] HMAC_KEY = HexaDecimal.decode(
"7fdd851a3b9d2dafc5f0d00030e22b9343900cd42ede4948568a4a2ee655291a");
static final CBORInt GREETINGS_LABEL = new CBORInt(1);
static final CBORInt OTHER_DATA_LABEL = new CBORInt(2);
static final CBORInt SIGNATURE_LABEL = new CBORInt(-1);
public static void main(String[] args) {
// Create CBOR data to be signed.
CBORMap dataToBeSigned = new CBORMap()
.set(GREETINGS_LABEL, new CBORString("Hello Signed CBOR World!"))
.set(OTHER_DATA_LABEL, new CBORArray()
.add(new CBORFloat(-4.5))
.add(new CBORBoolean(true)));
// Sign data using CSF.
byte[] signatureObject = new CBORHmacSigner(HMAC_KEY, HmacAlgorithms.HMAC_SHA256)
.sign(SIGNATURE_LABEL, dataToBeSigned).encode();
// Validate CSF object.
CBORMap decodedCbor = new CBORHmacValidator(HMAC_KEY)
.validate(SIGNATURE_LABEL, CBORDecoder.decode(signatureObject)).getMap();
// Fetch a map item.
System.out.println(decodedCbor.get(GREETINGS_LABEL).getString());
}
}
The resulting signed object in diagnostic notation:
{
1: "Hello Signed CBOR World!",
2: [-4.5, true],
-1: {
1: 5,
6: h'56e814042baa44e5a0614f576ed95587abaa3af4183b16e0c5b8cff24680a532'
}
}
1: "Hello Signed CBOR World!",
2: [-4.5, true],
-1: {
1: 5,
6: h'56e814042baa44e5a0614f576ed95587abaa3af4183b16e0c5b8cff24680a532'
}
}
The resulting signed object expressed in hexadecimal:
a301781848656c6c6f205369676e65642043424f5220576f726c64210282f9c480f520a2010506582056e814042baa44e5a0614f576ed95587abaa3af4183b16e0c5b8cff24680a532