Logofpack

macOS: certificates and credentials

Create Developer ID certificates and notarization credentials, and export them for CI.

macOS:证书与凭证

创建 Developer ID 证书和公证凭证,并导出给 CI 使用。

Distributing a macOS app outside the App Store needs a Developer ID Application certificate (signs the .app, zip and DMG), optionally a Developer ID Installer certificate (signs the pkg), and one kind of notarization credential. All of it requires a paid Apple Developer Program membership, and Developer ID certificates can only be created by the team's Account Holder.

The macOS tutorial shows how to use them.

1. Create the Developer ID certificates#

Do this on a Mac. Pick one way.

In Xcode (easiest): Xcode → Settings… → Accounts → select the team → Manage Certificates… → + → Developer ID Application (and Developer ID Installer if you build pkgs). Certificate and private key go straight into the login keychain.

On the web: Keychain Access → Certificate Assistant → Request a Certificate From a Certificate Authority… (save the request to disk) → developer.apple.com/account/resources/certificates → + → Developer ID Application / Developer ID Installer → upload the request → download the .cer and double-click it — on the Mac that created the request, which holds the private key.

Check that they are installed:

security find-identity -v -p codesigning   # "Developer ID Application: Your Name (ABCDE12345)"
security find-identity -v -p basic         # also lists "Developer ID Installer: …"

Tell fpack the names exactly as printed; the ten characters in parentheses are your Team ID:

Purposefpack.yamlVariableFlag
App signing identity macos.sign.identity FPACK_MACOS_SIGN_IDENTITY --sign-identity
pkg signing identity macos.sign.installer_identity FPACK_MACOS_INSTALLER_IDENTITY --installer-identity

2. Create notarization credentials#

Pick one kind. fpack prefers them in this order: keychain profile, API key, Apple ID. Any of them turns notarization on.

a) Keychain profile (best on your own Mac)#

Apple ID + app-specific password + Team ID, stored once in the keychain.

  1. Open account.apple.com → Sign-In and Security → App-Specific Passwords and generate one (it looks like abcd-efgh-ijkl-mnop).

  2. Find your Team ID: developer.apple.com/account → Membership details, or the ten characters in the certificate name.

  3. Store it (omit --password to be prompted instead):

    xcrun notarytool store-credentials fpack-notary \
      --apple-id you@example.com --team-id ABCDE12345 --password abcd-efgh-ijkl-mnop
    xcrun notarytool history --keychain-profile fpack-notary   # check
    
  4. Use it: macos.sign.notary_profile: fpack-notary (FPACK_MACOS_NOTARY_PROFILE, --notary-profile).

Without a stored profile you can pass the Apple ID directly: notary_apple_id, notary_team_id and notary_password (FPACK_NOTARY_APPLE_ID, FPACK_NOTARY_TEAM_ID, FPACK_NOTARY_PASSWORD). Keep the password in an environment variable or CI secret; fpack redacts it in logs, --dry-run output and NOTARIZATION.md.

b) App Store Connect API key (best for CI)#

Not tied to a person's Apple ID and revocable at any time.

  1. App Store Connect → Users and Access → Integrations → App Store Connect API → Team Keys → +, access Developer or higher.

  2. Download AuthKey_<KEY_ID>.p8. It can be downloaded only once, so keep it safe. Note the Key ID and the Issuer ID shown on the page (individual keys have no issuer; leave it out).

  3. Tell fpack:

    Valuefpack.yamlVariable
    path of the .p8 filemacos.sign.notary_api_keyFPACK_NOTARY_API_KEY
    Key IDmacos.sign.notary_api_key_idFPACK_NOTARY_API_KEY_ID
    Issuer IDmacos.sign.notary_api_issuerFPACK_NOTARY_API_ISSUER

    Check: xcrun notarytool history --key AuthKey_ABC123DEF4.p8 --key-id ABC123DEF4 --issuer <Issuer ID>.

3. Export the certificates (.p12) for CI#

