In an iOS application, I receive a PEM-encoded Elliptic curve public key.
I would like to create a SecKey
object from it.
This question has been very useful to get RSA key parsing to work.
But I struggle adapting it to work with an EC key.
var secKeyCreateError : Unmanaged<CFError>?
guard
let stringPublicKey = Data(
base64Encoded: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhT0OXGhPWpbrZBTIScIFQVooi/Qo/NyTYRnrIyZ42nksKCBeSOBu+FPOHCI5U4RUSc2cUOe83dyuKmboU2Kdc1dTq9HDAau3dhpE7VLzZKzMHay+8XW5V6kQJ2oOIGKJphsjJLDM5KxCr5etHEHE5rfrPIBZA0sgcvyT0TsavOAhr55Eu4U2fu8SefxM4CWobXKANiWbmSzzYbo2EIZrfhhe2RncwnH5kr0PMk6Q+kEcuRt58VyYoDAa7vRQvY+KDwxE81CCkIjKpJ55f4uN0/VDclXzFjK8FeOgIiH3n8KD6xqtkvmFc+M8tEJYlzdHWIRN7VoNqbn4IoevnziYhQIDAQAB"
),
let peerPublicKey = SecKeyCreateWithData(
stringPublicKey as CFData,
[
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
] as CFDictionary,
&secKeyCreateError
)
else {
NSLog("Failed to create SecKey : %@", secKeyCreateError!.takeRetainedValue().localizedDescription)
return
}
NSLog("SecKey successfully created")
var secKeyCreateError : Unmanaged<CFError>?
guard
let stringPublicKey = Data(
base64Encoded: "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEhYvCTeKdth6ffyCKReeO7cJSfN94BfieZ/9zkE6sDFz/ZifyMkgeg7mq8XB4UYn7aSEcsnqFNswROLnU4NqVFbmGDi5wAI0jRazdskGFBf+0R/zIPozZgJOSrREMEqi7"
),
let peerPublicKey = SecKeyCreateWithData(
stringPublicKey as CFData,
[
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
] as CFDictionary,
&secKeyCreateError
)
else {
NSLog("Failed to create SecKey : %@", secKeyCreateError!.takeRetainedValue().localizedDescription)
return
}
NSLog("SecKey successfully created")
Execution returns the following logs :
[seckey] SecKeyCreate init(ECPublicKey) failed: -26275
Failed to create SecKey : The operation couldn’t be completed. (OSStatus error -50 - EC public key creation from data failed)
For information, I used https://mkjwk.org/ to generate public keys.
I tried to extract the DER BIT STRING using ASN1Decoder and ASN1Swift without success.
Would you have any idea of what's going on with those EC keys ? Thanks a lot 🙏
As pointed out by CyonAlexRdx here, a SECG key must be in X9.63 format to be imported using Security Framework.
Here, we are in trouble because Key is in PEM format. I identified 2 solutions.
you may import it using CryptoKit, then map it to a Security Framework object.
Detailed explainations from eskimo on developer.apple.com forum
For example, the following routine imports a PEM secp256r1 private key and returns a SecKey object:
func createSecKeyWithPEMSecp256r1Private(_ pem: String) throws -> SecKey {
let privateKeyCK = try P256.Signing.PrivateKey(pemRepresentation: pem)
let x963Data = privateKeyCK.x963Representation
var errorQ: Unmanaged<CFError>? = nil
guard let privateKeySF = SecKeyCreateWithData(x963Data as NSData, [
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
] as NSDictionary, &errorQ) else {
throw errorQ!.takeRetainedValue()
}
return privateKeySF
}
You can use ASN1Decoder to extract key data (in x9.63 format) from DER.
import ASN1Decoder
class DerDecoder {
func decodePublicKey(_ data: Data, _ error: UnsafeMutablePointer<Unmanaged<CFError>?>?) -> SecKey? {
guard
let asn1 = try? ASN1DERDecoder.decode(data: data),
let keyData = asn1.first?.sub(1)?.value as? Data
else {
return nil
}
return SecKeyCreateWithData(
keyData as CFData,
[
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
] as CFDictionary,
error
)
}
}
var secKeyCreateError : Unmanaged<CFError>?
guard
let stringPublicKey = Data(
base64Encoded: "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEhYvCTeKdth6ffyCKReeO7cJSfN94BfieZ/9zkE6sDFz/ZifyMkgeg7mq8XB4UYn7aSEcsnqFNswROLnU4NqVFbmGDi5wAI0jRazdskGFBf+0R/zIPozZgJOSrREMEqi7"
),
let peerPublicKey = DerDecoder().decodePublicKey(
stringPublicKey,
&secKeyCreateError,
)
else {
NSLog("Failed to create SecKey : %@", secKeyCreateError!.takeRetainedValue().localizedDescription)
return
}
NSLog("SecKey successfully created")