Skip to content

COSE/CWT (CBOR Object Signing)

This guide covers the COSE (CBOR Object Signing and Encryption) and CWT (CBOR Web Token) implementation with post-quantum algorithm support.

COSE (CBOR Object Signing and Encryption) is a compact binary format for signed and encrypted messages, analogous to JOSE (JSON Object Signing and Encryption) but using CBOR encoding for efficiency.

CWT (CBOR Web Token) is the CBOR equivalent of JWT, using COSE signatures to protect claims in a compact binary format.

QPKI implements COSE/CWT with post-quantum algorithm support.

StandardDescription
RFC 9052CBOR Object Signing and Encryption (COSE): Structures and Process
RFC 9053COSE: Initial Algorithms
RFC 8392CBOR Web Token (CWT)
RFC 8949Concise Binary Object Representation (CBOR)
RFC 9360COSE Header Parameters for X.509 Certificates
draft-ietf-cose-dilithium-04COSE algorithm identifiers for ML-DSA
FIPS 204ML-DSA (Dilithium)
FIPS 205SLH-DSA (SPHINCS+)
AlgorithmCOSE IDDescription
ES256-7ECDSA P-256 with SHA-256
ES384-35ECDSA P-384 with SHA-384
ES512-36ECDSA P-521 with SHA-512
EdDSA-8EdDSA (Ed25519/Ed448)
PS256-37RSASSA-PSS with SHA-256
PS384-38RSASSA-PSS with SHA-384
PS512-39RSASSA-PSS with SHA-512
AlgorithmCOSE IDSource
ML-DSA-44-48draft-ietf-cose-dilithium-04
ML-DSA-65-49draft-ietf-cose-dilithium-04
ML-DSA-87-50draft-ietf-cose-dilithium-04
SLH-DSA-SHA2-128s-70020Private-use range
SLH-DSA-SHA2-128f-70021Private-use range
SLH-DSA-SHA2-192s-70022Private-use range
SLH-DSA-SHA2-192f-70023Private-use range
SLH-DSA-SHA2-256s-70024Private-use range
SLH-DSA-SHA2-256f-70025Private-use range
SLH-DSA-SHAKE-128s-70026Private-use range
SLH-DSA-SHAKE-128f-70027Private-use range
SLH-DSA-SHAKE-192s-70028Private-use range
SLH-DSA-SHAKE-192f-70029Private-use range
SLH-DSA-SHAKE-256s-70030Private-use range
SLH-DSA-SHAKE-256f-70031Private-use range
+------------------------------------------------------------------+
| COSE Message |
+------------------------------------------------------------------+
| +------------------+ +------------------+ +--------------+ |
| | Protected Header | | Payload | | Signature | |
| | (alg, kid, x5c) | | (CWT claims or | | (bytes) | |
| +------------------+ | arbitrary data) | +--------------+ |
+------------------------------------------------------------------+
| | |
v v v
CBOR Encoded CBOR Encoded Signature Bytes

Message Types:

  • COSE_Sign1 (Tag 18): Single signature
  • COSE_Sign (Tag 98): Multiple signatures (hybrid mode)
  • CWT: Sign1 or Sign with claims payload

Create a signed COSE message or CWT.

Terminal window
# Create a CWT with ECDSA (default type)
qpki cose sign --cert signer.crt --key signer.key \
--iss "https://issuer.example.com" \
--sub "user-123" \
--aud "https://api.example.com" \
--exp 1h \
-o token.cbor
qpki cose sign --cert pqc-signer.crt --key pqc-signer.key \
--iss "https://issuer.example.com" \
--sub "device-456" \
--exp 24h \
-o pqc-token.cbor
qpki cose sign --cert signer.crt --key signer.key \
--pqc-key pqc-signer.key \
--iss "https://issuer.example.com" \
--sub "hybrid-user" \
--exp 1h \
-o hybrid-token.cbor
qpki cose sign --type sign1 --cert signer.crt --key signer.key \
--data document.pdf \
-o signed-document.cbor
qpki cose sign --cert signer.crt --key signer.key \
--iss "https://issuer.example.com" \
--claim "8=admin" \
--claim "-65537=custom-value" \
-o token-with-claims.cbor

Options:

FlagDescriptionDefault
--typeMessage type: cwt, sign1, signcwt
--certSigner certificate (PEM)Required
--keySigner private key (PEM)Required
--pqc-keyPQC private key for hybrid mode (PEM)-
--passphraseKey passphrase-
--dataFile to sign (for sign1/sign)-
-o, --outOutput file (.cbor)Required

CWT Options:

FlagDescriptionDefault
--issIssuer claim-
--subSubject claim-
--audAudience claim-
--expExpiration duration (e.g., 1h, 24h, 30m)-
--claimCustom claim: key=value (can be repeated)-

