Ambiguous "Apple Developer" certificate for macOS

I have an Xcode project (generated from Qt) which is signed by a post-processing script.

It uses the invocation: codesign -o runtime --sign "$(CODE_SIGN_IDENTITY)"

CODE_SIGN_IDENTITY is set to "Apple Development" in the Build Settings for the target.

The signing step fails with this complaint

Apple Development: ambiguous (matches "Apple Development: <my name> (an ID)" and "Apple Development: <my company email> (another ID)" in login.keychain-db)

It is true, I do have two Apple Development certificates. I thought one is for personal development (when you pick the personal team) and the other for company development (when I pick the company team).

I have other Xcode projects (built "by hand") which have CODE_SIGN_IDENTITY set to "Apple Development" and with Automatic signing turned on, and they build just fine, even though I have two certificates with common names beginning "Apple Development".

However, when I look at the build log of those regular Xcode projects, which are signed by Xcode rather than in a post-processing script, the Signing step logs this:

Signing Identity: Apple Development: <my name> (an ID)

not simply "Apple Development". Xcode seems to have resolved the ambiguity all on its own before calling codesign. It then calls codesign using the hash of the certificate as its identifier.

How can I emulate Xcode's behavior here? The postprocessing script runs on different developer's machines - they all have multiple "Apple Development" certificates, and they are all different from one another.

Accepted Reply

I’ve never looked at the exact algorithm that Xcode uses for this, but it runs something like:

  1. Get the team from the project’s build settings.

  2. Build a list of all valid code-signing identities.

  3. Filter it based on the type: Apple Development, Apple Distribution, Developer ID Application, and so on.

  4. Filter it again based on the team.

  5. Use one of those.

I suspect Xcode is doing this in code, so it can rely on more precise APIs. In a script you’d take some shortcuts:

  • For step 2, Creating distribution-signed code for macOS shows how to find all the valid code-signing identities and their SHA1 hashes.

  • For step 3, look at the prefix on the identity name.

  • For step 4, you need to look at the Organisation Unit (OU) property within the identity’s certificate.

I’m not sure how best to get the OU from a script, but this will get you started:

% security find-certificate -c 'Apple Development: Quinn Quinn (7XFU7D52S4)' -p | openssl x509 -text
Certificate:
    …
    Signature Algorithm: sha256WithRSAEncryption
        …
        Subject: … OU=SKMME9E2Y8, O=Quinn Quinn, C=US
…

Share and Enjoy

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

Replies

I’ve never looked at the exact algorithm that Xcode uses for this, but it runs something like:

  1. Get the team from the project’s build settings.

  2. Build a list of all valid code-signing identities.

  3. Filter it based on the type: Apple Development, Apple Distribution, Developer ID Application, and so on.

  4. Filter it again based on the team.

  5. Use one of those.

I suspect Xcode is doing this in code, so it can rely on more precise APIs. In a script you’d take some shortcuts:

  • For step 2, Creating distribution-signed code for macOS shows how to find all the valid code-signing identities and their SHA1 hashes.

  • For step 3, look at the prefix on the identity name.

  • For step 4, you need to look at the Organisation Unit (OU) property within the identity’s certificate.

I’m not sure how best to get the OU from a script, but this will get you started:

% security find-certificate -c 'Apple Development: Quinn Quinn (7XFU7D52S4)' -p | openssl x509 -text
Certificate:
    …
    Signature Algorithm: sha256WithRSAEncryption
        …
        Subject: … OU=SKMME9E2Y8, O=Quinn Quinn, C=US
…

Share and Enjoy

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

and, of course after spending a day mired in awk, grep, sed and zsh minutiae, I find there's a EXPANDED_CODE_SIGN_IDENTITY and EXPANDED_CODE_SIGN_IDENTITY_NAME, which seems to be the SHA-1 hash and full name of the certificate which Xcode would choose, following its internal algorithm.

Interesting. That’s news to me. Thanks for closing the loop.

Share and Enjoy

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