Is it possible to directly distribute a macOS app with a Developer ID Certificate that belongs to a different team?
I am trying to resolve issues that arise when distributing a macOS app with a Network Extension (Packet Tunnel) outside the App Store using a Developer ID Certificate from a different team than the app’s provisioning profiles and entitlements.
I started by attempting Direct Distribution in Xcode with automatic signing. However, it fails with the following message:
Provisioning profile "Mac Team Direct Provisioning Profile: ” failed qualification checks: Profile doesn't match the entitlements file's value for the com.apple.developer.networking.networkextension entitlement.
I suspect the issue is that the provisioning profile allows "packet-tunnel-provider-systemextension", whereas the entitlements generated by Xcode contain "packet-tunnel-provider". When I manually modify the .entitlements file to include the -systemextension suffix, the project fails to build because Xcode does not recognize the modified entitlement. If there is a workaround for this issue, please let me know.
Due to these issues, I resorted to manually creating a signed and notarized app. My process is as follows:
-
Export the .app from the Xcode archive.
-
Since the exported .app does not contain the necessary entitlements or provisioning profile for direct distribution, I replace Contents/embedded.provisioningprofile in both the .app and the .appex network extension.
-
Sign the app and its components in the following order:
codesign --force --options runtime --timestamp --sign "Developer ID Application: <name>" <app>.app/Contents/Frameworks/<fw>.framework/
codesign --force --options runtime --timestamp --sign "Developer ID Application: <name>"<app>.app/Contents/PlugIns/<netext>.appex/Contents/Frameworks/<fw>.framework/Versions/A/<fw>
codesign --force --options runtime --entitlements dist-vpn.entitlements --timestamp --sign "Developer ID Application: <name>" <app>.app/Contents/PlugIns/<netext>.appex/
codesign --force --options runtime --entitlements dist.entitlements --timestamp --sign "Developer ID Application: <name>" <app>.app
- Verify the code signature:
codesign --verify --deep --strict --verbose=4 <app>.app
- <app>.app: valid on disk
- <app>.app: satisfies its Designated Requirement
- Create a ZIP archive using:
ditto -c -k --sequesterRsrc --keepParent <app>.app <app>.zip
- Notarize the app with notarytool and staple it.
The notarization completes successfully with errors: nil.
- Package the notarized app into a DMG, notarize, and staple the DMG.
The app runs successfully on the development machine. However, when moved to another machine and placed in /Applications, it fails to open. Inspecting Console.app reveals Gatekeeper is blocking the launch:
taskgated-helper <bundleid>: Unsatisfied entitlements: com.apple.developer.networking.networkextension, com.apple.developer.team-identifier taskgated-helper entitlements: { "com.apple.developer.networking.networkextension" = ("packet-tunnel-provider-systemextension"); "com.apple.developer.team-identifier" = <teamid>; }
As mentioned earlier, the Developer ID Certificate used for signing belongs to a different team. We are a third-party developer and do not have access to the Developer ID Certificate of the team assigned as the team-identifier. When I changed the bundle identifier (app ID), team, entitlements, and provisioning profiles to match the team associated with the Developer ID Certificate, the app worked.
My question is:
Is this failure caused by using a Developer ID Certificate from a different team, or should it still work if the provisioning profiles and entitlements are correctly set? Could there be an issue elsewhere in the provisioning profiles or entitlements for the original app ID?
Not without it being an entirely new app.
First up, Xcode won’t help you here )-: It has a bug that causes it to incorrectly sign Network Extensions when you export them for direct distribution with Developer ID signing. See Exporting a Developer ID Network Extension.
That post explains how to the export manually, but that’ll only work for the team that created the app in the first place. If you try to change teams then you run into this problem:
-
The Network Extension entitlements are restricted, which means they must be authorised by a provisioning profile.
-
The connection between your code and its profile is formed via the App ID entitlement. See TN3125 Inside Code Signing: Provisioning Profiles.
-
App IDs are specific to a team, so if team A has allocated the app’s App ID then team B can’t create a profile for it.
-
One component of the App ID is the bundle ID.
-
macOS uses the bundle ID to identify an app. If two apps have different bundle IDs, they’re different apps as far as macOS is concerned.
Some final notes:
-
If you mix Developer ID and Network Extension providers then you’ll end up needing to manually sign code. See Creating distribution-signed code for macOS and Packaging Mac software for distribution for specific advice on how to do that.
-
Don’t use
--sequesterRsrc
when creating zip archives for software distribution. See Extended Attributes and Zip Archives for info as to what this flag does. Ideally your product wouldn’t contain any extended attributes, and thus this issue is irrelevant. However, if it does contain extension attributes then sequestering them will cause you grief.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"