Verify a COSE message signature.

Terminal window
# Verify with CA certificate
qpki cose verify token.cbor --ca ca.crt
qpki cose verify hybrid-token.cbor --ca ca.crt --pqc-ca pqc-ca.crt
qpki cose verify token.cbor --ca ca.crt --check-exp
qpki cose verify signed-document.cbor --ca ca.crt --data document.pdf

Options:

FlagDescriptionDefault
--caCA certificate(s) for chain verification-
--pqc-caPQC CA certificate for hybrid verification-
--dataOriginal data file (for detached signatures)-
--check-expVerify expiration claimstrue

Output:

Verification: VALID
Mode: hybrid
Algorithms: ES256, ML-DSA-65
Issuer: https://issuer.example.com
Subject: hybrid-user
Expires: 2025-02-06T12:00:00Z

Display information about a COSE message.

Terminal window
qpki cose info token.cbor

Output:

COSE Message Info
=================
Type: CWT
Mode: hybrid
Content-Type:
Payload Size: 128 bytes
Payload: a50166697373...
Signatures (2):
[0] Algorithm: ES256 (id=-7)
Key ID: ab:cd:ef:12:34:...
Certificate:
Subject: CN=signer.example.com
Issuer: CN=ca.example.com
Not Before: 2025-01-01T00:00:00Z
Not After: 2026-01-01T00:00:00Z
Serial: 123456
Thumbprint: ab:cd:ef:...
[1] Algorithm: ML-DSA-65 (id=-49)
Key ID: 12:34:56:78:...
CWT Claims:
Issuer (iss): https://issuer.example.com
Subject (sub): user-123
Audience (aud): https://api.example.com
Expiration (exp): 2025-02-06T12:00:00Z
Issued At (iat): 2025-02-05T12:00:00Z
CWT ID (cti): ab:cd:ef:12:34:56:78:90
Validation: VALID

Uses traditional cryptographic algorithms (ECDSA, EdDSA, RSA-PSS).

Terminal window
# ECDSA P-256
qpki cose sign --cert ec-signer.crt --key ec-signer.key \
--iss "issuer" --sub "subject" -o token.cbor
qpki cose sign --cert ed-signer.crt --key ed-signer.key \
--iss "issuer" --sub "subject" -o token.cbor
qpki cose sign --cert rsa-signer.crt --key rsa-signer.key \
--iss "issuer" --sub "subject" -o token.cbor

Uses post-quantum algorithms (ML-DSA, SLH-DSA).

Terminal window
# ML-DSA-65 (recommended for general use)
qpki cose sign --cert mldsa-signer.crt --key mldsa-signer.key \
--iss "issuer" --sub "subject" -o pqc-token.cbor
qpki cose sign --cert slhdsa-signer.crt --key slhdsa-signer.key \
--iss "issuer" --sub "subject" -o slhdsa-token.cbor

Uses both classical and PQC signatures for quantum-safe transition.

Terminal window
# ECDSA + ML-DSA hybrid
qpki cose sign --cert ec-signer.crt --key ec-signer.key \
--pqc-key mldsa-signer.key \
--iss "issuer" --sub "subject" -o hybrid-token.cbor

Hybrid verification requires BOTH signatures to be valid:

Terminal window
qpki cose verify hybrid-token.cbor --ca ec-ca.crt --pqc-ca mldsa-ca.crt

A CWT is a COSE_Sign1 or COSE_Sign message where the payload contains CBOR-encoded claims.

Terminal window
# Create CWT
qpki cose sign --type cwt \
--cert signer.crt --key signer.key \
--iss "https://auth.example.com" \
--sub "user@example.com" \
--aud "https://api.example.com" \
--exp 1h \
-o access-token.cbor

Structure:

COSE_Sign1 [
Protected: {alg: ES256, kid: "..."},
Unprotected: {},
Payload: CBOR({1: "issuer", 2: "subject", 4: 1738800000, ...}),
Signature: bytes
]

A COSE_Sign1 message for signing arbitrary data.

Terminal window
# Sign a document
qpki cose sign --type sign1 \
--cert signer.crt --key signer.key \
--data document.pdf \
-o signed-document.cbor

CBOR Tag: 18 (0xd2)

A COSE_Sign message with multiple signatures, used for hybrid mode.

Terminal window
# Create multi-signature message
qpki cose sign --type sign \
--cert ec-signer.crt --key ec-signer.key \
--pqc-key mldsa-signer.key \
--data document.pdf \
-o hybrid-signed.cbor

CBOR Tag: 98 (0xd8 0x62)

Structure:

COSE_Sign [
Protected: {},
Unprotected: {},
Payload: bytes,
Signatures: [
[Protected: {alg: ES256}, Unprotected: {}, Signature: bytes],
[Protected: {alg: ML-DSA-65}, Unprotected: {}, Signature: bytes]
]
]

