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
Running Windows Shell Commands with Python
Variable Assignment and Modification (In Python)
Opencv Videocapture and Error: (-215:Assertion Failed) !_Src.Empty() in Function 'Cv::Cvtcolor'
SQL Join or R's Merge() Function in Numpy
Does Python Have a Module to Convert CSS Styles to Inline Styles for Emails
How Is the Feature Score(/Importance) in the Xgboost Package Calculated
Data Scraping from Published Power Bi Visual
Differencebetween Ruby and Python Versions Of"Self"
Learning Ruby from Python; Differences and Similarities
Why Can't Python Find Shared Objects That Are in Directories in Sys.Path
How Does Python Manage Int and Long
What's the Best Way to Return Multiple Values from a Function
Python Giving Filenotfounderror for File Name Returned by Os.Listdir
How to Install Python Opencv Through Conda
How to Make the Python Interpreter Correctly Handle Non-Ascii Characters in String Operations
How to Return 0 with Divide by Zero
Failed to Install Python Cryptography Package with Pip and Setup.Py