MacOS: EC public key creation from data failed

let pubString = "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECDDYmcIpUxC2EVJu8o/qPHPoZMId11A/2nVAXJByFOYSigUnZ6njK4qZQMGxLr3xNND17yYoXZ21QKus4bUHfg=="
let pubData = NSData(base64Encoded: pubString)

let pubRaw = pubData?.bytes.assumingMemoryBound(to: UInt8.self)

let CFPubData = CFDataCreate(nil, pubRaw!, pubData!.length)

let options: [String: Any] = [kSecAttrKeyType as String: kSecAttrKeyTypeEC,
                              kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
                              kSecAttrKeySizeInBits as String: 256]

var error: Unmanaged<CFError>?

guard let privKey = SecKeyCreateWithData(CFPubData!, options as CFDictionary, &error) else {
    throw error!.takeRetainedValue() as Error
}


I'm getting Code=-50 "EC public key creation from data failed" (paramErr: error in user parameter list)


The key is a public EC key generated (PEM Format, Headers stripped) with the following command:


openssl ecparam -genkey -name secp256k1 -noout -out ngthnd_priv.pem

Replies

First off you already have base64 pubString, which you're encoding to base64 again, you will want to decode the base 64 to get the original value then create a data object to represent that string before passing it to the SecKeyCreateWithData ...



https://forums.developer.apple.com/thread/72445

The problem here is that the data you’re passing in isn’t sufficiently ‘raw’. I decoded your Base64 and put the bytes into a file (

tmp.asn1
). Dumping that file with dumpasn1 it’s clearly ASN.1 DER data:
$ dumpasn1 -p tmp.asn1 
SEQUENCE {
  SEQUENCE {
    OBJECT IDENTIFIER '1 2 840 10045 2 1'
    OBJECT IDENTIFIER secp256k1 (1 3 132 0 10)
    }
  BIT STRING
    04 08 30 D8 99 C2 29 53 10 B6 11 52 6E F2 8F EA
    3C 73 E8 64 C2 1D D7 50 3F DA 75 40 5C 90 72 14
    E6 12 8A 05 27 67 A9 E3 2B 8A 99 40 C1 B1 2E BD
    F1 34 D0 F5 EF 26 28 5D 9D B5 40 AB AC E1 B5 07
    7E
  }

What you need to pass to

SecKeyCreateWithData
is the contents of the
BIT STRING
element. I extracted that:
$ hexdump -Cv tmp.dat
00000000  04 08 30 d8 99 c2 29 53  10 b6 11 52 6e f2 8f ea  |..0...)S...Rn...|
00000010  3c 73 e8 64 c2 1d d7 50  3f da 75 40 5c 90 72 14  |<s.d...P?.u@\.r.|
00000020  e6 12 8a 05 27 67 a9 e3  2b 8a 99 40 c1 b1 2e bd  |....'g..+..@....|
00000030  f1 34 d0 f5 ef 26 28 5d  9d b5 40 ab ac e1 b5 07  |.4...&(]..@.....|
00000040  7e                                                |~|

converted it to Base64:

$ base64 -b 76 tmp.dat
BAgw2JnCKVMQthFSbvKP6jxz6GTCHddQP9p1QFyQchTmEooFJ2ep4yuKmUDBsS698TTQ9e8mKF2d
tUCrrOG1B34=
$

and then rolled it into a simple test project:

let key = """
    BAgw2JnCKVMQthFSbvKP6jxz6GTCHddQP9p1QFyQchTmEooFJ2ep4yuKmUDBsS698TTQ9e8mKF2d\
    tUCrrOG1B34=
    """

func test() {
    let keyData = Data(base64Encoded: self.key)!
    var error: Unmanaged<CFError>? = nil
    let key = SecKeyCreateWithData(keyData as NSData, [
        kSecAttrKeyType: kSecAttrKeyTypeEC,
        kSecAttrKeyClass: kSecAttrKeyClassPublic
    ] as NSDictionary, &error)
    NSLog("%@", "\(key)")
}

