Skip to main content
Version: v0.25.0 (Latest)

KMS Providers

The IDK's key management system (KMS) supports multiple pluggable providers that handle cryptographic key storage and operations. Each provider targets a different backend - from in-memory software keys for development, to hardware-backed mobile secure enclaves, to cloud HSMs in AWS and Azure. Multiple providers can be active at the same time, and the KeyManagerService routes operations to the right one automatically.

How Provider Selection Works

Every KMS provider registers itself with the KmsProviderRegistry in the current session scope. When you call an operation on the KeyManagerService - such as generating a key or creating a signature - the service selects a provider in one of three ways:

  1. Explicit provider ID - you specify which provider to use by passing its providerId
  2. Algorithm-based lookup - the service finds the first registered provider that supports the requested algorithm
  3. Default provider - if no provider or algorithm is specified, the default provider is used

You can also query providers programmatically to find one that matches specific requirements:

val result = keyManager.queryProvider(kmsQuery {
operation = KmsProviderOperation.SIGN
signatureAlgorithm = SignatureAlgorithm.ECDSA_SHA256
requiresHardwareBacking = true
})

if (result.isOk) {
val provider = result.value.provider
// use provider...
}

The query DSL supports filtering by operation, algorithm, curve, key type, storage type, hardware backing, attestation, and more.

Available Providers

ProviderIDPlatformsStorageUse Case
SoftwaresoftwareAllMemory / fileDevelopment, testing, non-hardware scenarios
MobilemobileiOS, AndroidSecure Enclave / Android KeystoreProduction mobile wallets
AWS KMSaws_kmsJVMHSM (cloud)Server-side with AWS infrastructure
Azure Key VaultConfigurableJVM, JSHSM (cloud)Server-side with Azure infrastructure
REST ClientrestAllRemoteDelegated operations to a remote KMS server

Operations Overview

Providers can support different subsets of the following operations. The KmsProvider interface unifies them all behind a single abstraction.

Key Generation

Generate asymmetric key pairs (EC, RSA) or symmetric keys (for HMAC, AES):

// Asymmetric key pair (EC P-256)
val ecKey = keyManager.generateKeyAsync(
alias = "my-signing-key",
alg = SignatureAlgorithm.ECDSA_SHA256
)

// RSA key pair
val rsaKey = keyManager.generateKeyAsync(
alias = "my-rsa-key",
alg = SignatureAlgorithm.RSA_SHA256
)

// Symmetric key (HMAC)
val hmacKey = keyManager.generateKeyAsync(
alias = "my-hmac-key",
alg = SignatureAlgorithm.HMAC_SHA256
)

The returned ManagedKeyPair contains both COSE and JOSE (JWK) representations of the key, along with the key alias and provider ID.

Signing and Verification

Create and verify digital signatures. All asymmetric signature algorithms are supported - ECDSA, RSA PKCS#1 v1.5, and RSA-PSS:

val signature = keyManager.createRawSignature(
keyInfo = keyRef,
input = dataToSign,
requireX5Chain = false
)

val isValid = keyManager.isValidRawSignature(
keyInfo = keyRef,
input = dataToSign,
signature = signature
)

Supported signature algorithms:

FamilyAlgorithms
ECDSAES256 (P-256 + SHA-256), ES384 (P-384 + SHA-384), ES512 (P-521 + SHA-512), ES256K (secp256k1)
EdDSAEd25519
RSA PKCS#1RS256, RS384, RS512
RSA-PSSPS256, PS384, PS512
HMACHS256, HS384, HS512 (see MAC operations below)

MAC (HMAC) Operations

Generate and verify message authentication codes using symmetric HMAC keys. Unlike signatures, MACs use a shared secret key:

// Generate an HMAC
val mac = provider.generateMac(
keyId = "my-hmac-key",
message = data,
digestAlgorithm = DigestAlg.SHA256
)

