Skip to main content
Version: v0.13

Key Management

The IDK provides a unified key management system through the KeyManagerService. This service abstracts the details of different key storage backends, allowing you to generate, store, and use cryptographic keys consistently across platforms and providers.

KeyManagerService Overview

The KeyManagerService is the central entry point for all cryptographic key operations. It delegates actual key operations to registered KMS providers while presenting a consistent interface.

Key capabilities include:

  • Key generation with configurable algorithms
  • Key storage and retrieval
  • Signing and verification operations
  • Encryption and decryption
  • Key wrapping and unwrapping
  • Key agreement for establishing shared secrets
  • Provider capability querying

Accessing the Service

The KeyManagerService is available from the session component:

val keyManager = session.component.keyManagerService

Generating Keys

Generate cryptographic key pairs using the generateKeyAsync method:

import com.sphereon.crypto.core.SignatureAlgorithm
import com.sphereon.crypto.jose.JwkUse
import com.sphereon.crypto.core.KeyOperations

// Generate an ECDSA P-256 signing key
val signingKeyPair = keyManager.generateKeyAsync(
providerId = null, // Use default provider
alias = "my-signing-key",
use = JwkUse.sig,
keyOperations = arrayOf(KeyOperations.SIGN, KeyOperations.VERIFY),
alg = SignatureAlgorithm.ECDSA_SHA256,
keyVisibility = KeyVisibility.PRIVATE
)

// Generate with a specific provider
val awsKeyPair = keyManager.generateKeyAsync(
providerId = "aws",
alias = "my-aws-key",
use = JwkUse.sig,
alg = SignatureAlgorithm.ECDSA_SHA256
)

Supported Algorithms

The IDK supports the following signature algorithms:

AlgorithmDescriptionCurve/Size
ECDSA_SHA256ECDSA with SHA-256P-256
ECDSA_SHA384ECDSA with SHA-384P-384
ECDSA_SHA512ECDSA with SHA-512P-521
RSA_SHA256RSA PKCS#1 v1.5 with SHA-2562048+ bits
RSA_SHA384RSA PKCS#1 v1.5 with SHA-3842048+ bits
RSA_SHA512RSA PKCS#1 v1.5 with SHA-5122048+ bits
RSA_SSA_PSS_SHA256_MGF1RSA-PSS with SHA-2562048+ bits
RSA_SSA_PSS_SHA384_MGF1RSA-PSS with SHA-3842048+ bits
RSA_SSA_PSS_SHA512_MGF1RSA-PSS with SHA-5122048+ bits

ManagedKeyPair

After generating a key, you receive a ManagedKeyPair containing both COSE and JOSE representations:

// ManagedKeyPair contains both formats
val keyPair: ManagedKeyPair = keyManager.generateKeyAsync(...)

// Access COSE format
val cosePublicKey = keyPair.cose.publicCoseKey
val cosePrivateKey = keyPair.cose.privateCoseKey // May be null for hardware keys

// Access JOSE/JWK format
val publicJwk = keyPair.jose.publicJwk
val privateJwk = keyPair.jose.privateJwk // May be null for hardware keys

// Key identifiers
val kid = keyPair.kid
val alias = keyPair.alias
val providerId = keyPair.providerId

Key Info Types

The IDK provides different key info types for different use cases:

// Convert to ManagedKeyInfo for COSE operations
val coseKeyInfo: ManagedKeyInfoType<CoseKeyType> = keyPair.cborToManagedKeyInfo(
visibility = KeyVisibility.PUBLIC
)

// Convert to ManagedKeyInfo for JOSE operations
val joseKeyInfo: ManagedKeyInfoType<JwkType> = keyPair.joseToManagedKeyInfo(
visibility = KeyVisibility.PUBLIC
)

// Generic conversion with encoding specification
val keyInfo = keyPair.toManagedKeyInfo<CoseKeyType>(
visibility = KeyVisibility.PRIVATE,
keyEncoding = KeyEncoding.COSE
)

// Access key info properties
val providerId = keyInfo.providerId
val alias = keyInfo.alias
val signatureAlgorithm = keyInfo.signatureAlgorithm
val x5c = keyInfo.x5c // X.509 certificate chain if available

