Attempting to DECRYPT a cipher message using the Apple API SecKeyCreateDecryptedData(privateKey, .rsaEncryptionOAEPSHA256, encryptedMessage). Decryption ALWAYS fails for every algorithm.
SecKeyCreateDecryptedDataWithParameters Error: `Domain=NSOSStatusErrorDomain Code=-50 "algid:encrypt:RSA:OAEP:SHA256: algorithm not supported by the key <SecKeyRef:('com.yubico.Authenticator.TokenExtension:5621CDF8560D4C412030886584EC4C9E394CC376DD9738B0CCBB51924FC26EB6') 0x3007fd150>" UserInfo={numberOfErrorsDeep=0, NSDescription=algid:encrypt:RSA:OAEP:SHA256: algorithm not supported by the key <SecKeyRef:('com.yubico.Authenticator.TokenExtension:5621CDF8560D4C412030886584EC4C9E394CC376DD9738B0CCBB51924FC26EB6') 0x3007fd150>}`
Decryption failed: SecKeyCreateDecryptedData returned nil.
Error: One or more parameters passed to a function were not valid.
When checking with SecKeyIsAlgorithmSupported(privateKey, .decrypt, <ANYalgorithm>)
all algorithms fail. Btw - The privateKey does support decryption when retrieving the attributes.
Important to know:
The private key is a reference to an external private key placed in the iOS Keychain via a 3rd party CryptoTokenKit Extension app. When I perform, the SecKeyCreateSignature(...) and pass in the SAME privateKey reference, the OS automatically calls the 3rd party app to perform a successful signing with the private key that reside on a YubiKey.
Here's my code for obtaining the private key reference from an Identity:
func getKeyPairFromIdentity() -> (privateKey: SecKey, publicKey: SecKey)? {
let query = NSDictionary(
dictionary: [
kSecClass as String: kSecClassIdentity,
kSecAttrTokenID as String: self.tokenID!,
kSecReturnRef as String: kCFBooleanTrue as Any
]
)
var identityRef: CFTypeRef?
let status = SecItemCopyMatching(query, &identityRef)
if status == errSecSuccess, let identity = identityRef {
var privateKeyRef: SecKey?
let keyStatus = SecIdentityCopyPrivateKey(identity as! SecIdentity, &privateKeyRef)
if keyStatus == errSecSuccess, let privateKey = privateKeyRef {
let publicKey = SecKeyCopyPublicKey(privateKey)
if let publicKey = publicKey {
print("Private and public keys extracted successfully.")
return (privateKey, publicKey)
} else {
print("Failed to extract public key from private key.")
return nil
}
} else {
print("SecIdentityCopyPrivateKey: Private key not found error: \(keyStatus)")
return nil
}
} else {
print("SecIdentity not found or error: \(status)")
return nil
}
}
Thanks for all the background info.
Here's the attributes of the private key reference
That’s interesting. Note the presence of sign
and decr
, both with a value of 1. Those correspond to kSecAttrCanSign
and kSecAttrCanDecrypt
, which is the key indicating that it supports both signing and decryption.
If this were a normal key then it’d be the end of the story. However, for the case of a CTK-backed key then the actual work is done by the CTK appex. Whether a specific operation type or algorithm is supported or not depends on the appex. Given that you have access to the appex’s source code, I can explain the mechanics of that and then you can look up in the code to see what it does in that case.
Every CTK appex include a TKTokenSession
subclass [1]. That subclass typically acts as its own delegate. The delegate has to implement the tokenSession(_:supports:keyObjectID:algorithm:)
method. This determines what algorithms it supports. And, based on that, it then needs to support one or more of the following:
-
The
tokenSession(_:sign:keyObjectID:algorithm:)
method for signing -
The
tokenSession(_:decrypt:keyObjectID:algorithm:)
method for decrypting -
The
tokenSession(_:performKeyExchange:keyObjectID:algorithm:parameters:)
method for key exchange
Finally, keep in mind that a CTK appex associated with hardware will typically bundle up the crypto operation and pass it along to the hardware. That’s whole point of having a hardware token, in that the private key never actually leaves the hardware.
Different hardware places different limits on what operations a particular key can perform. And the hardware might support multiple keys, each with its own set of supported operations. For example, a PIV smart card typically supports signing in slot 9C and decryption in slot 9D.
If the hardware has such a limit then there’s nothing that the CTK appex can do about it. The best you can hope for is for it to set kSecAttrCanSign
and kSecAttrCanDecrypt
correctly.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] A smart card token actually subclasses TKSmartCardTokenSession
, which is itself a subclass of TKTokenSession
. You’re using a virtual token, so I’m gonna focus on that.
[2] One day we’ll update the docs to make the exact mechanics of that clear (r. 90838636).