// Verify an HMAC
val valid = provider.verifyMac(
keyId = "my-hmac-key",
message = data,
mac = mac,
digestAlgorithm = DigestAlg.SHA256
)

On cloud providers (AWS KMS, Azure Key Vault), HMAC computation runs inside the HSM - the symmetric key material never leaves the hardware.

Encryption and Decryption

Encrypt and decrypt data using AES-GCM or AES-CBC content encryption algorithms:

val encrypted = keyManager.encrypt(
keyInfo = keyRef,
plaintext = data,
algorithm = ContentEncryptionAlgorithm.A256GCM,
additionalAuthenticatedData = aad // optional AAD for GCM
)
// encrypted.ciphertext, encrypted.iv, encrypted.authTag

val decrypted = keyManager.decrypt(
keyInfo = keyRef,
ciphertext = encrypted.ciphertext,
algorithm = ContentEncryptionAlgorithm.A256GCM,
iv = encrypted.iv,
authTag = encrypted.authTag,
additionalAuthenticatedData = aad
)

Supported content encryption algorithms:

AlgorithmTypeNotes
A128GCM, A192GCM, A256GCMAES-GCMAuthenticated encryption with associated data
A128CBC-HS256, A192CBC-HS384, A256CBC-HS512AES-CBC + HMACCloud providers (AWS, Azure) only

Key Wrapping and Unwrapping

Wrap (encrypt) a key using another key, commonly used for envelope encryption:

val wrappedKey = keyManager.wrapKey(
wrappingKeyInfo = wrappingKeyRef,
keyToWrap = rawKeyBytes,
algorithm = KeyWrapAlgorithm.RSA_OAEP_256
)

val unwrappedKey = keyManager.unwrapKey(
unwrappingKeyInfo = wrappingKeyRef,
wrappedKey = wrappedKey,
algorithm = KeyWrapAlgorithm.RSA_OAEP_256
)

Supported key wrap algorithms:

AlgorithmNotes
RSA-OAEP, RSA-OAEP-256, RSA-OAEP-512RSA-based wrapping (Software only for -512)
RSA1_5Legacy RSA wrapping (Azure only)
A128KW, A192KW, A256KWAES Key Wrap

Key Agreement (ECDH)

Derive a shared secret from an EC private key and a peer's public key:

val sharedSecret = keyManager.performKeyAgreement(
privateKeyInfo = myPrivateKeyRef,
publicKeyInfo = peerPublicKeyRef,
algorithm = KeyAgreementAlgorithm.ECDH_ES,
keyDataLen = 256
)

Used internally for JWE encryption with ECDH-ES algorithms.

Capability Introspection

Every provider declares its full capabilities via getCapabilities(). This lets you inspect what a provider supports at runtime:

val capabilities = provider.getCapabilities()

// What operations are available?
capabilities.operations // [SIGN, VERIFY, ENCRYPT, DECRYPT, GENERATE_KEY, ...]

// What algorithms?
capabilities.signatureAlgorithms // [ECDSA_SHA256, RSA_SHA256, HMAC_SHA256, ...]
capabilities.contentEncryptionAlgorithms // [A128GCM, A256GCM, ...]
capabilities.supportedDigestAlgorithms // [SHA256, SHA384, SHA512]
capabilities.supportedCurves // [P_256, P_384, P_521]

// Storage and security
capabilities.storageTypes // [PERSISTENT, EPHEMERAL] or [HARDWARE]
capabilities.supportsHardwareBacking // true for Mobile, AWS, Azure
capabilities.supportsKeyImport // true for Software, AWS, Azure, REST
capabilities.supportsKeyExport // true for Software, REST
capabilities.exposePrivateKeys // true for Software (configurable)
capabilities.supportsX509 // true for Software

Querying Multiple Providers

Find all providers matching a set of requirements:

val result = keyManager.queryProviders(kmsQuery {
operation = KmsProviderOperation.ENCRYPT
contentEncryptionAlgorithm = ContentEncryptionAlgorithm.A256GCM
})

