SecKeyCreateWithData Returns ‘Nil’ with Error

We are trying to create a ’SecKey’ from a ‘.der’ file. But ‘SecKeyCreateWithData’ always throw ‘Nil ’ with Error.

Steps Followed::

First we created a ECDSA Private & Public key pair with the below Commands , then converted the .pem file holding the private key to ‘.der’file. Finally used the ‘.der’ file to generate a ‘SecKey’ via code.

Commands Used to Generate Private & Public Keys::

openssl ecparam -genkey -name prime256v1 -noout -out ec-key-pair.pem

openssl ec -in ec-key-pair.pem -pubout -out ec-key-pair.pub

Command Used to Generate .Der file::

openssl pkey -outform der -in ec-key-pair.pem -out ec-key-pair.der

Content Inside .pem file::

-----BEGIN EC PRIVATE KEY----- MHcCAQEEIKJTc3zI8D07Myh7ZIR+wGyQgsjEeKdH0+hSiErK5AjzoAoGCCqGSM49 AwEHoUQDQgAEvbOBrM/D2fX05zKQYuJiTRP6YiUBabImrHb9s+OHimxUxX+E9jVe oQ6nxSOkfgm0H1OjLfp2xGLqkDTuF38UGQ== -----END EC PRIVATE KEY-----

Error Received::

Unmanaged - _value : Error Domain=NSOSStatusErrorDomain Code=-50 "EC private key creation from data failed" UserInfo={NSDescription=EC private key creation from data failed}

Minimum Deployment Target Used::

iOS 14.0

Code Used::

if let certificateData = NSData(contentsOf:Bundle.main.url(forResource: "ec-key-pair", withExtension: "der")! ) {
      var error: Unmanaged<CFError>? = nil
      let privateSecKey = SecKeyCreateWithData(certificateData , [
          kSecAttrKeyType: kSecAttrKeyTypeEC,
          kSecAttrKeyClass: kSecAttrKeyClassPrivate] as NSDictionary, &error)
}

Replies

Right, so I believe the issue here is the PEM wrapper. The reason I say that is because when I try this on macOS using .pemArmour, then I am able to create the SecKey:

let privateKey = """
-----BEGIN PRIVATE KEY-----
MHcCAQEEIKJTc3zI8D07Myh7ZIR+wGyQgsjEeKdH0+hSiErK5AjzoAoGCCqGSM49
AwEHoUQDQgAEvbOBrM/D2fX05zKQYuJiTRP6YiUBabImrHb9s+OHimxUxX+E9jVe
oQ6nxSOkfgm0H1OjLfp2xGLqkDTuF38UGQ==
-----END PRIVATE KEY-----
"""
let privateKeyData = privateKey.data(using: .utf8)!
let privateKeyBytes = [UInt8](privateKeyData)
let cfKeyData = CFDataCreate(kCFAllocatorDefault, privateKeyBytes, privateKeyData.count)!
var importedItems: CFArray?
let status = SecItemImport(cfKeyData, "pem" as CFString, nil, nil, .pemArmour, nil, nil, &importedItems)

guard status == errSecSuccess,
      let keys = importedItems as? [SecKey],
      let secPrivateKey = keys.first else {
    fatalError("Private key could not be created")
}
print("Private key: \(secPrivateKey) keys count \(keys.count)")

NOTE I did not try to encrypt or decrypt here with secPrivateKey, but I just wanted to validate that I could get the key.

Now, when going through SecKeyCreateWithData with the PEM wrapped bytes I get a nil key:

guard let filepath = Bundle.main.path(forResource: "key", ofType: "pem") else {
	fatalError("File could not be found")
}
let url = URL(fileURLWithPath: filepath)
var privateKeyPemData = Data()
do {
	privateKeyPemData = try Data.init(contentsOf: url)
} catch {
	print("File could not be opened: \(error.localizedDescription)")
}
let privateKeyPemBytes = [UInt8](privateKeyPemData)
print("privateKeyPemBytes.count: \(privateKeyPemBytes.count)")
//let cfKeyBytes = CFDataCreate(kCFAllocatorDefault, privateKeyPemBytes, privateKeyPemBytes.count)!
var error: Unmanaged<CFError>? = nil
let privateSecKey = SecKeyCreateWithData(Data(privateKeyPemBytes) as CFData, [
										 kSecAttrKeyType: kSecAttrKeyTypeEC,
										 kSecAttrKeyClass: kSecAttrKeyClassPrivate] as NSDictionary, &error)

Now, when base64 decoding the PEM wrapper around your key into a ASN.1 structure I see:

$ dumpasn1 AuthKey.bin
   0 119: SEQUENCE {
   2   1:   INTEGER 1
   5  32:   OCTET STRING
		:     A2 53 73 7C C8 F0 3D 3B 33 28 7B 64 84 7E C0 6C
		:     90 82 C8 C4 78 A7 47 D3 E8 52 88 4A CA E4 08 F3
  39  10:   [0] {
  41   8:     OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
		:     }
  51  68:   [1] {
  53  66:     BIT STRING
		:       04 BD B3 81 AC CF C3 D9 F5 F4 E7 32 90 62 E2 62
		:       4D 13 FA 62 25 01 69 B2 26 AC 76 FD B3 E3 87 8A
		:       6C 54 C5 7F 84 F6 35 5E A1 0E A7 C5 23 A4 7E 09
		:       B4 1F 53 A3 2D FA 76 C4 62 EA 90 34 EE 17 7F 14
		:       19
		:     }
		:   }

And then taking those raw bytes and running them through SecKeyCreateWithData now a SecKey can be created:

let privateKeyRawBytes = [UInt8]([
	0x04, 0xBD, 0xB3, 0x81, 0xAC, 0xCF, 0xC3, 0xD9, 0xF5, 0xF4, 0xE7, 0x32, 0x90, 0x62, 0xE2, 0x62,
	0x4D, 0x13, 0xFA, 0x62, 0x25, 0x01, 0x69, 0xB2, 0x26, 0xAC, 0x76, 0xFD, 0xB3, 0xE3, 0x87, 0x8A,
	0x6C, 0x54, 0xC5, 0x7F, 0x84, 0xF6, 0x35, 0x5E, 0xA1, 0x0E, 0xA7, 0xC5, 0x23, 0xA4, 0x7E, 0x09,
	0xB4, 0x1F, 0x53, 0xA3, 0x2D, 0xFA, 0x76, 0xC4, 0x62, 0xEA, 0x90, 0x34, 0xEE, 0x17, 0x7F, 0x14,
	0x19, 0xA2, 0x53, 0x73, 0x7C, 0xC8, 0xF0, 0x3D, 0x3B, 0x33, 0x28, 0x7B, 0x64, 0x84, 0x7E, 0xC0, 0x6C,
	0x90, 0x82, 0xC8, 0xC4, 0x78, 0xA7, 0x47, 0xD3, 0xE8, 0x52, 0x88, 0x4A, 0xCA, 0xE4, 0x08, 0xF3,
])

var error: Unmanaged<CFError>? = nil
let privateSecKey = SecKeyCreateWithData(Data(privateKeyRawBytes) as CFData, [
										 kSecAttrKeyType: kSecAttrKeyTypeEC,
										 kSecAttrKeyClass: kSecAttrKeyClassPrivate] as NSDictionary, &error)
print("Private sec key: \(privateSecKey)")
*/
// Private sec key: Optional(<SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPrivateKey, version: 4, block size: 256 bits, addr: 0x100415920>)

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

Thanks Matt for you reply. Will try out your suggestion.