ClaimKeyTypeDescription
iss1stringIssuer
sub2stringSubject
aud3stringAudience
exp4intExpiration time (Unix timestamp)
nbf5intNot before time (Unix timestamp)
iat6intIssued at time (Unix timestamp)
cti7bytesCWT ID (unique identifier)

Use integer keys for custom claims. Positive integers are reserved for IANA registration; use negative integers for private claims.

Terminal window
# Add custom claims
qpki cose sign --cert signer.crt --key signer.key \
--iss "issuer" \
--claim "8=admin" \
--claim "-65537=tenant-id-123" \
-o token.cbor
Terminal window
# Verify with expiration check (default)
qpki cose verify token.cbor --ca ca.crt --check-exp
qpki cose verify token.cbor --ca ca.crt --check-exp=false

Terminal window
# Create device credential
qpki credential enroll --profile ml/codesigning \
--var cn="device-001.iot.example.com" \
--id device-001
qpki cose sign --cert device-001.crt --key device-001.key \
--iss "https://manufacturer.example.com" \
--sub "device-001" \
--claim "-65537=firmware-v2.1.0" \
--claim "-65538=$(sha256sum firmware.bin | cut -d' ' -f1)" \
--exp 8760h \
-o device-attestation.cbor
Terminal window
# Create access token
qpki cose sign --cert auth-server.crt --key auth-server.key \
--iss "https://auth.example.com" \
--sub "user@example.com" \
--aud "https://api.example.com" \
--exp 1h \
--claim "8=read,write" \
-o access-token.cbor
qpki cose verify access-token.cbor --ca auth-ca.crt

Use hybrid mode for quantum-safe transition:

Terminal window
# Create hybrid credentials
qpki credential enroll --profile ec/codesigning \
--var cn="signer.example.com" --id ec-signer
qpki credential enroll --profile ml/codesigning \
--var cn="pqc-signer.example.com" --id pqc-signer
qpki cose sign \
--cert ec-signer.crt --key ec-signer.key \
--pqc-key pqc-signer.key \
--iss "https://issuer.example.com" \
--sub "hybrid-protected-data" \
--exp 24h \
-o hybrid-token.cbor
qpki cose verify hybrid-token.cbor --ca ec-ca.crt --pqc-ca pqc-ca.crt
Terminal window
# Sign a document with timestamp
qpki cose sign --type sign1 \
--cert signer.crt --key signer.key \
--data contract.pdf \
-o signed-contract.cbor
qpki cose verify signed-contract.cbor --ca ca.crt --data contract.pdf

COSE signing supports HSM-stored keys via PKCS#11.

Terminal window
# Generate ML-DSA key in HSM
qpki key gen --algorithm ml-dsa-65 \
--hsm-config ./hsm.yaml --key-label cose-signer
# Create CA with HSM key
qpki ca init --hsm-config ./hsm.yaml --key-label cose-signer \
--profile ml/root-ca --var cn="COSE HSM CA" --ca-dir ./ca
# Sign CWT using HSM key
qpki cose sign --type cwt \
--cert ./ca/ca.crt \
--hsm-config ./hsm.yaml --key-label cose-signer \
--iss "https://hsm-issuer.example.com" \
--sub "user-123" --exp 1h \
-o token.cbor
# Verify
qpki cose verify token.cbor --ca ./ca/ca.crt
Terminal window
# Generate both EC and ML-DSA keys with same label
qpki key gen --algorithm ecdsa-p384 \
--hsm-config ./hsm.yaml --key-label hybrid-signer
qpki key gen --algorithm ml-dsa-65 \
--hsm-config ./hsm.yaml --key-label hybrid-signer
# Verify both keys exist
qpki key info --hsm-config ./hsm.yaml --key-label hybrid-signer
# Create hybrid CA
qpki ca init --hsm-config ./hsm.yaml --key-label hybrid-signer \
--profile hybrid/catalyst/root-ca \
--var cn="Hybrid COSE CA" --ca-dir ./hybrid-ca
# Sign with hybrid mode (COSE_Sign with 2 signatures)
qpki cose sign --type sign \
--cert ./hybrid-ca/ca.crt \
--hsm-config ./hsm.yaml --key-label hybrid-signer \
--data payload.bin -o hybrid-signed.cbor
# Verify
qpki cose verify hybrid-signed.cbor --ca ./hybrid-ca/ca.crt
AlgorithmSoftHSMUTIMACO
ECDSA (P-256/384/521)
RSA (2048/4096)
ML-DSA (44/65/87)
Hybrid (EC + ML-DSA)

Note: SLH-DSA is not supported by any HSM and only works in software mode.