if (result.isOk) {
result.value.providers.forEach { match ->
println("${match.providerId} supports A256GCM encryption")
}
}

Software Provider

The software provider performs all cryptographic operations in-process using the whyoleg/cryptography library. Keys can be stored in memory (ephemeral) or persisted to a keystore file.

When to use: Development, testing, cross-platform scenarios where hardware backing is not required, or when you need to export private keys.

Configuration

val softwareConfig = KmsProviderConfigBase(
kmsProviderType = PredefinedKmsProviderTypes.SOFTWARE.value,
id = "software",
enabled = true,
exposePrivateKeysDuringGeneration = true,
persistKeysDuringGeneration = true
)
OptionDefaultDescription
id"software"Provider identifier
enabledtrueEnable/disable this provider
orderMediumPriority when multiple providers match
exposePrivateKeysDuringGenerationtrueInclude private key material in the generation result
persistKeysDuringGenerationtruePersist keys to the backing keystore immediately

The software provider also supports optional self-signed certificate generation via the autoCreateCertificate setting.

Supported Operations

CategoryOperations
Key managementGenerate, import, export, delete, list
SignaturesECDSA (ES256/384/512), EdDSA (Ed25519), RSA (RS256/384/512), RSA-PSS (PS256/384/512)
MACHMAC-SHA256, HMAC-SHA384, HMAC-SHA512
EncryptionAES-GCM (128/192/256)
Key wrappingRSA-OAEP, RSA-OAEP-256, RSA-OAEP-512, AES-KW (128/192/256)
Key agreementECDH-ES, ECDH-ES+A128KW/A192KW/A256KW
CertificatesSelf-signed generation, import

Key Types

  • EC: P-256, P-384, P-521
  • RSA: 2048, 3072, 4096
  • Symmetric: HMAC keys (SHA-256/384/512), AES keys

Mobile Provider

The mobile provider uses platform-native secure storage - iOS Secure Enclave or Android Keystore - via the Signum library. Private keys never leave the secure hardware and cannot be exported.

When to use: Production mobile wallets and any mobile scenario where hardware-backed key storage is required.

Key Characteristics

  • Private keys are generated inside and never leave the secure hardware
  • Keys are bound to the device - they cannot be exported or backed up
  • Biometric or device-unlock authentication can be required for key use (platform-dependent)
  • Keys survive app restarts but may be lost on app uninstall (platform-dependent)
  • Key import is not supported - keys must be generated on-device

Configuration

val mobileConfig = KmsProviderConfigBase(
kmsProviderType = PredefinedKmsProviderTypes.MOBILE.value,
id = "mobile",
enabled = true
)

Supported Operations

CategoryOperations
Key managementGenerate, delete, list
SignaturesECDSA (ES256/384/512), RSA (RS256/384/512), RSA-PSS (PS256/384/512)

Encryption, key wrapping, key agreement, and MAC operations are not supported by the mobile provider. Use the software provider alongside it if you need these operations.

Platform Behaviour

PlatformSecure StoragePreferred Curve
iOSSecure EnclaveP-256
AndroidAndroid Keystore (hardware-backed when available)P-256

AWS KMS Provider

The AWS KMS provider delegates all cryptographic operations to AWS Key Management Service. Keys are HSM-backed and never leave the AWS infrastructure. Only the public key is available locally.

When to use: Server-side applications running on AWS, or any scenario requiring centralized cloud-hosted key management with HSM protection and audit logging.

Configuration

AWS KMS is configured via KeyProviderSettings containing AWS-specific client settings (region, credentials). The provider is auto-discovered when the aws KMS module is on the classpath and AWS credentials are available.

val awsConfig = KmsProviderConfigBase(
kmsProviderType = PredefinedKmsProviderTypes.AWS_KMS.value,
id = "aws_kms",
enabled = true,
exposePrivateKeysDuringGeneration = false
)

