Quick Summary
I'm having trouble using SecKeyCreateSignature(deviceSigningKeyRef, .ecdsaSignatureMessageX962SHA256, digest, &error) but when using SecureEnclave.P256.KeyAgreement.PrivateKey().signature(for: digest) the other code I'm using to verify succeeds.
Full use case and code
If I just initiate a SecureEnclave.P256.KeyAgreement.PrivateKey() class variable and then later use signature(for: digest).rawRepresentation to generate a signature, I get a signature value that can be passed to the verifying code
class MyClass {
var myPrivateKey: SecureEnclave.P256.KeyAgreement.PrivateKey?
init() {
myPrivateKey = SecureEnclave.P256.KeyAgreement.PrivateKey()
let myPublicKey = myPrivateKey?.publicKey.rawRepresentation
}
func createAndSendSignature(_ digest: Data) {
let signature = try? myPrivateKey?.signature(for: digest).rawRepresentation // 64 bytes
sendSignatureWithDigest(signature, digest)
}
}
But if I create my key in keychain via Secure Enclave with the way the documentation recommends (here's a few links to start Signing/Verifying, Keys for encryption), and then retrieve the key representation and use SecKeyCreateSignature, the resulting signature (which I manipulate a little more because it is DER encoded and does not comes back as 64 bytes) fails against the verifying code.
class MyClass {
var myKeyTag: String = "myKeyTag"
func createAndStoreKey() {
let access = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
.privateKeyUsage,
nil)! // Ignore errors.
let attributes: NSDictionary = [
kSecClass as String: kSecClassKey,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: myKeyTag,
kSecAttrAccessControl as String: access,
kSecAttrCanSign as String: true,
]
]
var error: Unmanaged<CFError>?
guard let keyRef: SecKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
return keyRef as SecKey!
}
func getKey(){
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: myKeyTag,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecReturnRef as String: true,
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess else {
throw KeyStoreError("Unable to retrieve key: \(status.message)")
}
return (item as! SecKey)
}
func createAndSendSignature(_ digest: Data) {
let privKey = getKey()
let signature = SecKeyCreateSignature(
privKey,
.ecdsaSignatureMessageX962SHA256,
digest as CFData,
&error) as Data? else {
print(error)
return
} // bytes varry due to DER encoding and R and S values
let ecdsaSignature = try P256.Signing.ECDSASignature(derRepresentation: signature)
let signatureBytes = ecdsaSignature.rawRepresentation
sendSignatureWithDigest(signatureBytes, digest)
}
}
An important note: digest is not an actual digest but a message that needs to be hashed to turn into a digest? Sorry if that sounds off, my security knowledge is limited.
Please forgive any syntax errors, I can't copy and paste the code and am just extracting the important elements.
Anything helps, thanks!