|
| 1 | +# macOS Signing and Notarization |
| 2 | + |
| 3 | +This repo can build a local unsigned app without Apple credentials. Public |
| 4 | +distribution outside the Mac App Store needs a Developer ID Application |
| 5 | +certificate, hardened runtime signing, notarization, and ticket stapling. |
| 6 | + |
| 7 | +## Current Local State |
| 8 | + |
| 9 | +The inspected local artifact is: |
| 10 | + |
| 11 | +```text |
| 12 | +apps/macos/dist/hmm.app |
| 13 | +``` |
| 14 | + |
| 15 | +Current signing state on this machine: |
| 16 | + |
| 17 | +- `codesign -dvvv --entitlements :- apps/macos/dist/hmm.app` reports an ad hoc |
| 18 | + signature. |
| 19 | +- `security find-identity -p codesigning -v` reports `0 valid identities found`. |
| 20 | +- `spctl -a -vv apps/macos/dist/hmm.app` fails before notarization because the |
| 21 | + artifact is not Developer ID signed. |
| 22 | + |
| 23 | +This is expected for the unsigned development archive. |
| 24 | + |
| 25 | +## What You Need From Apple |
| 26 | + |
| 27 | +1. Join the Apple Developer Program if the account is not already enrolled. |
| 28 | +2. Use the Account Holder role to create a Developer ID Application |
| 29 | + certificate. Apple also offers Developer ID Installer certificates, but this |
| 30 | + repo ships a zipped `.app`, so the Application certificate is the required |
| 31 | + first credential. |
| 32 | +3. Install the downloaded `.cer` in Keychain Access. It must appear under |
| 33 | + `My Certificates` with its private key. |
| 34 | +4. Create notarization credentials: |
| 35 | + - Recommended local path: store credentials in Keychain with `notarytool`. |
| 36 | + - CI path: use an App Store Connect API key file (`.p8`), key id, and issuer |
| 37 | + id as CI secrets. Individual API keys omit issuer id. |
| 38 | + |
| 39 | +## Store Notary Credentials Locally |
| 40 | + |
| 41 | +Use a Keychain profile named `hmm-notary`: |
| 42 | + |
| 43 | +```bash |
| 44 | +xcrun notarytool store-credentials hmm-notary \ |
| 45 | + --apple-id "you@example.com" \ |
| 46 | + --team-id "TEAMID" |
| 47 | +``` |
| 48 | + |
| 49 | +`notarytool` will prompt for an app-specific password if you do not pass |
| 50 | +`--password`. |
| 51 | + |
| 52 | +For API-key based authentication: |
| 53 | + |
| 54 | +```bash |
| 55 | +xcrun notarytool store-credentials hmm-notary \ |
| 56 | + --key /secure/path/AuthKey_KEYID.p8 \ |
| 57 | + --key-id KEYID \ |
| 58 | + --issuer ISSUER-UUID |
| 59 | +``` |
| 60 | + |
| 61 | +For Individual API Keys, omit `--issuer`. |
| 62 | + |
| 63 | +## Build a Signed Archive |
| 64 | + |
| 65 | +After installing the Developer ID Application certificate: |
| 66 | + |
| 67 | +```bash |
| 68 | +apps/macos/script/package_signed.sh |
| 69 | +``` |
| 70 | + |
| 71 | +The script auto-detects the first `Developer ID Application:` identity. To pin a |
| 72 | +specific identity: |
| 73 | + |
| 74 | +```bash |
| 75 | +SIGNING_IDENTITY="Developer ID Application: Your Name (TEAMID)" \ |
| 76 | + apps/macos/script/package_signed.sh |
| 77 | +``` |
| 78 | + |
| 79 | +Output: |
| 80 | + |
| 81 | +```text |
| 82 | +apps/macos/dist/hmm-macos-signed.zip |
| 83 | +``` |
| 84 | + |
| 85 | +The script signs with: |
| 86 | + |
| 87 | +- `--options runtime` |
| 88 | +- `--timestamp` |
| 89 | +- Developer ID Application identity |
| 90 | + |
| 91 | +No App Sandbox entitlement is applied. This app launches a local daemon and uses |
| 92 | +ScreenCaptureKit/system audio APIs; sandboxing should be reviewed as a separate |
| 93 | +product decision, not added casually for Developer ID distribution. |
| 94 | + |
| 95 | +## Notarize and Staple |
| 96 | + |
| 97 | +With a stored Keychain profile: |
| 98 | + |
| 99 | +```bash |
| 100 | +apps/macos/script/notarize.sh |
| 101 | +``` |
| 102 | + |
| 103 | +With direct API key environment variables: |
| 104 | + |
| 105 | +```bash |
| 106 | +NOTARY_KEY=/secure/path/AuthKey_KEYID.p8 \ |
| 107 | +NOTARY_KEY_ID=KEYID \ |
| 108 | +NOTARY_ISSUER=ISSUER-UUID \ |
| 109 | + apps/macos/script/notarize.sh |
| 110 | +``` |
| 111 | + |
| 112 | +For Individual API Keys, leave `NOTARY_ISSUER` unset. |
| 113 | + |
| 114 | +Output: |
| 115 | + |
| 116 | +```text |
| 117 | +apps/macos/dist/hmm-macos-notarized.zip |
| 118 | +``` |
| 119 | + |
| 120 | +The script submits the signed zip with `xcrun notarytool submit --wait`, staples |
| 121 | +the ticket to `hmm.app`, validates the staple, runs Gatekeeper assessment with |
| 122 | +`spctl`, and creates a final zip from the stapled app. |
| 123 | + |
| 124 | +## Validation Commands |
| 125 | + |
| 126 | +```bash |
| 127 | +security find-identity -p codesigning -v |
| 128 | +codesign -dvvv --entitlements :- apps/macos/dist/package/hmm.app |
| 129 | +codesign --verify --strict --verbose=4 apps/macos/dist/package/hmm.app |
| 130 | +xcrun stapler validate apps/macos/dist/package/hmm.app |
| 131 | +spctl -a -vv -t execute apps/macos/dist/package/hmm.app |
| 132 | +``` |
| 133 | + |
| 134 | +## CI Notes |
| 135 | + |
| 136 | +CI should not store a Developer ID private key unless release automation is |
| 137 | +explicitly required. The current CI workflow builds and packages unsigned |
| 138 | +artifacts only. To add signed CI releases later, store these as repository or |
| 139 | +environment secrets: |
| 140 | + |
| 141 | +- `MACOS_CERTIFICATE_P12_BASE64` |
| 142 | +- `MACOS_CERTIFICATE_PASSWORD` |
| 143 | +- `APPLE_NOTARY_KEY_P8_BASE64` |
| 144 | +- `APPLE_NOTARY_KEY_ID` |
| 145 | +- `APPLE_NOTARY_ISSUER_ID` for Team API Keys |
| 146 | + |
| 147 | +Create a temporary keychain inside the release job, import the certificate, |
| 148 | +run `apps/macos/script/package_signed.sh`, then run |
| 149 | +`apps/macos/script/notarize.sh`. |
| 150 | + |
| 151 | +## References |
| 152 | + |
| 153 | +- Apple Developer Program: https://developer.apple.com/programs/ |
| 154 | +- Developer ID certificates: https://developer.apple.com/help/account/certificates/create-developer-id-certificates/ |
| 155 | +- Create a private key: https://developer.apple.com/help/account/keys/create-a-private-key |
| 156 | +- Notarizing macOS software: https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution |
| 157 | +- Customizing notarization workflow: https://developer.apple.com/documentation/security/customizing-the-notarization-workflow |
0 commit comments