AWS credentials are resolved via the standard SDK credential chain (environment variables, ~/.aws/credentials, IAM roles, ECS task credentials).

Supported Operations

CategoryOperations
Key managementGenerate, import, delete, list (no private key export)
SignaturesECDSA (ES256/384/512), RSA (RS256/384/512), RSA-PSS (PS256/384/512)
EncryptionAES-GCM (128/192/256), AES-CBC-HS (128/192/256)
Key wrappingRSA-OAEP, RSA-OAEP-256
Key agreementECDH-ES (via AWS DeriveSharedSecret API)

Key Types

  • EC: P-256, P-384, P-521
  • RSA: 2048, 3072, 4096

IAM Permissions

The AWS credentials need the following KMS permissions:

{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"kms:CreateKey",
"kms:Sign",
"kms:Verify",
"kms:GetPublicKey",
"kms:DescribeKey",
"kms:ScheduleKeyDeletion",
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey",
"kms:DeriveSharedSecret"
],
"Resource": "*"
}]
}

Azure Key Vault Provider

The Azure Key Vault provider delegates operations to Azure Key Vault. Like AWS KMS, keys are HSM-backed and private key material never leaves the vault.

When to use: Server-side applications in Azure environments, or when Azure compliance certifications are required.

Configuration

val azureConfig = KmsProviderConfigBase(
kmsProviderType = PredefinedKmsProviderTypes.AZURE_KEYVAULT.value,
id = "azure-keyvault",
enabled = true,
exposePrivateKeysDuringGeneration = false
)

Azure authentication and vault URL are configured via the provider's AzureKmsProviderConfig, typically resolved from environment variables or application configuration.

Supported Operations

CategoryOperations
Key managementGenerate, import, delete, list (no private key export)
SignaturesECDSA (ES256/384/512), RSA (RS256/384/512), RSA-PSS (PS256/384/512)
EncryptionAES-GCM (128/192/256), AES-CBC-HS (128/192/256)
Key wrappingRSA1_5, RSA-OAEP, RSA-OAEP-256, AES-KW (128/192/256)

Key Types

  • EC: P-256, P-384, P-521, secp256k1
  • RSA: 2048

Azure Key Vault supports secp256k1 (used in blockchain/Bitcoin contexts) which most other providers do not.

note

Key agreement (ECDH) is not supported by Azure Key Vault. If you need ECDH alongside Azure, register a software provider as a secondary provider.

REST Client Provider

The REST client provider delegates all operations to a remote KMS server over HTTP. The actual capabilities depend on the remote backend - the REST provider acts as a transparent proxy.

When to use: When cryptographic operations must be performed by a central service (e.g., a compliance-controlled signing server), or when mobile/web clients need to call a backend KMS without direct access to cloud APIs.

Configuration

val restConfig = KmsProviderConfigBase(
kmsProviderType = PredefinedKmsProviderTypes.REST.value,
id = "rest",
enabled = true,
defaultConfigValues = mapOf(
"restKmsUrl" to "https://kms.example.com",
"restProviderId" to "software" // which provider on the remote server
)
)

Authentication

The REST provider supports multiple authentication methods configured via RestClientAuthConfig:

MethodDescription
Header-basedStatic token in Authorization header
OAuth 2.0Client credentials flow
OIDCOpenID Connect token exchange
Context-basedForwards the current tenant and principal IDs as headers (X-Tenant-ID, X-User-ID)

API Endpoints

The REST provider calls the following endpoints on the remote server:

OperationMethodPath
Generate keyPOST/providers/{providerId}/keys/generate
List keysGET/providers/{providerId}/keys
Get keyGET/providers/{providerId}/keys/{keyId}
Store keyPOST/providers/{providerId}/keys
Create signaturePOST/signatures/raw/create
Verify signaturePOST/signatures/raw/verify

Using Multiple Providers

