JOSE and COSE Operations
The IDK provides comprehensive support for two major cryptographic message formats: JOSE (JSON Object Signing and Encryption) and COSE (CBOR Object Signing and Encryption). JOSE is the primary format used in OpenID protocols (OID4VP, OID4VCI, SD-JWT), while COSE is used for ISO mDoc credentials.
Format Overview
| Aspect | JOSE | COSE |
|---|---|---|
| Encoding | Text (JSON) | Binary (CBOR) |
| Size | Larger | Compact |
| Primary Use | OAuth, OpenID, Web APIs | mDoc, IoT |
| Key Format | JWK | COSE_Key |
| Signature | JWS | COSE_Sign1 |
| Encryption | JWE | COSE_Encrypt |
JwtService - JWS and JWT Operations
The JwtService is the primary service for creating and verifying JSON Web Signatures (JWS) and JSON Web Tokens (JWT). It provides a command-based API for all JWS operations.
Obtaining the Service
- Android/Kotlin
- iOS/Swift
val jwtService = session.component.jwtService
// Access individual commands
val createJwsCompact = jwtService.commands.createJwsCompact
val createJwsJsonFlattened = jwtService.commands.createJwsJsonFlattened
val createJwsJsonGeneral = jwtService.commands.createJwsJsonGeneral
val verifyJws = jwtService.commands.verifyJws
let jwtService = session.component.jwtService
// Access individual commands
let createJwsCompact = jwtService.commands.createJwsCompact
let createJwsJsonFlattened = jwtService.commands.createJwsJsonFlattened
let createJwsJsonGeneral = jwtService.commands.createJwsJsonGeneral
let verifyJws = jwtService.commands.verifyJws
Creating JWS with Existing Keys
The most common use case is signing with a key that already exists in your KMS. Reference keys by alias, key ID, or pass the key directly:
- Android/Kotlin
- iOS/Swift
// Reference key by alias (most common for KMS-managed keys)
val byAlias = ManagedOptsAlias(identifier = "my-signing-key")
// Reference key by key ID
val byKid = ManagedOptsKid(identifier = "key-123")
// Reference key by KeyInfo (with additional metadata)
val byKeyInfo = ManagedOptsKeyInfo(
identifier = KeyInfo(
alias = "my-signing-key",
providerId = "software",
signatureAlgorithm = SignatureAlgorithm.ECDSA_SHA256
)
)
// Pass an existing JWK directly
val byJwk = ManagedOptsJwk(identifier = existingJwkDto)
// Reference key by alias (most common for KMS-managed keys)
let byAlias = ManagedOptsAlias(identifier: "my-signing-key")
// Reference key by key ID
let byKid = ManagedOptsKid(identifier: "key-123")
// Reference key by KeyInfo (with additional metadata)
let byKeyInfo = ManagedOptsKeyInfo(
identifier: KeyInfo(
alias: "my-signing-key",
providerId: "software",
signatureAlgorithm: .ecdsaSha256
)
)
// Pass an existing JWK directly
let byJwk = ManagedOptsJwk(identifier: existingJwkDto)
Creating Compact JWS
Compact serialization produces the familiar header.payload.signature format:
- Android/Kotlin
- iOS/Swift
// Create payload
val payload = mapOf(
"iss" to "https://issuer.example.com",
"sub" to "user-123",
"aud" to "https://verifier.example.com",
"iat" to System.currentTimeMillis() / 1000,
"exp" to (System.currentTimeMillis() / 1000) + 3600,
"name" to "Alice Smith"
)
// Create compact JWS using key alias
val result = jwtService.createJwsCompact(
CreateJwsArgs(
issuer = ManagedOptsAlias("my-signing-key"),
payload = payload,
mode = JwsIdentifierMode.KID, // Include key ID in header
opts = CreateJwsOpts(
protectedHeader = buildJsonObject {
put("typ", "JWT")
}
)
)
)
when (result) {
is IdkResult.Success -> {
val jwt = result.value.jwt
println("JWT: $jwt")
// eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im15LXNpZ25pbmcta2V5In0.eyJpc3MiOi...
}
is IdkResult.Failure -> {
println("Error: ${result.error.message}")
}
}
// Create payload
let payload: [String: Any] = [
"iss": "https://issuer.example.com",
"sub": "user-123",
"aud": "https://verifier.example.com",
"iat": Int64(Date().timeIntervalSince1970),
"exp": Int64(Date().timeIntervalSince1970) + 3600,
"name": "Alice Smith"
]
// Create compact JWS using key alias
let result = try await jwtService.createJwsCompact(
args: CreateJwsArgs(
issuer: ManagedOptsAlias(identifier: "my-signing-key"),
payload: payload,
mode: .kid, // Include key ID in header
opts: CreateJwsOpts(
protectedHeader: ["typ": "JWT"]
)
)
)
switch result {
case .success(let value):
let jwt = value.jwt
print("JWT: \(jwt)")
case .failure(let error):
print("Error: \(error.message)")
}
JWS Identifier Modes
Control how the signing key is identified in the JWS header:
| Mode | Header Field | Use Case |
|---|---|---|
KID | kid | Key ID reference - verifier looks up key |
JWK | jwk | Embed public key in header |
X5C | x5c | Embed X.509 certificate chain |
DID | kid (DID URL) | Decentralized identifier reference |
AUTO | Automatic | IDK selects based on key metadata |
- Android/Kotlin
- iOS/Swift
// Embed the public key in the header
val jwsWithEmbeddedKey = jwtService.createJwsCompact(
CreateJwsArgs(
issuer = ManagedOptsAlias("my-signing-key"),
payload = payload,
mode = JwsIdentifierMode.JWK // Embeds public JWK in header
)
)
// Include X.509 certificate chain
val jwsWithCertChain = jwtService.createJwsCompact(
CreateJwsArgs(
issuer = ManagedOptsKeyInfo(
identifier = KeyInfo(
alias = "my-signing-key",
x5c = arrayOf(base64EncodedCert)
)
),
payload = payload,
mode = JwsIdentifierMode.X5C
)
)
// Embed the public key in the header
let jwsWithEmbeddedKey = try await jwtService.createJwsCompact(
args: CreateJwsArgs(
issuer: ManagedOptsAlias(identifier: "my-signing-key"),
payload: payload,
mode: .jwk // Embeds public JWK in header
)
)
// Include X.509 certificate chain
let jwsWithCertChain = try await jwtService.createJwsCompact(
args: CreateJwsArgs(
issuer: ManagedOptsKeyInfo(
identifier: KeyInfo(
alias: "my-signing-key",
x5c: [base64EncodedCert]
)
),
payload: payload,
mode: .x5c
)
)
Creating JSON Serialized JWS
For scenarios requiring JSON format or multiple signatures:
- Android/Kotlin
- iOS/Swift
// JSON Flattened - single signature in JSON format
val flattenedResult = jwtService.createJwsJsonFlattened(
CreateJwsJsonArgs(
issuer = ManagedOptsAlias("my-signing-key"),
payload = payload,
mode = JwsIdentifierMode.KID
)
)
// Result structure:
// {
// "payload": "eyJpc3MiOi...",
// "protected": "eyJhbGciOi...",
// "header": { "kid": "my-signing-key" },
// "signature": "DtEhU3lj..."
// }
// JSON General - supports multiple signatures
val generalResult = jwtService.createJwsJsonGeneral(
CreateJwsJsonArgs(
issuer = ManagedOptsAlias("primary-key"),
payload = payload,
mode = JwsIdentifierMode.KID,
existingSignatures = listOf(existingSignature) // Add to existing signatures
)
)
// Result structure:
// {
// "payload": "eyJpc3MiOi...",
// "signatures": [
// { "protected": "...", "header": {...}, "signature": "..." },
// { "protected": "...", "header": {...}, "signature": "..." }
// ]
// }
// JSON Flattened - single signature in JSON format
let flattenedResult = try await jwtService.createJwsJsonFlattened(
args: CreateJwsJsonArgs(
issuer: ManagedOptsAlias(identifier: "my-signing-key"),
payload: payload,
mode: .kid
)
)
// JSON General - supports multiple signatures
let generalResult = try await jwtService.createJwsJsonGeneral(
args: CreateJwsJsonArgs(
issuer: ManagedOptsAlias(identifier: "primary-key"),
payload: payload,
mode: .kid,
existingSignatures: [existingSignature]
)
)
Verifying JWS
Verify signatures using the verifier's key or by resolving the key from the JWS header:
- Android/Kotlin
- iOS/Swift
// Parse the JWS
val jws = Jws.fromCompact(jwtString)
// Verify with explicit key
val result = jwtService.verifyJws(
VerifyJwsArgs(
jws = jws,
identifier = ManagedOptsAlias("issuer-public-key")
)
)
when (result) {
is IdkResult.Success -> {
val validation = result.value
if (validation.isValid) {
println("Signature valid")
println("Payload: ${jws.payload.decodeToString()}")
} else {
println("Invalid: ${validation.errorMessages.joinToString()}")
}
}
is IdkResult.Failure -> {
println("Verification error: ${result.error.message}")
}
}
// Verify using embedded JWK from header (if present)
val autoResult = jwtService.verifyJws(
VerifyJwsArgs(
jws = jws,
identifier = null // Key resolved from header
)
)
// Parse the JWS
let jws = Jws.fromCompact(compact: jwtString)
// Verify with explicit key
let result = try await jwtService.verifyJws(
args: VerifyJwsArgs(
jws: jws,
identifier: ManagedOptsAlias(identifier: "issuer-public-key")
)
)
switch result {
case .success(let validation):
if validation.isValid {
print("Signature valid")
print("Payload: \(String(data: jws.payload, encoding: .utf8)!)")
} else {
print("Invalid: \(validation.errorMessages.joined(separator: ", "))")
}
case .failure(let error):
print("Verification error: \(error.message)")
}
// Verify using embedded JWK from header (if present)
let autoResult = try await jwtService.verifyJws(
args: VerifyJwsArgs(
jws: jws,
identifier: nil // Key resolved from header
)
)
JweService - JWE Encryption Operations
The JweService handles JSON Web Encryption for encrypting content to one or more recipients.
Obtaining the Service
- Android/Kotlin
- iOS/Swift
val jweService = session.component.jweService
// Access individual commands
val prepareJwe = jweService.commands.prepareJwe
val createJweCompact = jweService.commands.createJweCompact
val createJweJsonFlattened = jweService.commands.createJweJsonFlattened
val createJweJsonGeneral = jweService.commands.createJweJsonGeneral
val decryptJwe = jweService.commands.decryptJwe
let jweService = session.component.jweService
// Access individual commands
let prepareJwe = jweService.commands.prepareJwe
let createJweCompact = jweService.commands.createJweCompact
let createJweJsonFlattened = jweService.commands.createJweJsonFlattened
let createJweJsonGeneral = jweService.commands.createJweJsonGeneral
let decryptJwe = jweService.commands.decryptJwe
JWE Algorithm Selection
JWE uses two algorithms: one for key encryption and one for content encryption.
Key Encryption Algorithms:
| Algorithm | Description | Key Type |
|---|---|---|
RSA-OAEP | RSA with OAEP padding | RSA |
RSA-OAEP-256 | RSA-OAEP with SHA-256 | RSA |
ECDH-ES | Direct ECDH key agreement | EC |
ECDH-ES+A128KW | ECDH with AES key wrap | EC |
ECDH-ES+A256KW | ECDH with AES-256 key wrap | EC |
A128KW | AES key wrap (symmetric) | Symmetric |
A256KW | AES-256 key wrap (symmetric) | Symmetric |
dir | Direct encryption (no key wrap) | Symmetric |
Content Encryption Algorithms:
| Algorithm | Description |
|---|---|
A128GCM | AES-128 in GCM mode |
A192GCM | AES-192 in GCM mode |
A256GCM | AES-256 in GCM mode |
A128CBC-HS256 | AES-128-CBC with HMAC-SHA-256 |
A256CBC-HS512 | AES-256-CBC with HMAC-SHA-512 |
Creating Compact JWE
- Android/Kotlin
- iOS/Swift
val plaintext = "Sensitive data to encrypt".encodeToByteArray()
// Step 1: Prepare the JWE (generates CEK, builds headers)
val prepareResult = jweService.prepareJwe(
PrepareJweArgs(
plaintext = plaintext,
recipient = ManagedOptsAlias("recipient-public-key"),
keyEncryptionAlg = "RSA-OAEP-256",
contentEncryptionAlg = "A256GCM",
opts = CreateJweOpts(
compress = false
)
)
)
when (prepareResult) {
is IdkResult.Success -> {
val prepared = prepareResult.value
// Step 2: Create the compact JWE
val jweResult = jweService.createJweCompact(
CreateJweCompactArgs(
preparedJwe = prepared,
aad = null // Optional additional authenticated data
)
)
when (jweResult) {
is IdkResult.Success -> {
val jwe = jweResult.value
println("JWE: ${jwe.compact}")
// eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0.OKOaw...
}
is IdkResult.Failure -> {
println("Encryption failed: ${jweResult.error.message}")
}
}
}
is IdkResult.Failure -> {
println("Preparation failed: ${prepareResult.error.message}")
}
}
let plaintext = "Sensitive data to encrypt".data(using: .utf8)!
// Step 1: Prepare the JWE (generates CEK, builds headers)
let prepareResult = try await jweService.prepareJwe(
args: PrepareJweArgs(
plaintext: plaintext,
recipient: ManagedOptsAlias(identifier: "recipient-public-key"),
keyEncryptionAlg: "RSA-OAEP-256",
contentEncryptionAlg: "A256GCM",
opts: CreateJweOpts(
compress: false
)
)
)
switch prepareResult {
case .success(let prepared):
// Step 2: Create the compact JWE
let jweResult = try await jweService.createJweCompact(
args: CreateJweCompactArgs(
preparedJwe: prepared,
aad: nil
)
)
switch jweResult {
case .success(let jwe):
print("JWE: \(jwe.compact)")
case .failure(let error):
print("Encryption failed: \(error.message)")
}
case .failure(let error):
print("Preparation failed: \(error.message)")
}
Creating Multi-Recipient JWE
JSON General serialization supports encrypting to multiple recipients:
- Android/Kotlin
- iOS/Swift
// Prepare for primary recipient
val prepareResult = jweService.prepareJwe(
PrepareJweArgs(
plaintext = plaintext,
recipient = ManagedOptsAlias("recipient-1-key"),
keyEncryptionAlg = "ECDH-ES+A256KW",
contentEncryptionAlg = "A256GCM"
)
)
when (prepareResult) {
is IdkResult.Success -> {
val prepared = prepareResult.value
// Create multi-recipient JWE
val generalResult = jweService.createJweJsonGeneral(
CreateJweJsonGeneralArgs(
preparedJwe = prepared,
additionalRecipients = listOf(
AdditionalRecipient(
recipient = ManagedOptsAlias("recipient-2-key"),
keyEncryptionAlg = "ECDH-ES+A256KW"
),
AdditionalRecipient(
recipient = ManagedOptsAlias("recipient-3-key"),
keyEncryptionAlg = "RSA-OAEP-256"
)
)
)
)
// Result: JweJsonGeneral with recipients array
// Each recipient can decrypt using their own key
}
is IdkResult.Failure -> { /* handle error */ }
}
// Prepare for primary recipient
let prepareResult = try await jweService.prepareJwe(
args: PrepareJweArgs(
plaintext: plaintext,
recipient: ManagedOptsAlias(identifier: "recipient-1-key"),
keyEncryptionAlg: "ECDH-ES+A256KW",
contentEncryptionAlg: "A256GCM"
)
)
switch prepareResult {
case .success(let prepared):
// Create multi-recipient JWE
let generalResult = try await jweService.createJweJsonGeneral(
args: CreateJweJsonGeneralArgs(
preparedJwe: prepared,
additionalRecipients: [
AdditionalRecipient(
recipient: ManagedOptsAlias(identifier: "recipient-2-key"),
keyEncryptionAlg: "ECDH-ES+A256KW"
),
AdditionalRecipient(
recipient: ManagedOptsAlias(identifier: "recipient-3-key"),
keyEncryptionAlg: "RSA-OAEP-256"
)
]
)
)
case .failure(let error):
// handle error
}
Decrypting JWE
- Android/Kotlin
- iOS/Swift
// Parse the JWE (handles compact, flattened, and general formats)
val jwe = Jwe.parse(jweString)
// Decrypt using recipient's private key
val decryptResult = jweService.decryptJwe(
DecryptJweArgs(
jwe = jwe,
decryptor = ManagedOptsAlias("my-private-key")
)
)
when (decryptResult) {
is IdkResult.Success -> {
val decrypted = decryptResult.value
val plaintext = decrypted.plaintext
println("Decrypted: ${plaintext.decodeToString()}")
// Access header information
val header = decrypted.header
println("Algorithm: ${header.alg}")
println("Encryption: ${header.enc}")
}
is IdkResult.Failure -> {
println("Decryption failed: ${decryptResult.error.message}")
}
}
// Parse the JWE (handles compact, flattened, and general formats)
let jwe = Jwe.parse(jweString: jweString)
// Decrypt using recipient's private key
let decryptResult = try await jweService.decryptJwe(
args: DecryptJweArgs(
jwe: jwe,
decryptor: ManagedOptsAlias(identifier: "my-private-key")
)
)
switch decryptResult {
case .success(let decrypted):
let plaintext = decrypted.plaintext
print("Decrypted: \(String(data: plaintext, encoding: .utf8)!)")
// Access header information
let header = decrypted.header
print("Algorithm: \(header.alg)")
print("Encryption: \(header.enc)")
case .failure(let error):
print("Decryption failed: \(error.message)")
}
COSE Operations
COSE is used for ISO mDoc credentials and other CBOR-based protocols. The IDK provides CoseCryptoService for COSE operations.
Obtaining the Service
- Android/Kotlin
- iOS/Swift
val coseCryptoService = session.component.coseCryptoService
let coseCryptoService = session.component.coseCryptoService
Creating COSE_Sign1
COSE_Sign1 is the single-signer signature format used in mDoc:
- Android/Kotlin
- iOS/Swift
// Build COSE_Sign1 input
val coseSign1Input = CoseSign1Input(
protectedHeader = CoseHeaderCbor(
algorithm = SignatureAlgorithm.ECDSA_SHA256.cose
),
unprotectedHeader = null,
payload = CborByteString("Payload data".encodeToByteArray())
)
// Get key info for signing
val keyInfo = KeyInfo<CoseKeyType>(
alias = "my-signing-key",
providerId = "mobile",
keyVisibility = KeyVisibility.PRIVATE,
signatureAlgorithm = SignatureAlgorithm.ECDSA_SHA256
)
// Sign
val signResult = coseCryptoService.sign1<ByteArray>(
input = coseSign1Input,
keyInfo = keyInfo,
requireX5Chain = false
)
val coseSign1 = signResult.coseSign1
val cborBytes = coseSign1.toCbor()
// Build COSE_Sign1 input
let coseSign1Input = CoseSign1Input(
protectedHeader: CoseHeaderCbor(
algorithm: SignatureAlgorithm.ecdsaSha256.cose
),
unprotectedHeader: nil,
payload: CborByteString(data: "Payload data".data(using: .utf8)!)
)
// Get key info for signing
let keyInfo = KeyInfo<CoseKeyType>(
alias: "my-signing-key",
providerId: "mobile",
keyVisibility: .private_,
signatureAlgorithm: .ecdsaSha256
)
// Sign
let signResult = try await coseCryptoService.sign1(
input: coseSign1Input,
keyInfo: keyInfo,
requireX5Chain: false
)
let coseSign1 = signResult.coseSign1
let cborBytes = coseSign1.toCbor()
Verifying COSE_Sign1
- Android/Kotlin
- iOS/Swift
// Parse COSE_Sign1 from CBOR bytes
val coseSign1 = CoseSign1.fromCbor<ByteArray>(cborBytes)
// Get public key for verification
val publicKeyInfo = KeyInfo<CoseKeyType>(
alias = "issuer-public-key",
providerId = "software",
keyVisibility = KeyVisibility.PUBLIC
)
// Verify
val verificationResult = coseCryptoService.verify1(
input = coseSign1,
keyInfo = publicKeyInfo,
requireX5Chain = false
)
if (verificationResult.isValid) {
val payload = coseSign1.payload?.value
println("Verified payload: ${payload?.decodeToString()}")
} else {
println("Verification failed: ${verificationResult.error}")
}
// Parse COSE_Sign1 from CBOR bytes
let coseSign1 = CoseSign1.fromCbor(cborBytes)
// Get public key for verification
let publicKeyInfo = KeyInfo<CoseKeyType>(
alias: "issuer-public-key",
providerId: "software",
keyVisibility: .public_
)
// Verify
let verificationResult = try await coseCryptoService.verify1(
input: coseSign1,
keyInfo: publicKeyInfo,
requireX5Chain: false
)
if verificationResult.isValid {
let payload = coseSign1.payload?.value
print("Verified payload: \(String(data: payload!, encoding: .utf8)!)")
} else {
print("Verification failed: \(verificationResult.error)")
}
Key Format Conversion
The IDK provides mapping utilities to convert between COSE and JOSE formats. This is essential when working with systems that use different formats.
Converting Existing Keys
Use KeyInfo or ResolvedKeyInfo to work with existing keys and convert between formats:
- Android/Kotlin
- iOS/Swift
// Start with an existing JWK
val existingJwk: Jwk = loadJwkFromSomewhere()
// Create KeyInfo for JOSE operations
val joseKeyInfo = ResolvedKeyInfo(
key = existingJwk,
kid = existingJwk.kid,
keyVisibility = KeyVisibility.PUBLIC,
signatureAlgorithm = SignatureAlgorithm.ECDSA_SHA256,
keyEncoding = KeyEncoding.JOSE
)
// Convert JWK to COSE Key
val coseKey = CoseKey.fromJwk(existingJwk)
// Create KeyInfo for COSE operations
val coseKeyInfo = ResolvedKeyInfo(
key = coseKey,
kid = coseKey.kid?.value,
keyVisibility = KeyVisibility.PUBLIC,
signatureAlgorithm = SignatureAlgorithm.ECDSA_SHA256,
keyEncoding = KeyEncoding.COSE
)
// Start with an existing JWK
let existingJwk: Jwk = loadJwkFromSomewhere()
// Create KeyInfo for JOSE operations
let joseKeyInfo = ResolvedKeyInfo(
key: existingJwk,
kid: existingJwk.kid,
keyVisibility: .public_,
signatureAlgorithm: .ecdsaSha256,
keyEncoding: .jose
)
// Convert JWK to COSE Key
let coseKey = CoseKey.fromJwk(jwk: existingJwk)
// Create KeyInfo for COSE operations
let coseKeyInfo = ResolvedKeyInfo(
key: coseKey,
kid: coseKey.kid?.value,
keyVisibility: .public_,
signatureAlgorithm: .ecdsaSha256,
keyEncoding: .cose
)
Direct Key Conversion
Convert keys directly between formats:
- Android/Kotlin
- iOS/Swift
// JWK to COSE Key
val jwk: Jwk = ...
val coseKey = CoseKey.fromJwk(jwk)
// COSE Key to JWK
val coseKey: CoseKey = ...
val jwk = coseKey.toJwk()
// Both directions preserve key material and metadata
println("Original kid: ${jwk.kid}")
println("Converted kid: ${coseKey.kid?.value}")
// JWK to COSE Key
let jwk: Jwk = ...
let coseKey = CoseKey.fromJwk(jwk: jwk)
// COSE Key to JWK
let coseKey: CoseKey = ...
let jwk = coseKey.toJwk()
// Both directions preserve key material and metadata
print("Original kid: \(jwk.kid ?? "none")")
print("Converted kid: \(coseKey.kid?.value ?? "none")")
Algorithm Mapping
Convert algorithm identifiers between COSE and JOSE:
- Android/Kotlin
- iOS/Swift
// From SignatureAlgorithm to both formats
val algorithm = SignatureAlgorithm.ECDSA_SHA256
val coseAlg = algorithm.cose // CoseAlgorithm.ES256 (-7)
val joseAlg = algorithm.jose // JwaAlgorithm.ES256 ("ES256")
// Convert COSE algorithm to JOSE
val joseFromCose = SignatureAlgorithm.toJose(CoseAlgorithm.ES256)
// Result: JwaAlgorithm.ES256
// Convert JOSE algorithm to COSE
val coseFromJose = SignatureAlgorithm.toCose(JwaAlgorithm.RS256)
// Result: CoseAlgorithm.RS256
// Flexible conversion from any format
val alg = SignatureAlgorithm.toJoseAlg(-7) // Works with Int (COSE ID)
val alg2 = SignatureAlgorithm.toCoseAlg("ES256") // Works with String (JOSE name)
// From SignatureAlgorithm to both formats
let algorithm = SignatureAlgorithm.ecdsaSha256
let coseAlg = algorithm.cose // CoseAlgorithm.es256 (-7)
let joseAlg = algorithm.jose // JwaAlgorithm.es256 ("ES256")
// Convert COSE algorithm to JOSE
let joseFromCose = SignatureAlgorithm.toJose(cose: .es256)
// Result: JwaAlgorithm.es256
// Convert JOSE algorithm to COSE
let coseFromJose = SignatureAlgorithm.toCose(jose: .rs256)
// Result: CoseAlgorithm.rs256
Key Type and Curve Mapping
- Android/Kotlin
- iOS/Swift
// Key type mapping
val keyType = KeyTypeMapping.EC
val joseKty = keyType.jose // JwaKeyType.EC
val coseKty = keyType.cose // CoseKeyTypeEnum.EC2
// Convert from COSE to JOSE
val joseKeyType = KeyTypeMapping.toJose(CoseKeyTypeEnum.EC2)
// Result: JwaKeyType.EC
// Curve mapping
val curve = Curve.P_256
val joseCrv = curve.jose // JwaCurve.P_256
val coseCrv = curve.cose // CoseCurve.P_256
// Key operations mapping
val ops = KeyOperations.SIGN
val joseOps = ops.jose // JoseKeyOperations.SIGN
val coseOps = ops.cose // CoseKeyOperations.SIGN
// Key type mapping
let keyType = KeyTypeMapping.ec
let joseKty = keyType.jose // JwaKeyType.ec
let coseKty = keyType.cose // CoseKeyTypeEnum.ec2
// Convert from COSE to JOSE
let joseKeyType = KeyTypeMapping.toJose(cose: .ec2)
// Result: JwaKeyType.ec
// Curve mapping
let curve = Curve.p256
let joseCrv = curve.jose // JwaCurve.p256
let coseCrv = curve.cose // CoseCurve.p256
// Key operations mapping
let ops = KeyOperations.sign
let joseOps = ops.jose // JoseKeyOperations.sign
let coseOps = ops.cose // CoseKeyOperations.sign
Algorithm Reference
Signature Algorithms
| Algorithm | COSE ID | JOSE Name | Curve/Key |
|---|---|---|---|
ED25519 | -8 | EdDSA | Ed25519 |
ECDSA_SHA256 | -7 | ES256 | P-256 |
ECDSA_SHA384 | -35 | ES384 | P-384 |
ECDSA_SHA512 | -36 | ES512 | P-521 |
RSA_SHA256 | -257 | RS256 | RSA |
RSA_SHA384 | -258 | RS384 | RSA |
RSA_SHA512 | -259 | RS512 | RSA |
RSA_SSA_PSS_SHA256_MGF1 | -37 | PS256 | RSA |
RSA_SSA_PSS_SHA384_MGF1 | -38 | PS384 | RSA |
RSA_SSA_PSS_SHA512_MGF1 | -39 | PS512 | RSA |
Key Types
| Key Type | COSE | JOSE |
|---|---|---|
| Elliptic Curve | EC2 (2) | EC |
| RSA | RSA (3) | RSA |
| Octet Key Pair | OKP (1) | OKP |
Curves
| Curve | COSE | JOSE |
|---|---|---|
| P-256 | P-256 (1) | P-256 |
| P-384 | P-384 (2) | P-384 |
| P-521 | P-521 (3) | P-521 |
| Ed25519 | Ed25519 (6) | Ed25519 |
| X25519 | X25519 (4) | X25519 |
| secp256k1 | secp256k1 (8) | secp256k1 |
When to Use JOSE vs COSE
Use JOSE when:
- Working with OAuth 2.0 or OpenID Connect
- Implementing OID4VP or OID4VCI protocols
- Working with SD-JWT credentials
- Building web APIs
- Human readability aids debugging
Use COSE when:
- Working with ISO mDoc credentials (ISO 18013-5)
- Size constraints are critical
- Interoperating with CBOR-based systems
- Building IoT or constrained device applications
Many identity solutions use both formats. The IDK makes it straightforward to work with both and convert between them as needed.