Output Seckeycopyexternalrepresentation

Output SecKeyCopyExternalRepresentation

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.

Getting a string representation of a SecKey

The normal way to encode a binary blob, like a certificate or RSA key as a string is to use base64 encoding. You can convert a Data to base64 quite easily with the function base64EncodedString(options:). i.e.

let myString = myData.base64EncodedString()

whether that is exactly what you need for this application is hard to tell because your question doesn't give much context.

Looking at your screen shot, as well as the base64 encoded string, you need a header and footer. Most of the apparently random letters in the data structure are the base64 string (the JSON conversion has encoded the line feeds with \n and something else has doubled up the backslashes). Your last step is therefore to prepend the string with -----BEGIN PUBLIC KEY----- and a new line, ad append a new line and -----END PUBLIC KEY-----

One more thing: you can get the original data back from the base64 string quite easily with Data.init?((base64Encoded base64String:,options:). i.e.

guard let myDataCopy = Data(base64Encoded: myString)
else { fatalError("the string was not really base64") }

Swift 3 export SecKey to String

Export Key (iOS 10 only)

var error:Unmanaged<CFError>?
if let cfdata = SecKeyCopyExternalRepresentation(publicKey!, &error) {
let data:Data = cfdata as Data
let b64Key = data.base64EncodedString()
}

See https://stackoverflow.com/a/30662270/5276890 and https://stackoverflow.com/a/27935528/5276890 for longer ways which probably support iOS < 10.

Reimport Key

guard let data2 = Data.init(base64Encoded: b64Key) else {
return
}

let keyDict:[NSObject:NSObject] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits: NSNumber(value: 512),
kSecReturnPersistentRef: true as NSObject
]

guard let publicKey = SecKeyCreateWithData(data2 as CFData, keyDict as CFDictionary, nil) else {
return
}

Note: This generates a base64 key and not a certificate. A lot of code samples online deal with how to generate a public key from a certificate using SecCertificateCreateWithData

Also: 512 bit is fast to generate but worthless. Pick a longer and secure value once you're satisfied with the results.

I got valid results back when importing the key I generated and exported, so I assume it works, but I did not try to encrypt and decrypt with it.

Convert ECPublicKey(SecKey) to PEM String in swift

Start by converting to ASN.1 format:
(see your decoded example in ASN.1 Decoder)

let publicKeyData: CFData = ...
let ecHeader: [UInt8] = [
/* sequence */ 0x30, 0x59,
/* |-> sequence */ 0x30, 0x13,
/* |---> ecPublicKey */ 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, // (ANSI X9.62 public key type)
/* |---> prime256v1 */ 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, // (ANSI X9.62 named elliptic curve)
/* |-> bit headers */ 0x07, 0x03, 0x42, 0x00
]

var asn1 = Data()
asn1.append(Data(ecHeader))
asn1.append(publicKeyData as Data)

Then Base64-encode and add PEM header & footer:

let encoded = asn1.base64EncodedString(options: .lineLength64Characters)
let pemString = "-----BEGIN PUBLIC KEY-----\r\n\(encoded)\r\n-----END PUBLIC KEY-----\r\n"

Importing ANSI X9.63 formatted key pair in Java

Notice in the Apple documentation how the first 65-bytes are the uncompressed public key (04 || X || Y) concatenated with the private scalar (|| K). Take these bytes off and you can create the private key. I hope this helps somebody.

/*
* 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.
*/
private PrivateKey createECPrivateKey(byte[] rawBytes) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidParameterSpecException {
KeyFactory kf = KeyFactory.getInstance("EC");
BigInteger s = new BigInteger(Arrays.copyOfRange(rawBytes, 65, rawBytes.length));
return kf.generatePrivate(new ECPrivateKeySpec(s, ecParameterSpecForCurve("secp256r1")));
}

OpenSSL RSA public key digest

It turns out OpenSSL has an undocumented parameter -RSAPublicKey_out that outputs the same data that SecKeyCopyExternalRepresentation does.

openssl rsa -pubin -RSAPublicKey_out -outform DER | openssl dgst -sha256

This provides the same digest for both iOS and macOS



Related Topics



Leave a reply



Submit