Best practices for post-build codesigning

My post-build script takes the "developeridexport" archive export, zips it up and uses notarytool to notarize it. I then add the .zip to a .dmg disk image. The next step is to codesign the disk image before notarizing that too. The issue is my Developer ID Application certificate is not accessible to the build host. (When I was doing this in Microsoft AppCenter (now defunct), it had a copy of my Developer ID Application certificate.) What steps do I need to take to get the disk image signed for notarization?

Thanks! Lance

Answered by DTS Engineer in 856864022

My understanding is that Xcode Cloud uses cloud signing [1]. Assuming that, the result you’re seeing make sense. With cloud signing the signing identity is never present in the keychain. Hence the whole “cloud” thing (-:

Cloud signing is only supported in Xcode (and xcodebuild). It’s not directly supported by codesign [2]. So, you won’t be able to use it to sign your disk image.

That leaves you with a few options:

  • Don’t use a disk image. If it’s just a normal app, distributing it as a zip archive is a reasonable option.
  • Don’t sign your disk image. While I generally recommend that folks sign their disk images, it’s not an absolute requirement.
  • Pass your normal (so, not managed) Developer ID Application signing identity to Xcode Cloud and use that to sign your disk image.

I’ll admit that none of these are particularly appealing, and I think it’d make sense for you to file an enhancement request against Xcode Cloud for a better way to deal with this.

Please post your bug number, just for the record.

Share and Enjoy

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

[1] TN3161 Inside Code Signing: Certificates says that explicitly, but I wrote that so I’m not sure if it’s to be trusted (-:

There is, however, a way you can confirm this for yourself:

  1. Download a copy of your app that was signed by Xcode Cloud.

  2. Extract the certificates from it using --extract-certificates:

    % codesign -d --extract-certificates Test799215.app 
    
  3. Dump the leaf certificate like so:

    % codesign -d --extract-certificates Test799215.app 
    …
    % openssl x509 -inform der -in codesign0 -text
    Certificate:
        …
        Signature Algorithm: sha256WithRSAEncryption
            …
            Subject: UID=SKMME9E2Y8, CN=Developer ID Application: Quinn Quinn …
            …
            X509v3 extensions:
                …
                1.2.840.113635.100.6.1.32: 
                    ..
    …
    

The Developer ID Application in the subject confirms its a Developer ID Application certificate. And the presence of an extension with the 1.2.840.113635.100.6.1.32 OID confirms that it’s cloud managed.

Say what? For stuff like this I typically refer to the docs on the Apple PKI page. In this case Developer ID CPS makes it clear that this extension is present on “Cloud Managed Certificates”.

[2] I dug into the mechanics of this at one point and it’s interesting. Xcode passes a ‘to be signed’ blob to the Developer website which returns the signature, and then Xcode invokes codesign to insert the signature into the app. If you’re curious, export a cloud signed app from the Xcode organiser and look at the resulting Packaging.log file.

WARNING This is all implementation detail of course, not something you should rely on. Don’t interpret the above as an invitation to reverse engineer this mechanism!

I’d like to clarify your issue here. You wrote:

The issue is my Developer ID Application certificate is not accessible to the build host.

I’m presuming you mean “Developer ID Application signing identity” here, because you can’t sign anything with just a certificate. For more about that common terminological mixup, see TN3161 Inside Code Signing: Certificates.

However, that’s not my actual question. Rather, I’d like to focus on the “is not accessible to the build host” bit. You put this thread in the Xcode Cloud subtopic, which suggests that by “build host” you mean the Xcode Cloud builder. Is that right?

Share and Enjoy

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

Hi. Yes, the Developer ID identity. Thanks for promoting the use of proper terminology 😁 I've read through a number of your other posts on this forum and found them informative and helpful. And yes, I'm referring to Xcode Cloud. My ci_post_xcodebuild.sh script includes:

codesign ... --sign "$CODESIGN_IDENTITY" diskimage.dmg

Where CODESIGN_IDENTITY is the SHA-1 of my "Developer ID Application: My Company (MYTEAMID)". It unsurprisingly fails with:

error: The specified item could not be found in the keychain.

I can also see there are no identities in the keychain with:

security find-identity -v -p codesigning

I'm hoping Xcode Cloud provides me a better way to access the identity than uploading the .p12 to the host...

Thanks!

Accepted Answer

My understanding is that Xcode Cloud uses cloud signing [1]. Assuming that, the result you’re seeing make sense. With cloud signing the signing identity is never present in the keychain. Hence the whole “cloud” thing (-:

Cloud signing is only supported in Xcode (and xcodebuild). It’s not directly supported by codesign [2]. So, you won’t be able to use it to sign your disk image.

That leaves you with a few options:

  • Don’t use a disk image. If it’s just a normal app, distributing it as a zip archive is a reasonable option.
  • Don’t sign your disk image. While I generally recommend that folks sign their disk images, it’s not an absolute requirement.
  • Pass your normal (so, not managed) Developer ID Application signing identity to Xcode Cloud and use that to sign your disk image.

I’ll admit that none of these are particularly appealing, and I think it’d make sense for you to file an enhancement request against Xcode Cloud for a better way to deal with this.

Please post your bug number, just for the record.

Share and Enjoy

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

[1] TN3161 Inside Code Signing: Certificates says that explicitly, but I wrote that so I’m not sure if it’s to be trusted (-:

There is, however, a way you can confirm this for yourself:

  1. Download a copy of your app that was signed by Xcode Cloud.

  2. Extract the certificates from it using --extract-certificates:

    % codesign -d --extract-certificates Test799215.app 
    
  3. Dump the leaf certificate like so:

    % codesign -d --extract-certificates Test799215.app 
    …
    % openssl x509 -inform der -in codesign0 -text
    Certificate:
        …
        Signature Algorithm: sha256WithRSAEncryption
            …
            Subject: UID=SKMME9E2Y8, CN=Developer ID Application: Quinn Quinn …
            …
            X509v3 extensions:
                …
                1.2.840.113635.100.6.1.32: 
                    ..
    …
    

The Developer ID Application in the subject confirms its a Developer ID Application certificate. And the presence of an extension with the 1.2.840.113635.100.6.1.32 OID confirms that it’s cloud managed.

Say what? For stuff like this I typically refer to the docs on the Apple PKI page. In this case Developer ID CPS makes it clear that this extension is present on “Cloud Managed Certificates”.

[2] I dug into the mechanics of this at one point and it’s interesting. Xcode passes a ‘to be signed’ blob to the Developer website which returns the signature, and then Xcode invokes codesign to insert the signature into the app. If you’re curious, export a cloud signed app from the Xcode organiser and look at the resulting Packaging.log file.

WARNING This is all implementation detail of course, not something you should rely on. Don’t interpret the above as an invitation to reverse engineer this mechanism!

Thank you for such a thorough response! It's at least reassuring that I'm on the right path 😁 I posted a suggestion request under FB20115488. As a quick fix solution, I'm going to try storing the identity as a Base64 encoded environment variable and adding it to the keychain during the build. This is the approach advocated when using GitHub Actions and (somewhat) Azure DevOps Pipelines. I'll report back if that hits a roadblock...

I'm happy to report that I implemented a successful solution using environment variables and a bunch of keychain related security commands.

I posted a suggestion request under FB20115488.

Much appreciated. I’ve added my own notes to your bug.

I'm happy to report that I implemented a successful solution

Well, that’s fun |-: But I am glad you got it working.

Share and Enjoy

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

Best practices for post-build codesigning
 
 
Q