A CI runner has no keychain with your certificates; you import them from secrets.

  1. Keychain Access → login keychain → My Certificates → select "Developer ID Application: …" (expand it: the private key must be underneath) → File → Export Items… → format Personal Information Exchange (.p12) → choose an export password. Repeat for "Developer ID Installer: …" if you build pkgs (or select both and export one file).

  2. Base64-encode it into CI secrets (for example MACOS_CERTS_P12_BASE64 and MACOS_CERTS_P12_PASSWORD); the API key likewise (NOTARY_API_KEY_P8_BASE64):

    base64 -i DeveloperID.p12 | pbcopy
    base64 -i AuthKey_ABC123DEF4.p8 | pbcopy
    
  3. Import them into a temporary keychain on the runner and build (GitHub Actions):

    - name: import Developer ID certificates
      env:
        P12_BASE64: ${{ secrets.MACOS_CERTS_P12_BASE64 }}
        P12_PASSWORD: ${{ secrets.MACOS_CERTS_P12_PASSWORD }}
        KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}   # any random string
        API_KEY_BASE64: ${{ secrets.NOTARY_API_KEY_P8_BASE64 }}
      run: |
        KEYCHAIN="$RUNNER_TEMP/signing.keychain-db"
        echo "$P12_BASE64" | base64 --decode > "$RUNNER_TEMP/certs.p12"
        security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
        security set-keychain-settings -lut 21600 "$KEYCHAIN"
        security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
        security import "$RUNNER_TEMP/certs.p12" -k "$KEYCHAIN" -P "$P12_PASSWORD" \
          -T /usr/bin/codesign -T /usr/bin/productbuild -T /usr/bin/pkgbuild
        security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
        security list-keychains -d user -s "$KEYCHAIN" $(security list-keychains -d user | tr -d '"')
        security find-identity -v -p codesigning "$KEYCHAIN"
        echo "$API_KEY_BASE64" | base64 --decode > "$RUNNER_TEMP/AuthKey.p8"
        rm "$RUNNER_TEMP/certs.p12"
    - name: fpack build macos dmg pkg
      env:
        FPACK_MACOS_SIGN_IDENTITY: "Developer ID Application: Your Name (ABCDE12345)"
        FPACK_MACOS_INSTALLER_IDENTITY: "Developer ID Installer: Your Name (ABCDE12345)"
        FPACK_NOTARY_API_KEY: ${{ runner.temp }}/AuthKey.p8
        FPACK_NOTARY_API_KEY_ID: ${{ secrets.NOTARY_API_KEY_ID }}
        FPACK_NOTARY_API_ISSUER: ${{ secrets.NOTARY_API_ISSUER }}
      run: fpack build macos dmg pkg
    - name: clean up keychain
      if: always()
      run: security delete-keychain "$RUNNER_TEMP/signing.keychain-db" || true
    

    set-key-partition-list lets codesign and productbuild use the private key without a prompt; -lut 21600 keeps the keychain unlocked for six hours.

Danger

Never commit certificates, .p12 / .p8 files or passwords. Keep them in CI secrets. fpack redacts passwords in all output, but it cannot protect a file you commit.

Next#

Back to the macOS tutorial, or on to provisioning profiles if your app uses push, iCloud or other restricted entitlements.

在 App Store 之外分发 macOS 应用需要:Developer ID Application 证书(给 .app、zip 和 DMG 签名)、可选的 Developer ID Installer 证书(给 pkg 签名),以及一种公证凭证。这些都需要付费的 Apple Developer Program 账号,而且 Developer ID 证书只能由团队的**账号持有者(Account Holder)**创建。

怎么使用它们见 macOS 教程。

1. 创建 Developer ID 证书#

在 Mac 上操作,二选一。

在 Xcode 里(最简单):Xcode → Settings… → Accounts → 选择团队 → Manage Certificates… → + → Developer ID Application(要构建 pkg 就再加 Developer ID Installer)。证书和私钥会直接进入登录钥匙串。

在网页上:钥匙串访问 → 证书助理 → 从证书颁发机构请求证书…(把请求文件存到磁盘)→ developer.apple.com/account/resources/certificates → + → Developer ID Application / Developer ID Installer → 上传请求文件 → 下载 .cer 并双击,必须在创建请求的那台 Mac 上操作,因为私钥在那里。

检查是否已安装:

security find-identity -v -p codesigning   # "Developer ID Application: Your Name (ABCDE12345)"
security find-identity -v -p basic         # 同时列出 "Developer ID Installer: …"

把名称按打印的原样告诉 fpack;括号里的十位字符就是你的 Team ID:

用途fpack.yaml变量参数
App 签名证书 macos.sign.identity FPACK_MACOS_SIGN_IDENTITY --sign-identity
pkg 签名证书 macos.sign.installer_identity FPACK_MACOS_INSTALLER_IDENTITY --installer-identity

2. 创建公证凭证#

三选一。fpack 的优先顺序是:钥匙串配置、API 密钥、Apple ID。任何一种都会开启公证。

a) 钥匙串配置(自己的 Mac 上最推荐)#

Apple ID + App 专用密码 + Team ID,一次性存进钥匙串。

  1. 打开 account.apple.com → 登录与安全 → App 专用密码,生成一个(形如 abcd-efgh-ijkl-mnop)。

  2. 查 Team ID:developer.apple.com/account → 会员资格详情,或证书名称里的十位字符。

  3. 存储(省略 --password 会改为提示输入):

    xcrun notarytool store-credentials fpack-notary \
      --apple-id you@example.com --team-id ABCDE12345 --password abcd-efgh-ijkl-mnop
    xcrun notarytool history --keychain-profile fpack-notary   # 检查
    
  4. 使用:macos.sign.notary_profile: fpack-notary(FPACK_MACOS_NOTARY_PROFILE、--notary-profile)。

