How do you codesign with a SmartCard

According to https://developer.apple.com/forums/thread/677139 such certs dont appear in Keychain assistant or security find-identities

******, but sure, I can take that.

security export-smartcard

shows my cert up:

==== certificate #4
        class : "cert"
        subj : <aaaaaaaaaa
        cenc : 3
        ctyp : 3
        pkhh : <aaaaa aaaa>
        agrp : "com.apple.token"
        pdmn : "dk"
        labl : "Certificate For Card Authentication (Developer ID Application: XXXXX)"
        UUID : "XXXX-XXXXX"
        mdat : 2000-00-00-00
        slnr : <XXXXX>
        sync : 0
        sha1 : <XXXXX>
        tkid : "com.apple.pivtoken:XXXXXX"
        musr : <>
        cdat : 2000-***-XX-***
        tomb : 0
        issr : <XXXX>
        skid : <XXXXX>
        accc : constraints: {
                        ord : true
                }
                protection: {
                        tkid : "com.apple.pivtoken:XXXX"
                }

Now that question is, how do I tell codesign to use this certificate?

Replies

My understanding is that this should work, although I must admit to having never actually done it in practice.

The best way to specify an exact signing identity to use is to pass a SHA-1 hash of the identity’s certificate to codesign. See the discussion of this in the codesign man page. If you do that, does it work?

Share and Enjoy

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

My steps:

  • I create the PrivKey on the yubikey, generate the CSR on-device, then submitted to apple.
  • Then I download the cert from Apple, import to yubikey as well as my local keychain.

If I import into the keychain, the keychain assistant shows the cert, but not showing up the private key. codesign gives me the following error:

➜  ~ codesign -s '966XXXX' EXECUTABLE --force
EXECUTABLE: replacing existing signature
Warning: unable to build chain to self-signed root for signer "Developer ID Application: XXXXXX"
EXECUTABLE: errSecInternalComponent
➜  ~ 

If I do not import the cert, I receive:

➜  ~ codesign -s '966***' EXECUTABLE --force
replacing existing signature
errSecInternalComponent

Note no more warning this time

Just for the sake of testing, if you run through this same process but with the private key in the keychain rather than the hardware token, does that work?

IMPORTANT Don’t use a Developer ID certificate for this, because those as precious. Rather, create a temporary Apple Development certificate that you can happily revoke at the end.

The reason I ask is that these errors suggest that codesign found your digital identity but is missing an intermediate. This is something I discuss in gory detail in… OK, it’s time to add a hot key for this one… Resolving errSecInternalComponent errors during code signing.

Share and Enjoy

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

Not sure what you mean by "same process", but I've already had working certs in my system keychain for a while

> Not sure what you mean by "same process",

I’m suggesting that:

  1. On a fresh Mac [1]…

  2. Create a CSR.

  3. Use the Developer website to issue an Apple Development certificate for that.

  4. Import that into your keychain.

  5. Use it to sign code, using passing the certificate’s SHA-1 hash to codesign.

  6. Export the digital identity from your keychain as a .p12.

  7. Delete the public key, private key, and certificate from your keychain.

  8. Import the .p12 into your hardware token.

  9. Repeat step 5.

What do you see?

Share and Enjoy

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

[1] I use a VM for this sort of things so it’s easy to get back to a clean state. If you don’t have that setup, try doing this from a Mac that you’ve note previously used for code signing. And if you don’t have one of those, create a new user account on your main Mac.

  • This works, it seems, but why, and does this my the previous DeveloperID cert I generated on-device is gone forever, wasted my last DeveloperID slot?

Add a Comment

However: signing this way results in this:

codesign -dvvvv Tools
....
CDHash=a1b5e18c27bdb1df84f49c7ae0eff9b13681e86f
Signature size=4785
Authority=(unavailable)

But signing with the key in my system keychain:

CDHash=a1b5e18c27bdb1df84f49c7ae0eff9b13681e86f
Signature size=4785
Authority=Apple Development: XXXXX
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=Mar 31, 2023 at 5:08:52 PM

