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:
- Explicit provider ID - you specify which provider to use by passing its
providerId - Algorithm-based lookup - the service finds the first registered provider that supports the requested algorithm
- 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
| Provider | ID | Platforms | Storage | Use Case |
|---|---|---|---|---|
| Software | software | All | Memory / file | Development, testing, non-hardware scenarios |
| Mobile | mobile | iOS, Android | Secure Enclave / Android Keystore | Production mobile wallets |
| AWS KMS | aws_kms | JVM | HSM (cloud) | Server-side with AWS infrastructure |
| Azure Key Vault | Configurable | JVM, JS | HSM (cloud) | Server-side with Azure infrastructure |
| REST Client | rest | All | Remote | Delegated 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:
| Family | Algorithms |
|---|---|
| ECDSA | ES256 (P-256 + SHA-256), ES384 (P-384 + SHA-384), ES512 (P-521 + SHA-512), ES256K (secp256k1) |
| EdDSA | Ed25519 |
| RSA PKCS#1 | RS256, RS384, RS512 |
| RSA-PSS | PS256, PS384, PS512 |
| HMAC | HS256, 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:
| Algorithm | Type | Notes |
|---|---|---|
| A128GCM, A192GCM, A256GCM | AES-GCM | Authenticated encryption with associated data |
| A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 | AES-CBC + HMAC | Cloud 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:
| Algorithm | Notes |
|---|---|
| RSA-OAEP, RSA-OAEP-256, RSA-OAEP-512 | RSA-based wrapping (Software only for -512) |
| RSA1_5 | Legacy RSA wrapping (Azure only) |
| A128KW, A192KW, A256KW | AES 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
)
| Option | Default | Description |
|---|---|---|
id | "software" | Provider identifier |
enabled | true | Enable/disable this provider |
order | Medium | Priority when multiple providers match |
exposePrivateKeysDuringGeneration | true | Include private key material in the generation result |
persistKeysDuringGeneration | true | Persist keys to the backing keystore immediately |
The software provider also supports optional self-signed certificate generation via the autoCreateCertificate setting.
Supported Operations
| Category | Operations |
|---|---|
| Key management | Generate, import, export, delete, list |
| Signatures | ECDSA (ES256/384/512), EdDSA (Ed25519), RSA (RS256/384/512), RSA-PSS (PS256/384/512) |
| MAC | HMAC-SHA256, HMAC-SHA384, HMAC-SHA512 |
| Encryption | AES-GCM (128/192/256) |
| Key wrapping | RSA-OAEP, RSA-OAEP-256, RSA-OAEP-512, AES-KW (128/192/256) |
| Key agreement | ECDH-ES, ECDH-ES+A128KW/A192KW/A256KW |
| Certificates | Self-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
| Category | Operations |
|---|---|
| Key management | Generate, delete, list |
| Signatures | ECDSA (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
| Platform | Secure Storage | Preferred Curve |
|---|---|---|
| iOS | Secure Enclave | P-256 |
| Android | Android 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
| Category | Operations |
|---|---|
| Key management | Generate, import, delete, list (no private key export) |
| Signatures | ECDSA (ES256/384/512), RSA (RS256/384/512), RSA-PSS (PS256/384/512) |
| Encryption | AES-GCM (128/192/256), AES-CBC-HS (128/192/256) |
| Key wrapping | RSA-OAEP, RSA-OAEP-256 |
| Key agreement | ECDH-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
| Category | Operations |
|---|---|
| Key management | Generate, import, delete, list (no private key export) |
| Signatures | ECDSA (ES256/384/512), RSA (RS256/384/512), RSA-PSS (PS256/384/512) |
| Encryption | AES-GCM (128/192/256), AES-CBC-HS (128/192/256) |
| Key wrapping | RSA1_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.
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:
| Method | Description |
|---|---|
| Header-based | Static token in Authorization header |
| OAuth 2.0 | Client credentials flow |
| OIDC | OpenID Connect token exchange |
| Context-based | Forwards 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:
| Operation | Method | Path |
|---|---|---|
| Generate key | POST | /providers/{providerId}/keys/generate |
| List keys | GET | /providers/{providerId}/keys |
| Get key | GET | /providers/{providerId}/keys/{keyId} |
| Store key | POST | /providers/{providerId}/keys |
| Create signature | POST | /signatures/raw/create |
| Verify signature | POST | /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:
- The manager reads KMS provider configuration from the active
ConfigService(environment variables, property files, database settings, etc.) - For each configured provider, it finds the matching
KmsProviderFactoryvia DI - The factory creates a provider instance with the session's execution context
- 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:
| Algorithm | OID | Notes |
|---|---|---|
| SHA-256 | 2.16.840.1.101.3.4.2.1 | Most common, used with ES256/RS256/PS256 |
| SHA-384 | 2.16.840.1.101.3.4.2.2 | Used with ES384/RS384/PS384 |
| SHA-512 | 2.16.840.1.101.3.4.2.3 | Used with ES512/RS512/PS512 |
| SHA3-256 | 2.16.840.1.101.3.4.2.8 | SHA-3 family (software provider) |
| SHA3-384 | 2.16.840.1.101.3.4.2.9 | SHA-3 family (software provider) |
| SHA3-512 | 2.16.840.1.101.3.4.2.10 | SHA-3 family (software provider) |
Provider Comparison
Key Management
| Software | Mobile | AWS KMS | Azure KV | REST | |
|---|---|---|---|---|---|
| Generate keys | Yes | Yes | Yes | Yes | Yes |
| Import keys | Yes | - | Yes | Yes | Yes |
| Export private keys | Yes | - | - | - | Backend-dependent |
| Certificates (X.509) | Yes | - | - | - | Backend-dependent |
| Symmetric keys (HMAC/AES) | Yes | - | - | - | Backend-dependent |
Cryptographic Operations
| Software | Mobile | AWS KMS | Azure KV | REST | |
|---|---|---|---|---|---|
| ECDSA signing | Yes | Yes | Yes | Yes | Yes |
| EdDSA signing | Yes | - | - | - | Backend-dependent |
| RSA signing | Yes | Yes | Yes | Yes | Yes |
| HMAC (MAC) | Yes | - | - | - | Backend-dependent |
| AES-GCM encryption | Yes | - | Yes | Yes | Backend-dependent |
| AES-CBC encryption | - | - | Yes | Yes | - |
| Key wrapping | Yes | - | Yes | Yes | Backend-dependent |
| ECDH key agreement | Yes | - | Yes | - | Backend-dependent |
Storage and Security
| Software | Mobile | AWS KMS | Azure KV | REST | |
|---|---|---|---|---|---|
| Hardware backing | - | Yes | Yes (HSM) | Yes (HSM) | Backend-dependent |
| Persistent storage | Yes | Yes | Yes | Yes | Yes |
| Ephemeral mode | Yes | Yes | - | - | Yes |
| Private key exposure | Yes (configurable) | Never | Never | Never | Backend-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.