Signing and Verification
The IDK provides comprehensive support for creating and verifying cryptographic signatures. This guide covers raw signatures, the signature algorithms supported, and best practices.
Raw Signatures
Raw signatures operate directly on byte arrays without additional structure. They're suitable when you need full control over the data format or when integrating with systems that expect raw signatures.
Creating Raw Signatures
- Android/Kotlin
- iOS/Swift
val keyManager = session.component.keyManagerService
// Generate or retrieve a signing key
val keyPair = keyManager.generateKeyAsync(
providerId = null,
alias = "signing-key",
alg = SignatureAlgorithm.ECDSA_SHA256
)
// Prepare the data to sign
val data = "Important message to sign".encodeToByteArray()
// Get key info for signing (need private key access)
val keyInfo = keyPair.cborToManagedKeyInfo(visibility = KeyVisibility.PRIVATE)
// Create the signature
val signature = keyManager.createRawSignature(
keyInfo = keyInfo,
input = data,
requireX5Chain = false
)
// The signature is a raw byte array
println("Signature length: ${signature.size} bytes")
let keyManager = session.component.keyManagerService
// Generate or retrieve a signing key
let keyPair = try await keyManager.generateKeyAsync(
providerId: nil,
alias: "signing-key",
alg: .ecdsaSha256
)
// Prepare the data to sign
let data = "Important message to sign".data(using: .utf8)!
// Get key info for signing (need private key access)
let keyInfo = keyPair.cborToManagedKeyInfo(visibility: .private_)
// Create the signature
let signature = try await keyManager.createRawSignature(
keyInfo: keyInfo,
input: data,
requireX5Chain: false
)
// The signature is a raw byte array
print("Signature length: \(signature.count) bytes")
Verifying Raw Signatures
- Android/Kotlin
- iOS/Swift
// 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 verified successfully")
} else {
println("Signature verification failed - data may have been tampered with")
}
// Get public key info for verification
let publicKeyInfo = keyPair.cborToManagedKeyInfo(visibility: .public_)
// Verify the signature
let isValid = try await keyManager.isValidRawSignature(
keyInfo: publicKeyInfo,
input: data,
signature: signature
)
if isValid {
print("Signature verified successfully")
} else {
print("Signature verification failed - data may have been tampered with")
}
Signature Algorithms
The IDK supports multiple signature algorithms. Choose based on your security requirements and interoperability needs.
ECDSA (Elliptic Curve Digital Signature Algorithm)
ECDSA is the most common choice for mobile applications due to its compact signatures and good performance.
| Algorithm | Curve | Signature Size | Security Level |
|---|---|---|---|
ECDSA_SHA256 | P-256 | 64 bytes | 128-bit |
ECDSA_SHA384 | P-384 | 96 bytes | 192-bit |
ECDSA_SHA512 | P-521 | 132 bytes | 256-bit |
- Android/Kotlin
- iOS/Swift
// ECDSA_SHA256 - recommended for most use cases
val es256Key = keyManager.generateKeyAsync(
alias = "es256-key",
alg = SignatureAlgorithm.ECDSA_SHA256
)
// ECDSA_SHA384 - higher security, larger signatures
val es384Key = keyManager.generateKeyAsync(
alias = "es384-key",
alg = SignatureAlgorithm.ECDSA_SHA384
)
// ECDSA_SHA512 - highest security, largest signatures
val es512Key = keyManager.generateKeyAsync(
alias = "es512-key",
alg = SignatureAlgorithm.ECDSA_SHA512
)
// ECDSA_SHA256 - recommended for most use cases
let es256Key = try await keyManager.generateKeyAsync(
alias: "es256-key",
alg: .ecdsaSha256
)
// ECDSA_SHA384 - higher security, larger signatures
let es384Key = try await keyManager.generateKeyAsync(
alias: "es384-key",
alg: .ecdsaSha384
)
// ECDSA_SHA512 - highest security, largest signatures
let es512Key = try await keyManager.generateKeyAsync(
alias: "es512-key",
alg: .ecdsaSha512
)
RSA Signatures
RSA signatures are larger but offer broad compatibility with legacy systems.
| Algorithm | Description |
|---|---|
RSA_SHA256 | RSA PKCS#1 v1.5 with SHA-256 |
RSA_SHA384 | RSA PKCS#1 v1.5 with SHA-384 |
RSA_SHA512 | RSA PKCS#1 v1.5 with SHA-512 |
RSA_SSA_PSS_SHA256_MGF1 | RSA-PSS with SHA-256 |
RSA_SSA_PSS_SHA384_MGF1 | RSA-PSS with SHA-384 |
RSA_SSA_PSS_SHA512_MGF1 | RSA-PSS with SHA-512 |
- Android/Kotlin
- iOS/Swift
// RSA-PSS with SHA-256 (recommended over PKCS#1 v1.5)
val rsaPssKey = keyManager.generateKeyAsync(
alias = "rsa-pss-key",
alg = SignatureAlgorithm.RSA_SSA_PSS_SHA256_MGF1
)
// RSA PKCS#1 v1.5 with SHA-256 (for legacy compatibility)
val rsaKey = keyManager.generateKeyAsync(
alias = "rsa-key",
alg = SignatureAlgorithm.RSA_SHA256
)
// RSA-PSS with SHA-256 (recommended over PKCS#1 v1.5)
let rsaPssKey = try await keyManager.generateKeyAsync(
alias: "rsa-pss-key",
alg: .rsaSsaPssSha256Mgf1
)
// RSA PKCS#1 v1.5 with SHA-256 (for legacy compatibility)
let rsaKey = try await keyManager.generateKeyAsync(
alias: "rsa-key",
alg: .rsaSha256
)
Including X.509 Certificates
When signing for external parties, you may need to include the X.509 certificate chain that vouches for your signing key:
- Android/Kotlin
- iOS/Swift
// Sign with certificate chain included
val signature = keyManager.createRawSignature(
keyInfo = keyInfo,
input = data,
requireX5Chain = true // Include X.509 certificate chain
)
// The key info must have an associated certificate for this to work
// If no certificate is available and requireX5Chain is true,
// the operation will fail
// Sign with certificate chain included
let signature = try await keyManager.createRawSignature(
keyInfo: keyInfo,
input: data,
requireX5Chain: true // Include X.509 certificate chain
)
// The key info must have an associated certificate for this to work
// If no certificate is available and requireX5Chain is true,
// the operation will fail
Encryption and Decryption
The KeyManagerService also supports content encryption operations:
- Android/Kotlin
- iOS/Swift
// Encrypt data
val encryptionResult = keyManager.encrypt(
keyInfo = keyInfo,
plaintext = "Secret message".encodeToByteArray(),
algorithm = ContentEncryptionAlgorithm.A256GCM,
additionalAuthenticatedData = "context".encodeToByteArray() // Optional AAD
)
// Access encrypted components
val ciphertext = encryptionResult.ciphertext
val iv = encryptionResult.iv
val authTag = encryptionResult.authTag
// Decrypt data
val plaintext = keyManager.decrypt(
keyInfo = keyInfo,
ciphertext = ciphertext,
algorithm = ContentEncryptionAlgorithm.A256GCM,
iv = iv,
authTag = authTag,
additionalAuthenticatedData = "context".encodeToByteArray()
)
// Encrypt data
let encryptionResult = try await keyManager.encrypt(
keyInfo: keyInfo,
plaintext: "Secret message".data(using: .utf8)!,
algorithm: .a256gcm,
additionalAuthenticatedData: "context".data(using: .utf8) // Optional AAD
)
// Access encrypted components
let ciphertext = encryptionResult.ciphertext
let iv = encryptionResult.iv
let authTag = encryptionResult.authTag
// Decrypt data
let plaintext = try await keyManager.decrypt(
keyInfo: keyInfo,
ciphertext: ciphertext,
algorithm: .a256gcm,
iv: iv,
authTag: authTag,
additionalAuthenticatedData: "context".data(using: .utf8)
)
Content Encryption Algorithms
| Algorithm | Description |
|---|---|
A128GCM | AES-128-GCM |
A192GCM | AES-192-GCM |
A256GCM | AES-256-GCM |
Key Wrapping
Wrap and unwrap keys for secure key transport:
- Android/Kotlin
- iOS/Swift
// Wrap a key for transport
val wrappedKey = keyManager.wrapKey(
wrappingKeyInfo = wrappingKeyInfo,
keyToWrap = secretKey,
algorithm = KeyWrapAlgorithm.RSA_OAEP_256
)
// Unwrap a received key
val unwrappedKey = keyManager.unwrapKey(
unwrappingKeyInfo = unwrappingKeyInfo,
wrappedKey = wrappedKey,
algorithm = KeyWrapAlgorithm.RSA_OAEP_256
)
// Wrap a key for transport
let wrappedKey = try await keyManager.wrapKey(
wrappingKeyInfo: wrappingKeyInfo,
keyToWrap: secretKey,
algorithm: .rsaOaep256
)
// Unwrap a received key
let unwrappedKey = try await keyManager.unwrapKey(
unwrappingKeyInfo: unwrappingKeyInfo,
wrappedKey: wrappedKey,
algorithm: .rsaOaep256
)
Key Wrap Algorithms
| Algorithm | Description |
|---|---|
RSA_OAEP | RSA-OAEP with SHA-1 |
RSA_OAEP_256 | RSA-OAEP with SHA-256 |
RSA_OAEP_512 | RSA-OAEP with SHA-512 |
Key Agreement
Perform ECDH key agreement to establish shared secrets:
- Android/Kotlin
- iOS/Swift
// Perform ECDH key agreement
val sharedSecret = keyManager.performKeyAgreement(
privateKeyInfo = myPrivateKeyInfo,
publicKeyInfo = theirPublicKeyInfo,
algorithm = KeyAgreementAlgorithm.ECDH_ES_HKDF_256,
keyDataLen = 32 // Desired output length in bytes
)
// Use the shared secret for symmetric encryption
// Perform ECDH key agreement
let sharedSecret = try await keyManager.performKeyAgreement(
privateKeyInfo: myPrivateKeyInfo,
publicKeyInfo: theirPublicKeyInfo,
algorithm: .ecdhEsHkdf256,
keyDataLen: 32 // Desired output length in bytes
)
// Use the shared secret for symmetric encryption
Key Agreement Algorithms
| Algorithm | Description |
|---|---|
ECDH_ES_HKDF_256 | ECDH-ES with HKDF-SHA256 |
ECDH_ES_HKDF_512 | ECDH-ES with HKDF-SHA512 |
ECDH_SS_HKDF_256 | ECDH-SS with HKDF-SHA256 |
ECDH_SS_HKDF_512 | ECDH-SS with HKDF-SHA512 |
Best Practices
When implementing signing and verification:
Always verify signatures before trusting data. Never assume data is authentic without cryptographic verification.
Use appropriate algorithms for your security requirements. ECDSA_SHA256 provides good security for most applications. Use ECDSA_SHA384 or ECDSA_SHA512 for higher security requirements.
Protect signing keys. Use the mobile KMS provider for hardware-backed protection on mobile devices.
Consider replay attacks. Signatures alone don't prevent replay attacks. Include timestamps, nonces, or sequence numbers in signed data.
Log signature failures. Failed signature verifications may indicate attacks or data corruption and should be logged for security monitoring.
Handle errors appropriately. Signature operations can fail for various reasons including key not found, hardware errors, or missing certificates.