admin管理员组

文章数量:1124411

I want to generate the same kind of key pair as the result of following openssl command:

openssl ecparam -genkey -name prime256v1 -noout -out ec256-key-pair.pem
openssl ec -in ec256-key-pair.pem -pubout > my-public-ec.pem

When I inspect the result:

openssl pkey -pubin -in my-public-ec.pem -text -noout

I get:

Public-Key: (256 bit)
pub:
    04:fc:7a:1d:5a:2d:8b:c9:ff:47:cc:56:ae:66:37:
    4b:5d:69:d9:d4:9a:10:72:30:e5:2d:2e:9d:86:19:
    30:2a:44:4e:b2:04:09:8f:d3:89:3a:5d:5e:f2:32:
    c7:70:12:bb:55:18:28:c1:a7:2d:d4:1c:e6:9b:12:
    46:4d:98:07:1b
ASN1 OID: prime256v1
NIST CURVE: P-256

Now I want to do the same in Swift:

The code I use to do this is as follows:

func generateKeys() throws -> (privateKey: SecKey, publicKey: SecKey)  {
        let query: [String: Any] = [
            kSecAttrKeyType as String: kSecAttrKeyTypeEC,
            kSecAttrKeySizeInBits as String: 256,
            kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
            kSecAttrIsPermanent as String: false
        ]

        var error: Unmanaged<CFError>?
           
        guard let privateKey = SecKeyCreateRandomKey(query as CFDictionary, &error) else {
            throw error as! any Error
        }
        let publicKey = SecKeyCopyPublicKey(privateKey)!
        return (privateKey, publicKey)

}

This runs without errors.

Next I generate the key pair, and print the public key in PEM format:

extension SecKey {
    func exportBase64EncodedKey() -> String {
        var error: Unmanaged<CFError>?
        guard let data = SecKeyCopyExternalRepresentation(self, &error) else {
            fatalError("Failed to export key: \(error!.takeRetainedValue())")
        }
        return (data as Data).base64EncodedString(options: [.lineLength64Characters])
    }
}


func printPublicKey() {
        let keyPair = try! generateKeys()
        let encodedPublicKey = keyPair.publicKey.exportBase64EncodedKey()
        var header = "-----BEGIN PUBLIC KEY-----"
        var footer = "-----END PUBLIC KEY-----"
        var pemKey = "\(header)\n\(encodedPublicKey)\n\(footer)\n"
        print(pemKey)

}

printPublicKey()

This prints something like:

-----BEGIN PUBLIC KEY-----
BJe2Tq7I1H6gbzcTW0Mq8c8FOhOakEbq6EPQXYSnlDF4IHHyzlERs6YUdcwy8KvT
fuNHfOQ7b3ITi5FFHcCXmps=
-----END PUBLIC KEY-----

If I copy this in a pem file and I run the same command:

openssl pkey -pubin -in new_public_key.pem -text -noout

I get the following error:

Could not find private key of Public Key from new_public_key.pem
404278EC01000000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:102:No supported data to decode.

What am I doing wrong?

Running the above code with the following parameters:

            kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
            kSecAttrKeySizeInBits as String: 1024,

Results in a public key that Openssl can parse without problems.

For easy testing, here code you can copy-paste in a Swift Playground to run it:

import Foundation
import Security
import CryptoKit

func generateKeys() throws -> (privateKey: SecKey, publicKey: SecKey)  {
        let query: [String: Any] = [
            kSecAttrKeyType as String: kSecAttrKeyTypeEC,
            kSecAttrKeySizeInBits as String: 256,
            kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
            kSecAttrIsPermanent as String: false
        ]

        var error: Unmanaged<CFError>?
           
        guard let privateKey = SecKeyCreateRandomKey(query as CFDictionary, &error) else {
            throw error as! any Error
        }
        let publicKey = SecKeyCopyPublicKey(privateKey)!
        return (privateKey, publicKey)

}