@eskimo Seems a bug on Apple side?

does this my the previous DeveloperID cert I generated on-device is gone forever, wasted my last DeveloperID slot?

I don’t understand this question.

This works

Cool.

However: signing this way results in this

Well, that’s weird. That again suggests a chain of trust issue. When you signed the program, did you get any warnings from codesign?

What does the full output from codesign -d look like?

If you do a codesign -d with the --extract-certificates, what do you get back?

Share and Enjoy

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

I don’t understand this question.

Since you can't export the private key from a smartcard, if this doesn't work, then the private key is useless, thus invalidated a developer ID cert.

To clarify, my original attempt was with a DeveloperID cert, with private key generated on device. Now I'm trying with AppleDeveloper cert original in my system keychain.

When you signed the program, did you get any warnings from codesign?

Nothing

What does the full output from codesign -d look like?

CodeDirectory v=20400 size=4044 flags=0x0(none) hashes=121+2 location=embedded
Hash type=sha256 size=32
CandidateCDHash sha256=a1b5e18c27bdb1df84f49c7ae0eff9b13681e86f
CandidateCDHashFull sha256=a1b5e18c27bdb1df84f49c7ae0eff9b13681e86f674af2438e32b7bb37f6dadf
Hash choices=sha256
CMSDigest=a1b5e18c27bdb1df84f49c7ae0eff9b13681e86f674af2438e32b7bb37f6dadf
CMSDigestType=2
Launch Constraints:
        None
CDHash=a1b5e18c27bdb1df84f49c7ae0eff9b13681e86f
Signature size=4785
Authority=(unavailable)
Info.plist=not bound
TeamIdentifier=XXXXXXXXXXX
Sealed Resources=none
Internal requirements count=1 size=172

If you do a codesign -d with the --extract-certificates, what do you get back?

codesign --display --extract-certificates FILE
Executable=FILE

and nothing else

Since you can't export the private key from a smartcard, if this doesn't work, then the private key is useless, thus invalidated a developer ID cert.

Indeed.

On the plus side, once you get this working with Apple Development, you should be able to ‘port’ the solution to Developer ID.

Nothing

Weird.

Somehow codesign has managed to create a signature that both knows about your certificate (the adhoc flag is not set and there’s a TeamIdentifier field) and doesn’t know about your certificate (there’s noting useful in Authority). Usually this means that it was unable to build a chain of trust but in that case I’d expect to output at least a warning.

and nothing else

No files were written to disk? For example:

% codesign -d --extract-certificates /usr/bin/true 
Executable=/usr/bin/true
% ls -lh
total 24
-rw-r--r--  1 quinn  staff   1.2K  3 Apr 07:34 codesign0
-rw-r--r--  1 quinn  staff   1.0K  3 Apr 07:34 codesign1
-rw-r--r--  1 quinn  staff   1.2K  3 Apr 07:34 codesign2

Share and Enjoy

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

➜  Downloads codesign -d --extract-certificates GPUTools
Executable=/Users/naville/Downloads/GPUTools
➜  Downloads ls | grep GPUTools
GPUTools

I've also sent the signed executable to your @apple.com address if it helps?