This successfully creates a

SecKey
.

Finally, the code you posted is working way harder than it needs to. In Swift you typically don’t use the

NSData
class but the Swift
Data
value. You can convert that to
NSData
using
as NSData
, and
NSData
is then compatible with the
CFData
required by
SecKeyCreateWithData
.

Share and Enjoy

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

let myEmail = "eskimo" + "1" + "@apple.com"
let pubString = """
BAlrIluxyea/xayqVZtMzsNZtbBxdw0GEsUujfneVp85xDbcfmUZK2seopYOFbvImrej+dHK0ZUbdgyVlADrhsE=
"""

let keyData = Data(base64Encoded: pubString)

var error: Unmanaged<CFError>? = nil

let key = SecKeyCreateWithData(keyData! as NSData, [
    kSecAttrKeyType: kSecAttrKeyTypeEC,
    kSecAttrKeyClass: kSecAttrKeyClassPublic
    ] as NSDictionary, &error)

NSLog("%@", "\(key)")

var test: String = "Testing String"

let tdat = test.data(using: .utf8)

var tsig: String = "MEUCIFxeafOypiP34gPldM0owqBZXVIDx/qZJm2l5DclV4aNAiEA+ntwG9kOqs3p5cU4RITlSVR5VvrVM76VE1BvnJb+rps="
let sDat = NSData(base64Encoded: tsig)

guard SecKeyVerifySignature(key!, SecKeyAlgorithm.ecdsaSignatureMessageX962SHA256, tdat! as NSData, sDat! as NSData, &error) else {
    throw error!.takeRetainedValue() as Error
}


This helped. but I have another question. If I use the following PHP code:

$kString = "-----BEGIN EC PRIVATE KEY-----

MHQCAQEEIB/i6S16PGkX6D6rVm5BlQ7TCxrKXy8Do3ve6mVxtUrtoAcGBSuBBAAK
oUQDQgAECWsiW7HJ5r/FrKpVm0zOw1m1sHF3DQYSxS6N+d5WnznENtx+ZRkrax6i
lg4Vu8iat6P50crRlRt2DJWUAOuGwQ==
-----END EC PRIVATE KEY-----";




$key = openssl_pkey_get_private($kString);




$stringToSign = "Testing String";
openssl_sign($stringToSign, $sig, $key, "sha256");

which SecKeyAlgorithm could I then use to verify the signature? because I'm getting "EC signature verification failed (ccerr -1)" with algorithm ecdsaSignatureMessageX962SHA256


With the PHP code, I was able to base64 encode the resulting signature ($sig) and it came out like this:

MEUCIFxeafOypiP34gPldM0owqBZXVIDx/qZJm2l5DclV4aNAiEA+ntwG9kOqs3p5cU4RITlSVR5VvrVM76VE1BvnJb+rps=

I don’t have a ready answer for you here. My general approach for dealing with problems like this is to import the private key on iOS, creating a test signature there, then try verifying that on the other platform. Some other hints:

  • I recommend that you pull PHP out of the equation (PHP crypto is super weird in my experience) and instead do the signing and verification using the

    openssl
    command line tool.
  • You should take a look at RFC 6979, which has test vectors for this stuff.

Share and Enjoy

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

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

So if I try to pass the private key into SecKeyCreateWithData, do I need to pass the OCTET STRING from dumpasn1 or both it and the BIT STRING or is there some special way I need to go about this?

So if I try to pass the private key into

SecKeyCreateWithData
, do I need to pass the
OCTET STRING
from dumpasn1 or both it and the
BIT STRING

I’m missing some context. The only

dumpasn1
example on this thread is mine, from 22 Sep, and it only includes a
BIT STRING
. Which
dumpasn1
output are you referring to?

Share and Enjoy

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

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

