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.
Signatures must for compatibility with CSF,
be provided as a CBOR
map
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 |
integer | 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 ECDSA public keys must be supplied with a value parameter for the y-coordinate as well. The public key object must not contain any 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 |
byte string | Signature value. |
Signature Validation
TheCBORValidator
super classes perform core CSF validation
according to the following set of rules.
while leaving policy enforcements to the signature-using application.
- CSF signatures are validated by running the signature validation
algorithm
(gathered from label1
), over the CBOR binary representation of themap
object holding the application data including the enveloped signature object, with thesignatureValue
(label6
) 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 or provided via
keyId
(label3
) attributes. - If
publicKey
(label4
) orcertificatePath
(label5
) attributes are supplied, 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
(label1
) attribute.
Tagged and Custom Signature Data
CSF containers can be further enhanced through a couple of options described in Crypto Options.Signature Algorithms
Currently supported COSE signature algorithms:
Name | Identifier | Compatibility |
---|---|---|
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/.
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.
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