I dusted off my YubiKey and tried this out today. Here’s what I did:

  1. On macOS 13.2.1 with YubiKey Manager 1.2.4 installed…

  2. In Terminal, I found my Apple Development signing identity:

    % security find-identity
    …
      Valid identities only
    …
      4) 4E587951B705280CBB8086325CD134D4CDA04977 "Apple Development: Quinn Quinn (7XFU7D52S4)"
    …
    
  3. I ran Keychain Access and exported that to a .p12 file.

  4. I then used Keychain Access to delete both the private key and the certificate.

  5. Back in Terminal, I repeated step 2 and confirmed that the identity was gone.

  6. I attached my YubiKey 5 NFC via USB.

  7. I elected not to pair it with my account.

  8. I use YubiKey Manager to clear out all the PIV slots.

  9. And then import the .p12 from step 3 into the Digital Signature slot (9c). (I don’t think the exact slot matters but that seems like the right choice.)

  10. I disconnected and reconnected the YubiKey, continuing to opt out of pairing.

  11. In Terminal, I ran sc_auth to confirm that my identity is online:

    % sc_auth identities
    …
    SmartCard: com.apple.pivtoken:7998D08B6740108651825B47E23CEE67
    Unpaired identities:
    D3E93367555726C010D6D0A6008AA9A21EAF1C7E	Certificate For Digital Signature (Apple Development: Quinn Quinn (7XFU7D52S4))
    
  12. I then made a copy of the true command-line tool:

    % cp /usr/bin/true MyTrue
    
  13. I re-signed it with the identity on my YubiKey:

    % codesign -s "Apple Development: Quinn Quinn (7XFU7D52S4)" -f MyTrue
    MyTrue: replacing existing signature
    

    During this process, the system prompted me for my YubiKey PIN twice.

  14. I dumped the signature to confirm that it looks OK:

    % codesign -d -vvv MyTrue                                            
    …
    Authority=Apple Development: Quinn Quinn (7XFU7D52S4)
    Authority=Apple Worldwide Developer Relations Certification Authority
    Authority=Apple Root CA
    …
    TeamIdentifier=SKMME9E2Y8
    …
    
  15. I successfully ran the command:

    % ./MyTrue ; echo $?
    0
    
  16. I repeat the process with the certificate hash:

    % codesign -s "4E587951B705280CBB8086325CD134D4CDA04977" -f MyTrue
    MyTrue: replacing existing signature
    

    That also worked just fine.

Share and Enjoy

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

My card is indeed pair with the OS, so I unpaired with:

sc_auth unpair

so sc_auth list returns empty.

Then:

sc_auth identities shows:

SmartCard: com.apple.pivtoken:45E0EF9E6A9A3C156F00F31E01CB7C87
Unpaired identities:
70AFFB8B07901499E4720DD172AF990A53877BFC        Certificate For Card Authentication (Developer ID Application)
9F6993375273AC0F931A6FCEDFCC41E4D6603AB7        Certificate For Digital Signature (Apple Development)
C897376499298448D3A9304F92B133399226E82C        Certificate For PIV Authentication (Yubico PIV Authentication)

Cool, and seems similar to what you got.

Then I try to remove the signature with codesign --remove-signature , now I have the executable:

code object is not signed at all

At last, I try to sign again:

codesign -s '458AXXXX' Tools --force

smartcard authentication does pop-up, then:

codesign -dvvv Tools

The same results appear

Let’s try to separate your tool from your signing. If you make a copy of the true tool, as I showed above, and then sign it as you’ve described, do you still have this problem?

Share and Enjoy

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

➜  Downloads cp /usr/bin/true .
➜  Downloads codesign -s '45XXXXXXX' true --force                
true: replacing existing signature
➜  Downloads codesign -dvvvv true
Executable=/Users/naville/Downloads/true
Identifier=true
Format=Mach-O universal (x86_64 arm64e)
CodeDirectory v=20400 size=328 flags=0x0(none) hashes=5+2 location=embedded
VersionPlatform=1
VersionMin=852736
VersionSDK=852736
Hash type=sha256 size=32
CandidateCDHash sha256=6e63efXXXXXX
CandidateCDHashFull sha256=6e63ef927f9eXXX
Hash choices=sha256
CMSDigest=6e63efXXXXXX
CMSDigestType=2
Executable Segment base=0
Executable Segment limit=16384
Executable Segment flags=0x1
Page size=4096
Launch Constraints:
        None
CDHash=6e63ef9XXXXXX
Signature size=4785
Authority=(unavailable)
Info.plist=not bound
TeamIdentifier=QXXXXX
Sealed Resources=none
Internal requirements count=1 size=168

OK. At this point I’m running out of ideas. This is working for me with the setup I described above. I recommend that you try replicating my setup more closely and see if you can figure out what’s different.

Share and Enjoy

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