An example of a key with Bit and Octet strings:

-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgU208KCg/doqiSzsV
F5sknVtYSgt8/3oiYGbvryIRrzSgCgYIKoZIzj0DAQehRANCAAQfrvDWizEnWAzB
2Hx2r/NyvIBO6KGBDL7wkZoKnz4Sm4+1P1dhD9fVEhbsdoq9RKEf8dvzTOZMaC/i
LqZFKSN6
-----END PRIVATE KEY-----


Dumpasn1 output:

SEQUENCE (3 elem)
    INTEGER 0
    SEQUENCE (2 elem)
        OBJECT IDENTIFIER 1.2.840.10045.2.1 ecPublicKey (ANSI X9.62 public key type)
        OBJECT IDENTIFIER 1.2.840.10045.3.1.7 prime256v1 (ANSI X9.62 named elliptic curve)
    OCTET STRING (1 elem)
        SEQUENCE (4 elem)
            INTEGER 1
            OCTET STRING (32 byte) 536D3C28283F768AA24B3B15179B249D5B584A0B7CFF7A226066EFAF2211AF34
            [0] (1 elem)
                 OBJECT IDENTIFIER 1.2.840.10045.3.1.7 prime256v1 (ANSI X9.62 named elliptic curve)
            [1] (1 elem)
                 BIT STRING (520 bit) 0000010000011111101011101111000011010110100010110011000100100111010110...

// Octet string from ASN1 dump:
0420536D3C28283F768AA24B3B15179B249D5B584A0B7CFF7A226066EFAF2211AF34
"MzA3NzAyMDEwMTA0MjA1MzZEM0MyODI4M0Y3NjyMjYwNjZFRkFGMjIxMUFGMzQ="
// Bit string from ASN1 dump:
034200041FAEF0D68B3127580CC1D87C76AFF372BC804EE8A1810CBEF0919A0A9F3E129B8FB53F57610FD7D51216EC768ABD44A11FF1DBF34CE64C682FE22EA64529237A
"MDM0MjAwMDQxRkFFRjBENjhCMzEyNzU4MENDMUQ4N0M3NkFGRjM3MkJDODA0RUU4QTE4MTBDQkVGMDkxOUEwQTlGM0UxMjlCOEZCNTNGNTc2MTBGRDdENTEyMTZFQzc2OEFCRDQ0QTExRkYxREJGMzRDRTY0QzY4MkZFMjJFQTY0NTI5MjM3QQ=="


OpenSSL parse result:

$ openssl asn1parse -in private.key
    0:d=0  hl=3 l= 147 cons: SEQUENCE  
    3:d=1  hl=2 l=   1 prim: INTEGER           :00
    6:d=1  hl=2 l=  19 cons: SEQUENCE  
    8:d=2  hl=2 l=   7 prim: OBJECT            :id-ecPublicKey
   17:d=2  hl=2 l=   8 prim: OBJECT            :prime256v1
   27:d=1  hl=2 l= 121 prim: OCTET STRING      [HEX DUMP]:30770201010420536D3C28283F768AA24B3B15179B249D5B584A0B7CFF7A226066EFAF2211AF34A00A06082A8648CE3D030107A144034200041FAEF0D68B3127580CC1D87C76AFF372BC804EE8A1810CBEF0919A0A9F3E129B8FB53F57610FD7D51216EC768ABD44A11FF1DBF34CE64C682FE22EA64529237A

