Using SecKeyCreateWithData to Load data from openssl ecparam generated keys

I've been reading previous threads such as this about how to import openssl generated keys using SecKeyCreateWithData but always get the error Code=-50 "EC private key creation from data failed". I understand that SecKeyCreateWithData doesn't allow the standard headers that openssl generates, but even after converting the bit string section to a binary file, it refuses to load.


This is the command I use to generate the key:


openssl ecparam -name prime256v1 -genkey -noout -out openssl.key -outform der


Using dumpasn1:

$ ~/Desktop/dumpasn1 -w76 openssl.key
  0 119: SEQUENCE {
  2   1:   INTEGER 1
  5  32:   OCTET STRING
       :     67 BF 75 38 C7 14 38 88 4C DD BC 91 A5 5C 88 10
       :     DE 0C 6F EE A7 2B 85 75 19 EC 71 1D 91 BD FD E3
39  10:   [0] {
41   8:     OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
       :     }
51  68:   [1] {
53  66:     BIT STRING
       :       04 5A C4 BB DE 43 15 25 9E E2 A3 CD D8 80 0E 12
       :       57 24 0D 68 BD 22 61 57 D9 87 F8 E2 16 DD 8C 02
       :       AD 73 4F D0 69 6F F0 61 0D FD FB 4D EB F1 45 C0
       :       AB D4 46 82 B6 DB 69 62 8F C9 7C C9 07 09 0C 91
       :       91
       :     }
       :   }


If I trim off the headers:


$ dd if=openssl.key of=stripped.openssl.key skip=56 bs=1
65+0 records in
65+0 records out
65 bytes transferred in 0.000320 secs (203001 bytes/sec)

$ hexdump -Cv stripped.openssl.key
00000000  04 5a c4 bb de 43 15 25  9e e2 a3 cd d8 80 0e 12  |.Z...C.%........|
00000010  57 24 0d 68 bd 22 61 57  d9 87 f8 e2 16 dd 8c 02  |W$.h."aW........|
00000020  ad 73 4f d0 69 6f f0 61  0d fd fb 4d eb f1 45 c0  |.sO.io.a...M..E.|
00000030  ab d4 46 82 b6 db 69 62  8f c9 7c c9 07 09 0c 91  |..F...ib..|.....|
00000040  91                                                |.|
00000041


It's 65 bytes beginning with 04, which I believe it just the keydata. This refuses to load on iOS with the error:


var error: Unmanaged?
        guard let key = SecKeyCreateWithData(privateKey as CFData,
                                             [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
                                              kSecAttrKeyClass: kSecAttrKeyClassPrivate,
                                              kSecAttrKeySizeInBits: 256] as CFDictionary,
                                             &error)



key length: 65
failed - Failed to create key: Unmanaged(_value: Error Domain=NSOSStatusErrorDomain Code=-50 "EC private key creation from data failed" UserInfo={NSDescription=EC private key creation from data failed})
However, it will load as a public key which I don't understand.


I tried creating a key in iOS then saving it as data and it generates a larger size (97 vs 65) but it will succesfully load.


$ hexdump -Cv ios.private.key
00000000  04 6b 17 c3 46 32 db 10  02 9a 2b 02 de 53 89 c3  |.k..F2....+..S..|
00000010  87 71 d3 bf b6 1a 64 c0  0e e1 35 6e 1c 9f af 5f  |.q....d...5n..._|
00000020  70 d2 05 ba fc 4e fb 5a  e2 93 6a 68 12 b1 18 a8  |p....N.Z..jh....|
00000030  c3 f1 2a db aa 77 a1 e0  57 bc d9 23 6c a4 82 c7  |..*..w..W..#l...|
00000040  75 a9 d0 e0 01 76 6c de  8d f0 22 64 a0 4e 06 bf  |u....vl..."d.N..|
00000050  c3 0e b1 4d 45 3e fc 9f  f1 a4 4b e3 85 e0 0f 07  |...ME>....K.....|
00000060  bd                                                |.|
00000061


I just want to understand what these extra bytes are and how to get a openssl-generated key to load in iOS.


Thanks

Accepted Reply

In

<Security.framework/SecKey.h>
you’ll see this comment:
The requested data format depend on the type of key (kSecAttrKeyType) being created:
 * kSecAttrKeyTypeRSA               PKCS#1 format, public key can be also in x509 public key format
 * kSecAttrKeyTypeECSECPrimeRandom  ANSI X9.63 format (04 || X || Y [ || K])

Look at the last line, which describes the EC key format. The

K
item is in square brackets, indicating it’s optional. This is the difference between a public key (without
K
) and a private key (with
K
).

I don’t know what spec the OpenSSL key conforms to — I don’t have time tonight to look up the details — but it seems that it stores this

K
value in first
OCTET STRING
. That is, if you take the 67 BF 75 38 … 91 BD FD E3 bytes and append it to 04 5A C4 BB … 09 0C 91 91 bytes, you end up with 97 bytes that import correctly.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

In

<Security.framework/SecKey.h>
you’ll see this comment:
The requested data format depend on the type of key (kSecAttrKeyType) being created:
 * kSecAttrKeyTypeRSA               PKCS#1 format, public key can be also in x509 public key format
 * kSecAttrKeyTypeECSECPrimeRandom  ANSI X9.63 format (04 || X || Y [ || K])

Look at the last line, which describes the EC key format. The

K
item is in square brackets, indicating it’s optional. This is the difference between a public key (without
K
) and a private key (with
K
).

I don’t know what spec the OpenSSL key conforms to — I don’t have time tonight to look up the details — but it seems that it stores this

K
value in first
OCTET STRING
. That is, if you take the 67 BF 75 38 … 91 BD FD E3 bytes and append it to 04 5A C4 BB … 09 0C 91 91 bytes, you end up with 97 bytes that import correctly.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Wow that works! thanks


I wrote a simple ruby script to convert it if anyone needs it: https://gist.github.com/niveus/f9ed2010dadeaf1df72a2d81d1317722