Step-by-step guide for SecKeyCreateWithData from OpenSSL PEM?

A quick search for SecKeyCreateWithData reveals that it isn't the most straight-forward API to use for many Developers' use-cases ...


It seems reasonable that Developers who wish to use ECDSA for any sort of server <==> iOS communication discrete data exchange should be able to simply:


1. Fire up Terminal

2. Create Private + Public ECDSA keys:

# Generate an ECDSA Private key. 
openssl ecparam -genkey -name prime256v1 -out ec256-private.pem
 
# Generate an ECDSA Public key. 
openssl ec -in ec256-private.pem -pubout -out ec256-public.pem


3. <-- Follow some steps to use the Base64 .pem to create keys in iOS -->

4. Use SecKeyCreateWithData, ex:

let publicKey = SecKeyCreateWithData(pemData as CFData ...)


@eskimo -- might you consider creating a step-by-step tutorial as to your / Apple's best recommendation on how to accomplish this?


Your responsiveness on the forums is excellent, so thank you. It just seems that individual Developer issues could be avoided if "best practice" guide existed.



Perhaps a good example for the guide: take some data & an associated, hex-encoded signature (generated server-side) and, using iOS, verify it's legitimate, e.g.:

  • Step 1: create a public key from a supplied PEM file using SecKeyCreateWithData
  • Step 2: use SecKeyVerifySignature (w/ the publicKey), to verify

Accepted Reply

I‘m simply outlining my use-case to show a case where being able to load a PEM key would be very helpful.

I’m not arguing against the ability to import keys in more formats, including PEM. That’d be super useful, and it’d bring iOS-based platforms up to a level that macOS has had for many years. I recommend you file an enhancement request about this.

However, if you’re looking to solve this problem right now my recommendation is that you wrap the public key from step 5 in a certificate. It’s easy to do this on the server side, because the server has a full-featured security toolkit, and importing a certificate on the iOS side is trivial.

Taking a step back, there’s two ways you can deal with the limitations imposed by iOS’s Security framework:

  • You can write code on iOS to do the low-level security things that need to be done (strip PEM headers, convert Base64 to DER, strip the

    SubjectPublicKeyInfo
    header off the ASN.1 structure, and so on).
  • You can write code on the other platform to put the data in a format that iOS likes (either a certificate for

    SecCertificateCreateWithData
    , or a raw EC key for
    SecKeyCreateWithData
    ).

The latter is much easier, because you can use high-level APIs on the other platform to do the heavy lifting. While writing low-level security code is my kind of fun, it’s not necessarily the best use of your time.

You asked for a “‘best practice’ guide”, and that’s my idea of best practice (-:

Share and Enjoy

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

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

Replies

It seems reasonable that Developers who wish to use ECDSA for any sort of server <==> iOS communication should be able to simply:

I’m not a big fan of this overall strategy. There’s two approachs that I prefer, and both of them are relatively well supported by iOS APIs. I’ll explain each in turn.

Pseudo CSR

In this approach you do the following:

  1. Create a key pair on the device (

    SecKeyCreateRandomKey
    ).
  2. Export the public key (

    SecKeyCopyExternalRepresentation
    ) and pass it to the server.
  3. The server then issues a certificate and passes that to the client.

  4. Import that certificate (

    SecCertificateCreateWithData
    ) and use it to form a digital identity (
    SecIdentity
    ).

Once you have a digital identity you can use it for all sorts of good things, most notably with TLS APIs including

NSURLSession
.

This approach is very much like the certificate signing request (CSR) approach (per RFC 2986) that you find in lots of places, and it has the same advantage, namely that the private key never leaves the client.

Note I’ve called this Pseudo CSR because, in step 2, you don’t create an actual CSR. Creating a CSR is tricky on iOS because there’s no dedicated API for it and doing it by hand requires an ASN.1 library, which is also not available on iOS.

Server-Side Generation

The alternative is to have the server generate the digital identity from scratch and then pass that digital identity to the client in a PKCS#12 blob. The client can directly import this using

SecPKCS12Import
and from there get a
SecIdentity
that can be used in lots of APIs.

This approach is super simple but it has one key disadvantage, namely that there’s no cryptographic assurance that the private key is private to the client.

Share and Enjoy

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

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

Thank you for the response; the top-level question (a guide for loading a Terminal / OpenSSL-generated PEM key) still stands.


I should have used a more generic phrase (vs. the word “communication”) so that we didn’t go down a specific rabbit hole. For my specific use-case (Apple Wallet passes / PassKit), here’s what I meant ...


  1. My server creates Apple Wallet passes (which contain information, displayed via a QR code).
  2. The server signs the QR code using an ECDSA private key (that is, the QR code ends up having both the information + the hex-encoded digital signature).
  3. I distribute the passes.
  4. (At some later point) I use mobile apps to scan the QR code (thus, reading in both the information + the hex-encoded digital signature).
  5. At this point in the “communication”, I need to be able to use a distributed Public key to verify the signature is legitimate.


Again, this particular implementation isn’t really relevant to the main purpose of this post. I‘m simply outlining my use-case to show a case where being able to load a PEM key would be very helpful.

I‘m simply outlining my use-case to show a case where being able to load a PEM key would be very helpful.

I’m not arguing against the ability to import keys in more formats, including PEM. That’d be super useful, and it’d bring iOS-based platforms up to a level that macOS has had for many years. I recommend you file an enhancement request about this.

However, if you’re looking to solve this problem right now my recommendation is that you wrap the public key from step 5 in a certificate. It’s easy to do this on the server side, because the server has a full-featured security toolkit, and importing a certificate on the iOS side is trivial.

Taking a step back, there’s two ways you can deal with the limitations imposed by iOS’s Security framework:

  • You can write code on iOS to do the low-level security things that need to be done (strip PEM headers, convert Base64 to DER, strip the

    SubjectPublicKeyInfo
    header off the ASN.1 structure, and so on).
  • You can write code on the other platform to put the data in a format that iOS likes (either a certificate for

    SecCertificateCreateWithData
    , or a raw EC key for
    SecKeyCreateWithData
    ).

The latter is much easier, because you can use high-level APIs on the other platform to do the heavy lifting. While writing low-level security code is my kind of fun, it’s not necessarily the best use of your time.

You asked for a “‘best practice’ guide”, and that’s my idea of best practice (-:

Share and Enjoy

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

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

Bug Report / Suggestion 40255347 filed!


Thank you for your suggestions.