macOS pkg 安装包签名与公证指南

1,868 阅读7分钟

在 macOS 上分发应用程序(尤其是通过 .pkg 安装包且在 Mac App Store 之外分发时),进行代码签名和公证是确保用户信任和绕过 Gatekeeper 安全检查的关键步骤。本指南将详细介绍如何签名和公证 .pkg 文件。

目录

  1. 简介
  2. 先决条件
  3. 步骤 1:对 .pkg 文件进行签名
  4. 步骤 2:公证已签名的 .pkg 文件
  5. 步骤 3:验证签名和公证
  6. 常见问题与故障排除

简介

  • 签名 (Signing): 确认软件来源是你(开发者),并保证自签名后代码未被篡改。使用你的 Developer ID 证书完成。
  • 公证 (Notarization): Apple 对你的软件进行自动化安全扫描的过程。通过公证的软件,Gatekeeper 会告知用户该软件已经过 Apple 检查,不包含已知恶意内容。这对在 macOS Catalina (10.15) 及更高版本上运行至关重要。

签名是公证的前提。你需要先对 .pkg 安装包进行签名,然后才能将其提交给 Apple 进行公证。

先决条件

  1. Apple Developer Program 成员资格: 你需要是付费的 Apple Developer Program 成员。
  2. Developer ID 证书:
    • Developer ID Application: 用于签名应用程序本身。
    • Developer ID Installer: 用于签名 .pkg 安装包。你需要从 Apple Developer 网站下载并安装到你的钥匙串中。
  3. Xcode: 需要安装最新版本的 Xcode,因为它包含了必要的命令行工具(如 productsign, notarytool, altool, stapler)。
    • 确保 Xcode 命令行工具已安装并选择:xcode-select --installsudo xcode-select -s /Applications/Xcode.app/Contents/Developer
  4. App-Specific Password 或 API Key:
    • 对于 altool (旧版): 需要在 appleid.apple.com 为你的 Apple ID 生成一个 App-Specific Password。
    • 对于 notarytool (推荐): 建议在 App Store Connect (用户和访问 -> 密钥 -> App Store Connect API) 创建 API Key (选择 "App Manager" 或更高权限)。这比 App-Specific Password 更安全、更灵活。或者,你也可以使用你的 App Store Connect 账户凭据(Apple ID 和 App-Specific Password),但不推荐。
  5. 稳定的网络连接: 公证过程需要上传文件到 Apple 服务器。

步骤 1:对 .pkg 文件进行签名

使用 productsign 命令行工具和你的 Developer ID Installer 证书来签名 .pkg 文件。注意,这个证书,只有持有人账号才有权限申请。

# 语法: productsign --sign "证书名称" 输入文件路径 输出文件路径

# 示例:
productsign --sign "Developer ID Installer: Your Company Name (YOUR_TEAM_ID)" \
            /path/to/your/unsigned_installer.pkg \
            /path/to/your/signed_installer.pkg
  • "Developer ID Installer: Your Company Name (YOUR_TEAM_ID)": 替换为你在钥匙串访问 (Keychain Access) 中看到的 完整 的 Developer ID Installer 证书名称。Team ID 通常是括号里的那串字母和数字。
  • /path/to/your/unsigned_installer.pkg: 你构建的未签名 .pkg 文件的路径。
  • /path/to/your/signed_installer.pkg: 你希望保存签名后 .pkg 文件的路径。

验证签名:

# 方法 1: 使用 spctl
spctl -a -vv -t install /path/to/your/signed_installer.pkg
# 预期输出应包含: source=Developer ID

# 方法 2: 使用 pkgutil
pkgutil --check-signature /path/to/your/signed_installer.pkg
# 预期输出应包含证书信息和状态

步骤 2:公证已签名的 .pkg 文件

签名完成后,将 signed_installer.pkg 提交给 Apple 进行公证。推荐使用 notarytool

方法 A:使用 notarytool (推荐)

notarytool 是 Apple 推荐的用于与公证服务交互的新工具,比 altool 更快、提供更清晰的反馈。

  1. 存储凭证 (推荐方法: API Key): 将从 App Store Connect 下载的 API Key ( .p8 文件) 和相关信息 (Issuer ID, Key ID) 安全地存储在钥匙串中。

    # 将你的 App Store Connect API Key 添加到钥匙串
    # ProfileName 可以是任意命名的名字,例如 "MyNotaryToolProfile"
    xcrun notarytool store-credentials "MyProjectNotaryKey" \
                     --key "/path/to/AuthKey_YOUR_KEY_ID.p8" \
                     --key-id "YOUR_KEY_ID" \
                     --issuer "YOUR_ISSUER_ID"
    
    • YOUR_KEY_ID: API Key ID.
    • YOUR_ISSUER_ID: Issuer ID.
    • /path/to/your/AuthKey_YOUR_KEY_ID.p8: 下载的 .p8 文件路径。

    注意这个步骤只是第一次使用需要用到,以后直接跳到第二步提交公证即可。

  2. 提交 .pkg 进行公证:

    # 使用存储的凭证提交并等待结果
    xcrun notarytool submit /path/to/your/signed_installer.pkg \
                         --keychain-profile "YourProfileName" \
                         --wait
    
    • YourProfileName: 是你在第一步中存储的凭证名称。
    • --wait: 命令会等待公证完成。如果公证过程较长,你也可以去掉 --wait,稍后手动检查状态。
    • 提交成功后,会返回一个 Submission ID
  3. 检查状态 (如果未使用 --wait):

    # 使用 Submission ID 检查状态
    xcrun notarytool info <Submission-ID> --keychain-profile "YourProfileName"
    
    # 查看最近的提交历史
    xcrun notarytool history --keychain-profile "YourProfileName"
    
    # 查看特定提交的日志 (获取更详细的错误信息)
    xcrun notarytool log <Submission-ID> --keychain-profile "YourProfileName"
    

    如果状态为 AcceptedSuccess,则公证成功。使用 notarytool 提交并成功后,Apple 会 自动 尝试将公证票据附加 (staple) 到你的 .pkg 文件上。你通常不需要手动执行 xcrun stapler staple