// Octet string from OpenSSL parse:
30770201010420536D3C28283F768AA24B3B15179B249D5B584A0B7CFF7A226066EFAF2211AF34A00A06082A8648CE3D030107A144034200041FAEF0D68B3127580CC1D87C76AFF372BC804EE8A1810CBEF0919A0A9F3E129B8FB53F57610FD7D51216EC768ABD44A11FF1DBF34CE64C682FE22EA64529237A
"MzA3NzAyMDEwMTA0MjA1MzZEM0MyODI4M0Y3NjhBQTI0QjNCMTUxNzlCMjQ5RDVCNTg0QTBCN0NGRjdBMjI2MDY2RUZBRjIyMTFBRjM0QTAwQTA2MDgyQTg2NDhDRTNEMDMwMTA3QTE0NDAzNDIwMDA0MUZBRUYwRDY4QjMxMjc1ODBDQzFEODdDNzZBRkYzNzJCQzgwNEVFOEExODEwQ0JFRjA5MTlBMEE5RjNFMTI5QjhGQjUzRjU3NjEwRkQ3RDUxMjE2RUM3NjhBQkQ0NEExMUZGMURCRjM0Q0U2NEM2ODJGRTIyRUE2NDUyOTIzN0E="


Test method for generating the key:

func test(b64: String) {
    guard let data = Data(base64Encoded: b64, options: [.ignoreUnknownCharacters]) else {fatalError("Unknown Format")}
    var error: Unmanaged<CFError>? = nil
    let params = [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClass: kSecAttrKeyClassPrivate] as CFDictionary
    if let key = SecKeyCreateWithData(data as CFData, params, &error) {
        print("Key", key)
    } else if let e = error {
        print("Error", e)
    }  else {
        print("Error")
    }
}


I've tried your suggestions in the previous posts as well as tried to combine the Octet and the Bit strings with no success yet. `SecKeyCreateWithData` continues to return an `Error -50` in all scenarios. Please let me know if I'm using the right API to handle such keys? Thanks in advance.

Reading your post it was unclear as to exactly which data you’re passing to

SecKeyCreateWithData
. I think you’re passing the data from line 11 of your third listing to the
b64
parameter of the
test(b64:)
method. Is that right?

If so, you definitely have an extra level of ASN.1 to deal with. Consider this:

$ xxd -p tmp.dat 
30770201010420536d3c28283f768aa24b3b15179b249d5b584a0b7cff7a
226066efaf2211af34a00a06082a8648ce3d030107a144034200041faef0
d68b3127580cc1d87c76aff372bc804ee8a1810cbef0919a0a9f3e129b8f
b53f57610fd7d51216ec768abd44a11ff1dbf34ce64c682fe22ea6452923
7a
$ dumpasn1 -p tmp.dat
SEQUENCE {
  INTEGER 1
  OCTET STRING
    53 6D 3C 28 28 3F 76 8A A2 4B 3B 15 17 9B 24 9D
    5B 58 4A 0B 7C FF 7A 22 60 66 EF AF 22 11 AF 34
  [0] {
    OBJECT IDENTIFIER ansiX9p256r1 (1 2 840 10045 3 1 7)
    }
  [1] {
    BIT STRING
      04 1F AE F0 D6 8B 31 27 58 0C C1 D8 7C 76 AF F3
      72 BC 80 4E E8 A1 81 0C BE F0 91 9A 0A 9F 3E 12
      9B 8F B5 3F 57 61 0F D7 D5 12 16 EC 76 8A BD 44
      A1 1F F1 DB F3 4C E6 4C 68 2F E2 2E A6 45 29 23
      7A
    }
  }

In situations like this I recommend creating a small test project with hard-wired data and then posting that. That’ll ensure we’re talking about the same thing.

Share and Enjoy

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

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

You're right. I'm passing the base 64 string as a value to the argument `b64`. I've tried with the values in L11, L17 and L20. Here is a test project:

func test(b64: String) {
  guard let data = Data(base64Encoded: b64, options: [.ignoreUnknownCharacters]) else {fatalError("Unknown Format")}
  var error: Unmanaged<CFError>? = nil
  let params = [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClass: kSecAttrKeyClassPrivate] as CFDictionary
  if let key = SecKeyCreateWithData(data as CFData, params, &error) {
      print("Key", key)
  } else if let e = error {
      print("Error", e)
  }  else {
      print("Error")
  }
}