extension SecKey {
    func exportBase64EncodedKey() -> String {
        var error: Unmanaged<CFError>?
        guard let data = SecKeyCopyExternalRepresentation(self, &error) else {
            fatalError("Failed to export key: \(error!.takeRetainedValue())")
        }
        return (data as Data).base64EncodedString(options: [.lineLength64Characters])
    }
}


func printPublicKey() {
        let keyPair = try! generateKeys()
        let encodedPublicKey = keyPair.publicKey.exportBase64EncodedKey()
        var header = "-----BEGIN PUBLIC KEY-----"
        var footer = "-----END PUBLIC KEY-----"
        var pemKey = "\(header)\n\(encodedPublicKey)\n\(footer)\n"
        print(pemKey)
}

printPublicKey()

I want to generate the same kind of key pair as the result of following openssl command:

openssl ecparam -genkey -name prime256v1 -noout -out ec256-key-pair.pem
openssl ec -in ec256-key-pair.pem -pubout > my-public-ec.pem

When I inspect the result:

openssl pkey -pubin -in my-public-ec.pem -text -noout

I get:

Public-Key: (256 bit)
pub:
    04:fc:7a:1d:5a:2d:8b:c9:ff:47:cc:56:ae:66:37:
    4b:5d:69:d9:d4:9a:10:72:30:e5:2d:2e:9d:86:19:
    30:2a:44:4e:b2:04:09:8f:d3:89:3a:5d:5e:f2:32:
    c7:70:12:bb:55:18:28:c1:a7:2d:d4:1c:e6:9b:12:
    46:4d:98:07:1b
ASN1 OID: prime256v1
NIST CURVE: P-256

Now I want to do the same in Swift:

The code I use to do this is as follows:

func generateKeys() throws -> (privateKey: SecKey, publicKey: SecKey)  {
        let query: [String: Any] = [
            kSecAttrKeyType as String: kSecAttrKeyTypeEC,
            kSecAttrKeySizeInBits as String: 256,
            kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
            kSecAttrIsPermanent as String: false
        ]

        var error: Unmanaged<CFError>?
           
        guard let privateKey = SecKeyCreateRandomKey(query as CFDictionary, &error) else {
            throw error as! any Error
        }
        let publicKey = SecKeyCopyPublicKey(privateKey)!
        return (privateKey, publicKey)

}

This runs without errors.

Next I generate the key pair, and print the public key in PEM format:

extension SecKey {
    func exportBase64EncodedKey() -> String {
        var error: Unmanaged<CFError>?
        guard let data = SecKeyCopyExternalRepresentation(self, &error) else {
            fatalError("Failed to export key: \(error!.takeRetainedValue())")
        }
        return (data as Data).base64EncodedString(options: [.lineLength64Characters])
    }
}


func printPublicKey() {
        let keyPair = try! generateKeys()
        let encodedPublicKey = keyPair.publicKey.exportBase64EncodedKey()
        var header = "-----BEGIN PUBLIC KEY-----"
        var footer = "-----END PUBLIC KEY-----"
        var pemKey = "\(header)\n\(encodedPublicKey)\n\(footer)\n"
        print(pemKey)

}

printPublicKey()

This prints something like:

-----BEGIN PUBLIC KEY-----
BJe2Tq7I1H6gbzcTW0Mq8c8FOhOakEbq6EPQXYSnlDF4IHHyzlERs6YUdcwy8KvT
fuNHfOQ7b3ITi5FFHcCXmps=
-----END PUBLIC KEY-----

If I copy this in a pem file and I run the same command:

openssl pkey -pubin -in new_public_key.pem -text -noout

I get the following error:

Could not find private key of Public Key from new_public_key.pem
404278EC01000000:error:1E08010C:DECODER routines:OSSL_DECODER_from_bio:unsupported:crypto/encode_decode/decoder_lib.c:102:No supported data to decode.

