Securing code signing ceritifcates in the secure enclave

I am on a mission to secure our key material for our iOS app's code signing certificate.

My first endeavor with storing the code signing certificate on a YubiKey is a marginal success - it seems that with a pin policy that requires entering the PIN at least once we must enter the PIN umpteen times per build. Creating a certificate with a policy of never would be ill-advised.

On the other hand, we could chose to store the code signing certificate in the Secure Enclave. However, it seems that I am only allowed to create eliptic curve private keys and not RSA keys in the secure enclave. When I attempt to upload a certificate signing request to AppStoreConnect, I am told that only an RSA2048 key will do.

What I am after is a way to authenticate access to the certificate once per boot so that we can make multiple builds per day without manual intervention whilst also ensuring that the key material is not stored on disk. A yubikey would be preferable, but I am fine with the secure enclave if need be. Is there a way to achieve this?

Best regards, Emīls

Answered by DTS Engineer in 876573022

First up, I want to be clear about terminology.

You don’t sign code with a certificate, you sign it with a digital identity, that is, the combination of a certificate and its associated private key. There’s no need to protect the certificate; it’s the private key that matters.

I talk more about this in TN3161 Inside Code Signing: Certificates.

Second, the Secure Enclave (SE) doesn’t store private keys. Rather, you protect a private key with the SE. The SE generates the private key and returns it wrapped in a way so that only that SE can use it. You then send key operations, like ‘sign this blob with this wrapped key’, to the SE, it internally unwraps the key, does the operation, and returns you the result. So the unwrapped key material never leaves the SE, but it’s not stored in the SE.

Note This is how the SE works but it’s not true for other hardware-based keys. Notably, with a smart card the private key is actually stored on the smart card itself. Folks using the smart card don’t work with a wrapped key, they work with a ‘handle’ to the key.


You might’ve found this already but just in case you haven’t, and for the benefit of others following along, I want to drop a link to Signing code with a hardware-based code-signing identity. This walks you through an example of how to sign code with a hardware-based key.


Coming back to your main issue, you are correct that an SE-protected key won’t work for this because the SE only works with EC keys whereas Apple’s code signing architecture is based on RSA [1].

As to other hardware-based keys, their access control policy is determined by the hardware itself. I can’t offer insights into what is or isn’t feasible in that space. You’d have to talk to the hardware vendor.

If you find hardware that works for you and it’s not supported out of the box by macOS, you can add support for it using CryptoTokenKit. CryptoTokenKit supports smart cards and virtual tokens, on the latter lets you interface with anything. For example, it’s common to see network-based HSMs supported this way.


Oh, and I just noticed this:

for our iOS app's code signing [identity]

Keep in mind that App Store code-signing identities are not particularly high value:

  • If you lose one, you can just revoke it and create a new one.
  • If one is compromised, any attacker will still need your App Store Connect credentials to submit an app.

Given that, I generally don’t see folks take heroic measures to protect these identities. The keychain is just fine.

In contrast, Developer ID code-signing identities need to be treated much more carefully. I discuss that in The Care and Feeding of Developer ID.

Developer ID is a Mac thing, but IMO a similar level of caution is warranted for Enterprise distribution signing identities.

Share and Enjoy

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

[1] You could argue that it shouldn’t be, because RSA is deeply problematic, and I’d probably agree with you |-:

First up, I want to be clear about terminology.

You don’t sign code with a certificate, you sign it with a digital identity, that is, the combination of a certificate and its associated private key. There’s no need to protect the certificate; it’s the private key that matters.

I talk more about this in TN3161 Inside Code Signing: Certificates.

Second, the Secure Enclave (SE) doesn’t store private keys. Rather, you protect a private key with the SE. The SE generates the private key and returns it wrapped in a way so that only that SE can use it. You then send key operations, like ‘sign this blob with this wrapped key’, to the SE, it internally unwraps the key, does the operation, and returns you the result. So the unwrapped key material never leaves the SE, but it’s not stored in the SE.

Note This is how the SE works but it’s not true for other hardware-based keys. Notably, with a smart card the private key is actually stored on the smart card itself. Folks using the smart card don’t work with a wrapped key, they work with a ‘handle’ to the key.


You might’ve found this already but just in case you haven’t, and for the benefit of others following along, I want to drop a link to Signing code with a hardware-based code-signing identity. This walks you through an example of how to sign code with a hardware-based key.


Coming back to your main issue, you are correct that an SE-protected key won’t work for this because the SE only works with EC keys whereas Apple’s code signing architecture is based on RSA [1].

As to other hardware-based keys, their access control policy is determined by the hardware itself. I can’t offer insights into what is or isn’t feasible in that space. You’d have to talk to the hardware vendor.

If you find hardware that works for you and it’s not supported out of the box by macOS, you can add support for it using CryptoTokenKit. CryptoTokenKit supports smart cards and virtual tokens, on the latter lets you interface with anything. For example, it’s common to see network-based HSMs supported this way.


Oh, and I just noticed this:

for our iOS app's code signing [identity]

Keep in mind that App Store code-signing identities are not particularly high value:

  • If you lose one, you can just revoke it and create a new one.
  • If one is compromised, any attacker will still need your App Store Connect credentials to submit an app.

Given that, I generally don’t see folks take heroic measures to protect these identities. The keychain is just fine.

In contrast, Developer ID code-signing identities need to be treated much more carefully. I discuss that in The Care and Feeding of Developer ID.

Developer ID is a Mac thing, but IMO a similar level of caution is warranted for Enterprise distribution signing identities.

Share and Enjoy

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

[1] You could argue that it shouldn’t be, because RSA is deeply problematic, and I’d probably agree with you |-:

Securing code signing ceritifcates in the secure enclave
 
 
Q