// Base 64 strings as found in L11, L17 and L20 in my previous post
let base64FromOctetStringInOpenSslParse = "MzA3NzAyMDEwMTA0MjA1MzZEM0MyODI4M0Y3NjhBQTI0QjNCMTUxNzlCMjQ5RDVCNTg0QTBCN0NGRjdBMjI2MDY2RUZBRjIyMTFBRjM0QTAwQTA2MDgyQTg2NDhDRTNEMDMwMTA3QTE0NDAzNDIwMDA0MUZBRUYwRDY4QjMxMjc1ODBDQzFEODdDNzZBRkYzNzJCQzgwNEVFOEExODEwQ0JFRjA5MTlBMEE5RjNFMTI5QjhGQjUzRjU3NjEwRkQ3RDUxMjE2RUM3NjhBQkQ0NEExMUZGMURCRjM0Q0U2NEM2ODJGRTIyRUE2NDUyOTIzN0E="
let base64FromBitStringInAsn1Dump = "MDM0MjAwMDQxRkFFRjBENjhCMzEyNzU4MENDMUQ4N0M3NkFGRjM3MkJDODA0RUU4QTE4MTBDQkVGMDkxOUEwQTlGM0UxMjlCOEZCNTNGNTc2MTBGRDdENTEyMTZFQzc2OEFCRDQ0QTExRkYxREJGMzRDRTY0QzY4MkZFMjJFQTY0NTI5MjM3QQ=="
let base64FromOctetStringInAsn1Dump = "MDQyMDUzNkQzQzI4MjgzRjc2OEFBMjRCM0IxNTE3OUIyNDlENUI1ODRBMEI3Q0ZGN0EyMjYwNjZFRkFGMjIxMUFGMzQ="
let base64FromMergedOctetAndBitStringsInAsn1Dump = "MDQyMDUzNkQzQzI4MjgzRjc2OEFBMjRCM0IxNTE3OUIyNDlENUI1ODRBMEI3Q0ZGN0EyMjYwNjZFRkFGMjIxMUFGMzQwMzQyMDAwNDFGQUVGMEQ2OEIzMTI3NTgwQ0MxRDg3Qzc2QUZGMzcyQkM4MDRFRThBMTgxMENCRUYwOTE5QTBBOUYzRTEyOUI4RkI1M0Y1NzYxMEZEN0Q1MTIxNkVDNzY4QUJENDRBMTFGRjFEQkYzNENFNjRDNjgyRkUyMkVBNjQ1MjkyMzdB"

// Test the base 64 strings
test(b64: base64FromOctetStringInOpenSslParse)
test(b64: base64FromBitStringInAsn1Dump)
test(b64: base64FromOctetStringInAsn1Dump)
test(b64: base64FromMergedOctetAndBitStringsInAsn1Dump)


Error message:

Error Unmanaged<CFError>(_value: Error Domain=NSOSStatusErrorDomain Code=-50 "EC public key creation from data failed" (paramErr: error in user parameter list) UserInfo={NSDescription=EC public key creation from data failed})

Thanks for the focused test example. The problem here is that your input data is for a public key and you’re trying to import it as a private key.

Consider the bit string from the ASN.1 dump from my previous post:

$ xxd -p tmp.dat
041faef0d68b3127580cc1d87c76aff372bc804ee8a1810cbef0919a0a9f
3e129b8fb53f57610fd7d51216ec768abd44a11ff1dbf34ce64c682fe22e
a64529237a

If you convert it to Base64 you get this:

$ base64 tmp.dat 
BB+u8NaLMSdYDMHYfHav83K8gE7ooYEMvvCRmgqfPhKbj7U/V2EP19USFux2ir1EoR/x2/NM5kxoL+IupkUpI3o=

I plugged that into your test snippet and change it to import a public key (

kSecAttrKeyClassPublic
), like so:
import Foundation