没有存储配置时,也可以直接传 Apple ID:notary_apple_id、notary_team_id 和 notary_password(FPACK_NOTARY_APPLE_ID、FPACK_NOTARY_TEAM_ID、FPACK_NOTARY_PASSWORD)。密码请放在环境变量或 CI 密钥中;fpack 会在日志、--dry-run 输出和 NOTARIZATION.md 里隐藏它。

b) App Store Connect API 密钥(CI 最推荐)#

不绑定某个人的 Apple ID,可随时吊销。

  1. App Store Connect → 用户和访问 → 集成 → App Store Connect API → 团队密钥 → +,访问权限选 Developer 或更高。

  2. 下载 AuthKey_<KEY_ID>.p8。它只能下载一次,请妥善保管。记下页面上显示的 Key ID 和 Issuer ID(个人密钥没有 issuer,不要填)。

  3. 告诉 fpack:

    值fpack.yaml变量
    .p8 文件路径macos.sign.notary_api_keyFPACK_NOTARY_API_KEY
    Key IDmacos.sign.notary_api_key_idFPACK_NOTARY_API_KEY_ID
    Issuer IDmacos.sign.notary_api_issuerFPACK_NOTARY_API_ISSUER

    检查:xcrun notarytool history --key AuthKey_ABC123DEF4.p8 --key-id ABC123DEF4 --issuer <Issuer ID>。

3. 为 CI 导出证书(.p12)#

CI 机器的钥匙串里没有你的证书,需要从密钥(secret)里导入。

  1. 钥匙串访问 → 登录钥匙串 → 我的证书 → 选中 “Developer ID Application: …”(展开它:下面必须有私钥)→ 文件 → 导出项目… → 格式选 个人信息交换(.p12) → 设置导出密码。要构建 pkg 的话,对 “Developer ID Installer: …” 重复一次(或两个一起选中导出成一个文件)。

  2. 把它 base64 后放进 CI 密钥(例如 MACOS_CERTS_P12_BASE64 和 MACOS_CERTS_P12_PASSWORD);API 密钥同理(NOTARY_API_KEY_P8_BASE64):

    base64 -i DeveloperID.p12 | pbcopy
    base64 -i AuthKey_ABC123DEF4.p8 | pbcopy
    
  3. 在运行机器上导入到临时钥匙串并构建(GitHub Actions):

    - name: import Developer ID certificates
      env:
        P12_BASE64: ${{ secrets.MACOS_CERTS_P12_BASE64 }}
        P12_PASSWORD: ${{ secrets.MACOS_CERTS_P12_PASSWORD }}
        KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}   # 任意随机字符串
        API_KEY_BASE64: ${{ secrets.NOTARY_API_KEY_P8_BASE64 }}
      run: |
        KEYCHAIN="$RUNNER_TEMP/signing.keychain-db"
        echo "$P12_BASE64" | base64 --decode > "$RUNNER_TEMP/certs.p12"
        security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
        security set-keychain-settings -lut 21600 "$KEYCHAIN"
        security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
        security import "$RUNNER_TEMP/certs.p12" -k "$KEYCHAIN" -P "$P12_PASSWORD" \
          -T /usr/bin/codesign -T /usr/bin/productbuild -T /usr/bin/pkgbuild
        security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
        security list-keychains -d user -s "$KEYCHAIN" $(security list-keychains -d user | tr -d '"')
        security find-identity -v -p codesigning "$KEYCHAIN"
        echo "$API_KEY_BASE64" | base64 --decode > "$RUNNER_TEMP/AuthKey.p8"
        rm "$RUNNER_TEMP/certs.p12"
    - name: fpack build macos dmg pkg
      env:
        FPACK_MACOS_SIGN_IDENTITY: "Developer ID Application: Your Name (ABCDE12345)"
        FPACK_MACOS_INSTALLER_IDENTITY: "Developer ID Installer: Your Name (ABCDE12345)"
        FPACK_NOTARY_API_KEY: ${{ runner.temp }}/AuthKey.p8
        FPACK_NOTARY_API_KEY_ID: ${{ secrets.NOTARY_API_KEY_ID }}
        FPACK_NOTARY_API_ISSUER: ${{ secrets.NOTARY_API_ISSUER }}
      run: fpack build macos dmg pkg
    - name: clean up keychain
      if: always()
      run: security delete-keychain "$RUNNER_TEMP/signing.keychain-db" || true
    

    set-key-partition-list 让 codesign 和 productbuild 无需弹窗就能使用私钥;-lut 21600 让钥匙串保持解锁六小时。

危险

绝不要提交证书、.p12 / .p8 文件或密码,把它们放在 CI 密钥里。fpack 会在所有输出里隐藏密码,但无法保护你提交进仓库的文件。

下一步#

回到 macOS 教程;如果你的应用使用推送、iCloud 或其他受限 entitlements,继续看描述文件。