Signing Data

Create signatures using stored keys:

// Get key info for signing (needs private key access)
val keyInfo = keyPair.cborToManagedKeyInfo(visibility = KeyVisibility.PRIVATE)

// Sign raw bytes
val data = "Hello, World!".encodeToByteArray()
val signature = keyManager.createRawSignature(
keyInfo = keyInfo,
input = data,
requireX5Chain = false
)

// Sign with X.509 certificate chain requirement
val signatureWithCert = keyManager.createRawSignature(
keyInfo = keyInfo,
input = data,
requireX5Chain = true // Requires associated certificate
)

Verifying Signatures

Verify signatures against public keys:

// Get public key info for verification
val publicKeyInfo = keyPair.cborToManagedKeyInfo(visibility = KeyVisibility.PUBLIC)

// Verify the signature
val isValid = keyManager.isValidRawSignature(
keyInfo = publicKeyInfo,
input = data,
signature = signature
)

if (isValid) {
println("Signature is valid")
} else {
println("Signature verification failed")
}

Key Retrieval

Retrieve previously generated keys using the key store:

// List all keys in the key store
val allKeys = keyManager.keyStore.listKeys()
for (keyInfo in allKeys) {
println("Key: ${keyInfo.alias}, Provider: ${keyInfo.providerId}")
}

// Get a specific key
val keyInfo = KeyInfo<CoseKeyType>(
alias = "my-signing-key",
providerId = keyManager.defaultProviderId()
)
val existingKey = keyManager.keyStore.getKey(keyInfo)

Key Deletion

Remove keys that are no longer needed:

// Delete a specific key
val keyInfo = KeyInfo<CoseKeyType>(
alias = "my-signing-key",
providerId = keyManager.defaultProviderId()
)
val deleted = keyManager.keyStore.deleteKey(keyInfo)
if (deleted) {
println("Key deleted successfully")
}

Provider Management

Query and manage KMS providers:

// Get default provider ID
val defaultProvider = keyManager.defaultProviderId()

// List all registered providers
val providerIds = keyManager.getProviderIds()

// Get a specific provider
val provider = keyManager.getProviderById("software")

// Find provider by algorithm support
val ecdsaProvider = keyManager.getKmsBySignatureAlgorithm(SignatureAlgorithm.ECDSA_SHA256)

// Query providers by capabilities
val queryResult = keyManager.queryProviderAsync(
KmsProviderQuery(
signatureAlgorithm = SignatureAlgorithm.ECDSA_SHA256,
requireHardwareBacking = true
)
)

when (queryResult) {
is IdkResult.Success -> {
val provider = queryResult.value.provider
println("Found provider: ${provider.id}")
}
is IdkResult.Failure -> {
println("No matching provider found")
}
}

Provider Capabilities

Query what operations a provider supports:

// Get all capabilities from all providers
val capsResult = keyManager.getAllCapabilitiesAsync(includeDisabled = false)

when (capsResult) {
is IdkResult.Success -> {
for (cap in capsResult.value.capabilities) {
println("Provider: ${cap.providerId}")
println(" Storage types: ${cap.storageTypes.joinToString()}")
println(" Supports import: ${cap.supportsKeyImport}")
println(" Supports export: ${cap.supportsKeyExport}")
println(" Hardware backing: ${cap.supportsHardwareBacking}")
println(" Supported curves: ${cap.supportedCurves.joinToString()}")
println(" Supports signing: ${cap.supportsSigning()}")
println(" Supports encryption: ${cap.supportsEncryption()}")
}
}
is IdkResult.Failure -> {
println("Failed to get capabilities")
}
}

Key Lifecycle Best Practices

When managing cryptographic keys:

Generate keys with appropriate algorithms. ECDSA_SHA256 (ES256) is a good default for signing, offering a balance of security and performance.

Use meaningful aliases. Include context like purpose and creation date to simplify key management.

Scope keys appropriately. Keys are tied to their provider and cannot be transferred between providers.

Consider key rotation. For long-lived applications, implement key rotation where new keys are generated periodically and old keys retained for verification until they expire.

Use hardware backing when available. The mobile provider uses platform secure storage (iOS Secure Enclave, Android Keystore) which cannot export private keys.