func test(b64: String) {
  guard let data = Data(base64Encoded: b64, options: [.ignoreUnknownCharacters]) else {fatalError("Unknown Format")}
  var error: Unmanaged<CFError>? = nil
  let params = [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClass: kSecAttrKeyClassPublic] as CFDictionary
  if let key = SecKeyCreateWithData(data as CFData, params, &error) {
      print("Key", key)
  } else if let e = error {
      print("Error", e)
  }  else {
      print("Error")
  }
}

test(b64: "BB+u8NaLMSdYDMHYfHav83K8gE7ooYEMvvCRmgqfPhKbj7U/V2EP19USFux2ir1EoR/x2/NM5kxoL+IupkUpI3o=")

It successfully imports the key, printing:

Key <SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPublicKey, version: 4, block size: 256 bits, y: 8FB53F57610FD7D51216EC768ABD44A11FF1DBF34CE64C682FE22EA64529237A, x: 1FAEF0D68B3127580CC1D87C76AFF372BC804EE8A1810CBEF0919A0A9F3E129B, addr: 0x101b5d4f0>

To further explore this I wrote a tiny test project to generate a new, random EC p256r1 key pair and then dumped the private and public key bytes:

let privateKey = SecKeyCreateRandomKey([
    kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
    kSecAttrKeySizeInBits: 256
] as NSDictionary, nil)!

let publicKey = SecKeyCopyPublicKey(privateKey)!

let privateData = SecKeyCopyExternalRepresentation(privateKey, nil)! as Data
let publicData = SecKeyCopyExternalRepresentation(publicKey, nil)! as Data
print("private: ", privateData as NSData)
print("public: ", publicData as NSData)

Here’s an example of its output:

private:  <04a21f94 07831a37 d9ba294c 2b9c16db 599496de 8a44e909 fd8b6fba f947ed51 c775152f 806af756 9409f18e 87261847 54d19e9a 63ccfd49 af2f7c1f 642da582 84c682d0 e2ac6c42 dcac9417 b31cdccc 2962926d fe088ff3 1dfc3c60 e19c02fb b9>
public:  <04a21f94 07831a37 d9ba294c 2b9c16db 599496de 8a44e909 fd8b6fba f947ed51 c775152f 806af756 9409f18e 87261847 54d19e9a 63ccfd49 af2f7c1f 642da582 84>

Note how the public key is 65 bytes long, a length that matches the key in your example, but the private key is longer (97 bytes).

I’m not sure why your PEM is labelled as a private key; you’ll need to have a chat with whoever generated it.

Share and Enjoy

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

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

Thanks Quinn. I finally got this working with secp384r1 public keys but I hit pitfalls along the way so I've got tips for other folks.


SecKeyCreateWithData does work with different EC curves. It doesn't use the OID (which we strip out) but instead detects the curve from the key's parameters.


SecKeyCreateWithData appears to completely ignore the kSecAttrKeySizeInBits attribute.


