Both the codesign
tool and Xcode allow you to sign code with a hardware-based code-signing identity. However, setting that up can be a bit of a challenge. Recently a developer open a DTS tech support incident requesting help with this, and so I thought I’d post my instructions here for the benefit of all.
If you have any questions or comments about this, please start a new thread, tagging it with Code Signing so that I see it.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Signing code with a hardware-based code-signing identity
Both the codesign
tool and Xcode allow you to sign code with a hardware-based code-signing identity. This post explains how to set that up.
I used macOS 14.2.1 with Xcode 15.2. For my hardware-based key I used a YubiKey 5 NFC that I reset to its defaults. I installed YubiKey Manager 1.2.5.
IMPORTANT While I used a YubiKey, the code signing parts of this process should work with any token that has a functioning CryptoTokenKit driver.
In the case of the YubiKey, it presents a PIV interface and thus it’s supported by macOS’s built-in PIV CryptoTokenKit driver.
In this example I created an Apple Development certificate because those are dime a dozen. This process should work with any other type of code-signing certificate. Indeed, it make sense to store your most precious keys in a hardware token, including your Developer ID keys. For more on that topic, see The Care and Feeding of Developer ID.
Generate a certificate signing request
To generate a certificate signing request (CSR):
-
Connect the YubiKey via USB.
-
Dismiss any system alerts:
-
If the “Allow this accessory to connect?” alert comes up, click Allow.
-
If the Keyboard Setup Assistant comes up, quit that.
-
If the ctkbind notification comes up, dismiss that. Coded signing does not require that you bind your login account to your hardware token.
-
-
Launch YubiKey Manager.
-
Choose Applications > PIV.
-
Click Configure Certificates.
-
Select Digital Signature (slot 9c). In the past I’ve run into situations where signing fails if you don’t use this slot, although I haven’t tested that in this particular case.
-
Click Generate.
-
Select Certificate Signing Request (CSR) and click Next.
-
Select the RSA2048 algorithm and click Next.
-
Enter a subject and click Next. The value you use here doesn’t matter because Apple ignores pretty much everything in the CSR except the public key.
-
Click Generate.
-
Choose a save location and name. Don’t include a file name extension.
-
When prompted for the management key, enter that and click OK.
-
When prompted for the PIN, enter that and click OK.
-
The app will generate a
.csr
file at your chosen location. -
Quit YubiKey Manager.
Note Apple typically uses the .certSigningRequest
extension for CSRs, but this process works just fine with the .csr
extension used by YubiKey Manager.
Generate a certificate from your CSR
To generate a certificate from that CSR:
-
In Safari, go to Developer > Account and log in.
-
If you’re a member of multiple teams, make sure you have the correct one selected at the top right.
-
Click Certificates.
-
Click the add (+) button to create a new certificate.
-
Select Apple Development and click Continue.
-
Click Choose File, select your CSR file, and click Upload.
-
Click Continue to generate your certificate.
-
That takes you to the Download Your Certificate page. Click Download.
-
In Terminal, calculate a SHA-1 hash of your
.cer
file.
% shasum "development.cer"
840f40ef6b10bedfb2315ac49e07f7e6508a1680 development.cer
Import the certificate to form a code-signing identity
To import this certificate into your YubiKey:
-
Convert the certificate to PEM form:
% openssl x509 -in "development.cer" -inform der -out "development.pem"
-
Launch YubiKey Manager.
-
Choose Applications > PIV.
-
Click Configure Certificates.
-
Select Digital Signature (slot 9c).
-
Click Import.
-
In the file dialog, select the PEM and click Import.
-
When prompted for the management key, enter that and click OK. The UI updates to show the certificate issuer (
Apple Worldwide Developer Relations Certificate Authority
) and subject (Apple Development: UUU
, whereUUU
identifies you). -
Quit YubiKey Manager.
-
Unplug the YubiKey and then plug it back in.
Sign a test program
Before digging into Xcode, check that you can sign code with the codesign
tool:
-
Create a small program to test with. In my case I decided to re-sign the built-in
true
command-line tool:% cp "/usr/bin/true" "MyTool" % codesign -s - -f "MyTool"
-
Run
codesign
to sign your program, passing in the SHA-1 hash of the certificate you imported into the YubiKey:% codesign -s 840f40ef6b10bedfb2315ac49e07f7e6508a1680 -f "MyTool"
-
When prompted for the PIN, enter that and click OK. The
codesign
invocation completes like so:% codesign -s 840f40ef6b10bedfb2315ac49e07f7e6508a1680 -f "MyTool" MyTool: replacing existing signature
Sign from Xcode
To sign from Xcode:
-
Open your project in Xcode. In my case I created a new project by choosing File > New then selecting macOS > Command Line tool.
-
In Signing & Capabilities for the tool target, turn off “Automatically manage signing”.
-
In Build Settings, find the Code Signing Identity build setting, choose Other, and then enter the SHA-1 hash of your certificate.
-
Choose Product > Build.
-
When prompted for the PIN, enter that and click OK. The build then completes.
IMPORTANT This requires Xcode 13 or later. Earlier versions of Xcode only work with file-based code-signing identities.