2

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.

Example working with an RSA 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")

Example failing with an EC key

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.

What else did I try

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

burnsi
  • 6,194
  • 13
  • 17
  • 27
Chralu
  • 43
  • 4

1 Answers1

2

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.

Your project uses Swift Package Manager

you may import it using CryptoKit, then map it to a Security Framework object.

Detailed explainations from eskimo on developer.apple.com forum

  1. Import the PEM key using Apple CryptoKit.
  2. Get the X9.63 representation.
  3. Create the Security framework key from that.

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
}

Your project uses cocoapods

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")
Chralu
  • 43
  • 4