Search code examples
pythonswiftcryptographypublic-keypublic-key-exchange

Output SecKeyCopyExternalRepresentation


I'm trying to pass around a public key from my iPhone to other parties, however I am unable to use the output from iOS.

let parameters: [String: Any] = [
    kSecAttrKeySizeInBits as String: 384,
    kSecAttrKeyType as String: kSecAttrKeyTypeEC,
    kSecPrivateKeyAttrs as String: [
        kSecAttrIsPermanent as String: false
    ]
]

var error: Unmanaged<CFError>?
let privateKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error)
let publicKey = SecKeyCopyPublicKey(privateKey!)

let pub = SecKeyCopyExternalRepresentation(publicKey!, &error)
let pubData = pub as Data?
print(pubData!.base64EncodedString())

Example output:

BJSCZtBatd2BYEHtyLB0qTZNlphKf3ZTGI6Nke3dSxIDpyP9FWMZbG0zcdIXWENyndskfxV0No/yz369ngL2EHZYw6ggNysOnZ5IQSPOLFFl44m1aAk0o0NdaRXTVAz4jQ==

In python (where my second party is) I have the following:

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization

pub_key = serialisation.load_pem_public_key(
    data=xcode_data.encode(),
    backend=default_backend()
)

The error I get is ValueError: Could not deserialize key data.

So what exactly is the output of the SecKeyCopyExternalRepresentation as described by the documentation:

The method returns data in the PCKS #1 format for an RSA key. For an elliptic curve public key, the format follows the ANSI X9.63 standard using a byte string of 04 || X || Y. For an elliptic curve private key, the output is formatted as the public key concatenated with the big endian encoding of the secret scalar, or 04 || X || Y || K. All of these representations use constant size integers, including leading zeros as needed.

How would one describe the X6.93 format? And how would I go about converting it to something I can use in the python code?

P.S. I have tried to add headers such as -----BEGIN PUBLIC KEY----- to the xcode output.


Solution

  • I havent quite found the answer to the question as I still don't know what exactly the output is that Apple provides, however, I came up with a solution found in this key import export manager.

    let parameters: [String: Any] = [
        kSecAttrKeySizeInBits as String: 384,
        kSecAttrKeyType as String: kSecAttrKeyTypeEC,
        kSecPrivateKeyAttrs as String: [
            kSecAttrIsPermanent as String: false
        ]
    ]
    
    var pubKey: SecKey?
    var priKey: SecKey?
    var error: Unmanaged<CFError>?
    let keyPair = SecKeyGeneratePair(parameters as CFDictionary, &pubKey, &priKey)
    
    let publicKeyData = SecKeyCopyExternalRepresentation(pubKey!, &error)
    // Code from the library
    let ieManager = CryptoExportImportManager()
    if let exportPEM = ieManager.exportPublicKeyToPEM(publicKeyData as Data!, keyType: kSecAttrKeyTypeEC as String, keySize: 384) {
        print(exportPEM)
    } else {
        print("Error exporting to PEM")
    }
    

    Example output:

    Exporting EC raw key: 97 bytes -----BEGIN PUBLIC KEY----- MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEFpCnTrJFQq0mZBvy+vzl9noKLZ4/s1cf I6hygug6s8dvBreMhabAcAbbhSa1losjCxV450nq92W9ZymonYasaAuhshDWjmvx 2qTXHEpVEVb9GawqX6XqpWtIBf+meHKS -----END PUBLIC KEY-----

    Implementation in python using cryptography

    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives import serialization
    
    xcode = '-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEFpCnTrJFQq0mZBvy+vzl9noKLZ4/s1cf\nI6hygug6s8dvBreMhabAcAbbhSa1losjCxV450nq92W9ZymonYasaAuhshDWjmvx2\nqTXHEpVEVb9GawqX6XqpWtIBf+meHKS\n-----END PUBLIC KEY-----'
    pub_key = serialization.load_pem_public_key(xcode.encode(), default_backend())
    xcode
    

    Outputs the following: <cryptography.hazmat.backends.openssl.ec._EllipticCurvePublicKey object at 0x7fb4f6f50e10>

    Note that you do have to add the new lines yourself in python in order for this all to work.

    Update

    The output of the SecKeyCopyExternalRepresentation for ECC keys is the X9.62 or X9.63 format of the key (in uncompressed form). This is very different from DER and therefor PEM encoding.

    The encoding is 04 || X || Y for a public key and 04 || X || Y || K for a private key. 04 is a fixed byte for this format. The X, Y and optionally K value are points or coordinates that define the curve of this key. More info about that over here.