If SecKeyCreateWithData fails, start by looking at the public key using dumpasn1. (get yourself a copy with brew: https://github.com/philpennock/homebrew-protocols ) Make sure you are using a public key not a private key.

After you extract the bit string, compare the output of hexdump with dumpasn1 to ensure you have the correct bytes.

FWIW, on all of the keys I looked, the important bytes started at offset 23 (hard coded as skip=23 in my script below)


Here's a script covering all the steps to generate a key pair, extract the public, extract the bit string, and base64 encode it. If you already have a public key, skip the first two steps.


#!/bin/sh
# generate secp384r1 keypair, export public
openssl ecparam -name secp384r1 -genkey -noout -out keypair.pem
openssl ec -in keypair.pem -pubout -out pubkey.pem

# strip PEM header/footer, decode to DER
tail -n +2 pubkey.pem  | sed  -e '$ d' > pubkey.b64
base64 -D -i pubkey.b64 -o pubkey.der

# extract bitstring and re-encode to base64
dd skip=23 if=pubkey.der of=barekey.der bs=1
base64 barekey.der -o barekey.b64

# you can compare the output of these to verify the BIT STRING is correct
#dumpasn1 pubkey.der
#hexdump -Cv barekey.der
echo "embed this in your code"
cat barekey.b64



Your pubkey.pem should look like:


-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEAs5xEIm001FFsRpXSRFEy57+swcr3nW9
SP3ERlrT539LE/x2auTwUVTCkaFS6R5IBl2QIvEml+UXgBU0sDK3PqZsqHcQzvBz
Z/lX7NehqphbVCQBr3nkhDwyq0tkrmyD
-----END PUBLIC KEY-----



And the final output will look like:

BALOcRCJtNNRRbEaV0kRRMue/rMHK951vUj9xEZa0+d/SxP8dmrk8FFUwpGhUukeSAZdkCLxJpflF4AVNLAytz6mbKh3EM7wc2f5V+zXoaqYW1QkAa955IQ8MqtLZK5sgw==


Code to parse the sample key produced by the script above.


let base64PubKey = """
BALOcRCJtNNRRbEaV0kRRMue/rMHK951vUj9xEZa0+d/SxP8dmrk8FFUwpGhUukeSAZdkCLxJpflF4AVNLAytz6mbKh3EM7wc2f5V+zXoaqYW1QkAa955IQ8MqtLZK5sgw==
"""
let pubKeyData = Data(base64Encoded: base64PubKey)!
var error: Unmanaged? = nil
let attributes = [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
                kSecAttrKeyClass: kSecAttrKeyClassPublic] as NSDictionary

if let pubKey = SecKeyCreateWithData(pubKeyData as NSData, attributes, &error) {
    print("pubKey = \(pubKey)")
} else {
    print("Failed to import public key: \(String(describing: error))")
}


Produces the output:

pubKey = < SecKeyRef curve type: kSecECCurveSecp384r1, 
algorithm id: 3, key type: ECPublicKey, version: 4, block size: 384 bits, 
y: 065D9022F12697E517801534B032B73EA66CA87710CEF07367F957ECD7A1AA985B542401AF79E4843C32AB4B64AE6C83, 
x: 02CE711089B4D35145B11A57491144CB9EFEB3072BDE75BD48FDC4465AD3E77F4B13FC766AE4F05154C291A152E91E48, 
addr: 0x7fd6cf112ff0>

To answer your question shortly, SecKeyCreateWithData expects DER formatted data, not PEM (DER is pretty much the PEM without the headers). Also note that at least kSecAttrKeyType, kSecAttrKeyClass and kSecAttrKeySizeInBits must be defined in the attributes. You are correct that the size attribute doesn’t matter (for now) as I have experienced that too, but it’s still required in the recreation attributes. Hope this helps! Craz1k0ek

Hi eskimo, could you please share how did you create dump from ASN1 code on MAC? I have got the public key but exactly facing the same issue. I need to get the bit string from the ASN and used it to create secKey. Thanks in advance.

could you please share how did you create dump from ASN1 code on MAC?

I used the dumpasn1 tool. There’s a link to it in this post.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Hi Eskimo,

I'm also trying to extract the BIT STRING part so as to create public key using SecKeyCreateWithData, but was failing with "EC public key creation from data failed"
So I tried checking out with the same string that's discussed here.

I created txt file with the base64 encoded string
Code Block
echo "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECDDYmcIpUxC2EVJu8o/qPHPoZMId11A/2nVAXJByFOYSigUnZ6njK4qZQMGxLr3xNND17yYoXZ21QKus4bUHfg==" >> new.txt


Then I base64 decoded it to a file
Code Block
base64 --decode new.txt > decoded.asn1  

Then I checked with dumpasn1
Code Block
$ dumpasn1 -p decoded.asn1
SEQUENCE {
 SEQUENCE {
  OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
  OBJECT IDENTIFIER secp256k1 (1 3 132 0 10)
  }
 BIT STRING
  04 08 30 D8 99 C2 29 53 10 B6 11 52 6E F2 8F EA
  3C 73 E8 64 C2 1D D7 50 3F DA 75 40 5C 90 72 14
  E6 12 8A 05 27 67 A9 E3 2B 8A 99 40 C1 B1 2E BD
  F1 34 D0 F5 EF 26 28 5D 9D B5 40 AB AC E1 B5 07
  7E
 }


The output was the same as yours
Then I tried hexdump:
Code Block
hexdump -Cv decoded.asn1 
00000000 30 56 30 10 06 07 2a 86 48 ce 3d 02 01 06 05 2b |0V0...*.H.=....+|
00000010 81 04 00 0a 03 42 00 04 08 30 d8 99 c2 29 53 10 |.....B...0...)S.|
00000020 b6 11 52 6e f2 8f ea 3c 73 e8 64 c2 1d d7 50 3f |..Rn...<s.d...P?|
00000030 da 75 40 5c 90 72 14 e6 12 8a 05 27 67 a9 e3 2b |.u@\.r.....'g..+|
00000040 8a 99 40 c1 b1 2e bd f1 34 d0 f5 ef 26 28 5d 9d |..@.....4...&(].|
00000050 b5 40 ab ac e1 b5 07 7e              |.@.....~|
00000058