The KeyManagerService orchestrates multiple providers. Each generated key tracks which provider owns it, so subsequent operations (signing, encryption) are automatically routed to the correct provider:

// Register providers
keyManager.registerProvider(softwareProvider, makeDefaultKms = true)
keyManager.registerProvider(awsProvider, makeDefaultKms = false)

// Generate keys in different providers
val localKey = keyManager.generateKeyAsync(
providerId = "software",
alias = "local-signing-key",
alg = SignatureAlgorithm.ECDSA_SHA256
)

val cloudKey = keyManager.generateKeyAsync(
providerId = "aws_kms",
alias = "cloud-signing-key",
alg = SignatureAlgorithm.ECDSA_SHA256
)

// Signing automatically routes to the right provider
val localSig = keyManager.createRawSignature(localKey.toKeyInfo(), data, false)
// → uses software provider

val cloudSig = keyManager.createRawSignature(cloudKey.toKeyInfo(), data, false)
// → uses AWS KMS (signature computed in HSM)

Provider Auto-Discovery

Providers are created per session by the KmsProviderManager using the factory pattern. When a session starts:

  1. The manager reads KMS provider configuration from the active ConfigService (environment variables, property files, database settings, etc.)
  2. For each configured provider, it finds the matching KmsProviderFactory via DI
  3. The factory creates a provider instance with the session's execution context
  4. The provider is registered with the session's KmsProviderRegistry

This means providers are tenant-isolated - each tenant can have different KMS configurations, and their keys are stored separately.

Digest Algorithms

The system supports the following digest (hash) algorithms across providers:

AlgorithmOIDNotes
SHA-2562.16.840.1.101.3.4.2.1Most common, used with ES256/RS256/PS256
SHA-3842.16.840.1.101.3.4.2.2Used with ES384/RS384/PS384
SHA-5122.16.840.1.101.3.4.2.3Used with ES512/RS512/PS512
SHA3-2562.16.840.1.101.3.4.2.8SHA-3 family (software provider)
SHA3-3842.16.840.1.101.3.4.2.9SHA-3 family (software provider)
SHA3-5122.16.840.1.101.3.4.2.10SHA-3 family (software provider)

Provider Comparison

Key Management

SoftwareMobileAWS KMSAzure KVREST
Generate keysYesYesYesYesYes
Import keysYes-YesYesYes
Export private keysYes---Backend-dependent
Certificates (X.509)Yes---Backend-dependent
Symmetric keys (HMAC/AES)Yes---Backend-dependent

Cryptographic Operations

SoftwareMobileAWS KMSAzure KVREST
ECDSA signingYesYesYesYesYes
EdDSA signingYes---Backend-dependent
RSA signingYesYesYesYesYes
HMAC (MAC)Yes---Backend-dependent
AES-GCM encryptionYes-YesYesBackend-dependent
AES-CBC encryption--YesYes-
Key wrappingYes-YesYesBackend-dependent
ECDH key agreementYes-Yes-Backend-dependent

Storage and Security

SoftwareMobileAWS KMSAzure KVREST
Hardware backing-YesYes (HSM)Yes (HSM)Backend-dependent
Persistent storageYesYesYesYesYes
Ephemeral modeYesYes--Yes
Private key exposureYes (configurable)NeverNeverNeverBackend-dependent

Choosing a Provider

Production mobile app - Use the Mobile provider. Keys stay in hardware and cannot be extracted. Add a Software provider alongside it if you also need encryption or key agreement.

Server-side (AWS) - Use AWS KMS for signing keys that need HSM protection and audit trails. Use Software for ephemeral or less sensitive keys.

Server-side (Azure) - Use Azure Key Vault. Note that ECDH is not supported - add a Software provider if you need key agreement.

Development and testing - Use the Software provider with exposePrivateKeysDuringGeneration = true so you can inspect keys during debugging.

Mobile-to-server delegation - Use the REST provider on mobile to delegate signing to a backend server that itself uses AWS KMS or Azure Key Vault.