方法 B:使用 altool (旧版)

altool 是旧版工具,虽然仍可用,但 Apple 已推荐迁移到 notarytool

  1. 提交 .pkg 进行公证:

    # 使用 App-Specific Password (直接提供或通过钥匙串)
    xcrun altool --notarize-app \
                 -f /path/to/your/signed_installer.pkg \
                 --primary-bundle-id "com.yourcompany.yourproduct.installer" \
                 -u "your_apple_id@example.com" \
                 -p "@keychain:YourAltoolPasswordItem" # 或直接写密码 "xxxx-xxxx-..."
                 # --asc-provider "YOUR_TEAM_SHORT_NAME" # 如果你的账户属于多个团队,可能需要此项
    
    # 示例 (使用存储在钥匙串中的密码):
    # 假设你已将 App-Specific Password 存储在名为 "AltoolNotaryPassword" 的钥匙串项中
    # xcrun altool --notarize-app \
    #              -f signed_installer.pkg \
    #              --primary-bundle-id "com.example.myapp.pkg" \
    #              -u "developer@example.com" \
    #              -p "@keychain:AltoolNotaryPassword"
    
    • --primary-bundle-id: 提供一个唯一的标识符,通常是你应用 Bundle ID 的变体,用于跟踪公证请求。
    • -u: 你的 Apple Developer 账户邮箱。
    • -p: App-Specific Password。推荐使用 @keychain:ItemName 的方式从钥匙串安全地读取,或者 @env:ENV_VARIABLE_NAME 从环境变量读取。直接在命令行写密码非常不安全。
    • --asc-provider: 如果你的 Apple ID 关联了多个开发团队,你需要指定团队的短名称 (可在 App Store Connect 查看)。
    • 提交成功后,会返回一个 RequestUUID
  2. 检查状态:

    # 使用 RequestUUID 检查特定请求的状态
    xcrun altool --notarization-info <RequestUUID> \
                 -u "your_apple_id@example.com" \
                 -p "@keychain:YourAltoolPasswordItem" # 或 "xxxx-xxxx-..."
    
    # 查看最近的公证历史
    xcrun altool --notarization-history 0 \
                 -u "your_apple_id@example.com" \
                 -p "@keychain:YourAltoolPasswordItem" # 或 "xxxx-xxxx-..."
    

    等待状态变为 Success。如果状态是 Invalid,请检查日志 URL 获取详细错误信息。

  3. 装订 (Staple) 公证票据: 使用 altool 成功公证后,必须 手动将 Apple 返回的公证票据附加 (staple) 到你的 .pkg 文件上。这样即使用户离线,Gatekeeper 也能验证公证状态。

    xcrun stapler staple /path/to/your/signed_installer.pkg
    

    这个命令会修改 signed_installer.pkg 文件,将票据嵌入其中。现在这个文件就是最终可分发的文件了。

步骤 3:验证签名和公证

最后,验证最终的 .pkg 文件是否已正确签名和公证,并且票据已装订。

# 验证签名、公证和装订状态
spctl -a -vv -t install /path/to/your/final_installer.pkg
  • 对于 已签名且已公证并成功装订.pkg 文件,预期输出应包含:
    • source=Notarized Developer ID
    • origin=Developer ID Installer: Your Company Name (YOUR_TEAM_ID)

如果只显示 source=Developer ID,表示签名成功但公证/装订可能未完成或失败。

常见问题与故障排除

  • 证书无效/找不到: 确保 Developer ID Installer 证书已正确安装在你的登录钥匙串中,并且未过期。检查 productsign 命令中的证书名称是否与钥匙串中的完全一致。
  • 公证失败 (Invalid Status): 使用 notarytool log <Submission-ID>altool --notarization-info <RequestUUID> 获取日志 URL,查看详细的失败原因。常见原因包括:
    • .pkg 包内的某个二进制文件未签名或签名无效。
    • 使用了不安全的 API。
    • 缺少强化的运行时 (Hardened Runtime) - 虽然对 .pkg 本身不强制,但其包含的 App 或二进制文件通常需要。
  • Stapler 失败: 检查网络连接。确保公证确实成功完成。有时 Apple 的服务器需要一点时间才能提供票据。
  • altool 认证失败: 确认使用的是 App-Specific Password 而不是你的常规 Apple ID 密码。检查密码是否正确或过期。如果使用 @keychain,确保钥匙串项名称无误。
  • notarytool 凭证问题: 确认 API Key (.p8), Key ID, Issuer ID, Team ID 都正确无误,并且 API Key 在 App Store Connect 中是激活状态。

完成这些步骤后,你的 .pkg 安装包就准备好分发给用户了,他们将能够更顺畅地安装你的软件。