What am I doing wrong?

Running the above code with the following parameters:

            kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
            kSecAttrKeySizeInBits as String: 1024,

Results in a public key that Openssl can parse without problems.

For easy testing, here code you can copy-paste in a Swift Playground to run it:

import Foundation
import Security
import CryptoKit

func generateKeys() throws -> (privateKey: SecKey, publicKey: SecKey)  {
        let query: [String: Any] = [
            kSecAttrKeyType as String: kSecAttrKeyTypeEC,
            kSecAttrKeySizeInBits as String: 256,
            kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
            kSecAttrIsPermanent as String: false
        ]

        var error: Unmanaged<CFError>?
           
        guard let privateKey = SecKeyCreateRandomKey(query as CFDictionary, &error) else {
            throw error as! any Error
        }
        let publicKey = SecKeyCopyPublicKey(privateKey)!
        return (privateKey, publicKey)

}

extension SecKey {
    func exportBase64EncodedKey() -> String {
        var error: Unmanaged<CFError>?
        guard let data = SecKeyCopyExternalRepresentation(self, &error) else {
            fatalError("Failed to export key: \(error!.takeRetainedValue())")
        }
        return (data as Data).base64EncodedString(options: [.lineLength64Characters])
    }
}


func printPublicKey() {
        let keyPair = try! generateKeys()
        let encodedPublicKey = keyPair.publicKey.exportBase64EncodedKey()
        var header = "-----BEGIN PUBLIC KEY-----"
        var footer = "-----END PUBLIC KEY-----"
        var pemKey = "\(header)\n\(encodedPublicKey)\n\(footer)\n"
        print(pemKey)
}

printPublicKey()
Share Improve this question edited 2 days ago Joris asked 2 days ago JorisJoris 6,2847 gold badges44 silver badges72 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

SecKeyCopyExternalRepresentation() returns the public key for an EC curve in uncompressed format: 0x04|<x>|<y>, where <x> and <y> are the x and y coordinates of the EC point and both of them are 32 bytes in size for P-256.

However, the PEM encoded public key requires the X.509/SPKI format. The X.509/SPKI format contains the uncompressed key at the very end. For keys of the same curve, the front part is constant.1)

So if the front part for a certain curve and an uncompressed key for this curve are concatenated, you get the key in X.509/SPKI format, e.g. for P-256 and your uncompressed key:

var spki = Data(base64Encoded: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgA=")! 
let raw = Data(base64Encoded: "BJe2Tq7I1H6gbzcTW0Mq8c8FOhOakEbq6EPQXYSnlDF4IHHyzlERs6YUdcwy8KvTfuNHfOQ7b3ITi5FFHcCXmps=")! 
spki.append(raw)
let key = spki.base64EncodedString(options: [.lineLength64Characters])
let header = "-----BEGIN PUBLIC KEY-----"
let footer = "-----END PUBLIC KEY-----"
let pemKey = "\(header)\n\(key)\n\(footer)\n"
print(pemKey)

This code gives you the PEM encoded key in X.509/SPKI format, which can be processed with your OpenSSL statement:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEl7ZOrsjUfqBvNxNbQyrxzwU6E5qQ
RuroQ9BdhKeUMXggcfLOURGzphR1zDLwq9N+40d85DtvchOLkUUdwJeamw==
-----END PUBLIC KEY-----

You can inspect this key in an ASN.1/DER parser, e.g. here.

1) As a side note: A public key in X.509/SPKI format can also contain a compressed key: <a>|<x> with <a> is <0x02> for even <y> and <0x03> for odd <y>. In this case, the front part differs (due to the length information it contains). However, this is not important here, as SecKeyCopyExternalRepresentation() returns the uncompressed key.


In this answer you will find an alternative approach that uses CryptoExportImportManager.

本文标签: Problem generating EC PEM keypair on iOS in Swiftusing Security frameworkStack Overflow