but as you can see, the complete data was getting converted. So I tried using hexdump with offset 23.

Code Block
hexdump -Cv -s 23 decoded.asn1 > hexdumped.dat         
00000017 04 08 30 d8 99 c2 29 53 10 b6 11 52 6e f2 8f ea |..0...)S...Rn...|
00000027 3c 73 e8 64 c2 1d d7 50 3f da 75 40 5c 90 72 14 |<s.d...P?.u@\.r.|
00000037 e6 12 8a 05 27 67 a9 e3 2b 8a 99 40 c1 b1 2e bd |....'g..+..@....|
00000047 f1 34 d0 f5 ef 26 28 5d 9d b5 40 ab ac e1 b5 07 |.4...&(]..@.....|
00000057 7e                        |~|
00000058


this gave me the desired output. But when I try to base64 encoding, this is what I got

Code Block
base64 -b 76 hexdumped.dat           
MDAwMDAwMTcgIDA0IDA4IDMwIGQ4IDk5IGMyIDI5IDUzICAxMCBiNiAxMSA1MiA2ZSBmMiA4ZiBl
YSAgfC4uMC4uLilTLi4uUm4uLi58CjAwMDAwMDI3ICAzYyA3MyBlOCA2NCBjMiAxZCBkNyA1MCAg
M2YgZGEgNzUgNDAgNWMgOTAgNzIgMTQgIHw8cy5kLi4uUD8udUBcLnIufAowMDAwMDAzNyAgZTYg
MTIgOGEgMDUgMjcgNjcgYTkgZTMgIDJiIDhhIDk5IDQwIGMxIGIxIDJlIGJkICB8Li4uLidnLi4r
Li5ALi4uLnwKMDAwMDAwNDcgIGYxIDM0IGQwIGY1IGVmIDI2IDI4IDVkICA5ZCBiNSA0MCBhYiBh
YyBlMSBiNSAwNyAgfC40Li4uJihdLi5ALi4uLi58CjAwMDAwMDU3ICA3ZSAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHx+fAowMDAwMDA1OAo=



This is not at all like the string that you got - BAgw2JnCKVMQthFSbvKP6jxz6GTCHddQP9p1QFyQchTmEooFJ2ep4yuKmUDBsS698TTQ9e8mKF2d
tUCrrOG1B34=


What could have gone wrong? can you help me out? If I can find the issue, maybe I'll be able to fix it for my actual string as well. Why is it that the base64 string is different?