macOS 应用签名

代码签名是一种安全技术,用于证明应用是由您创建的。

在 macOS 上,应用分发存在两层安全技术:代码签名公证

  • 代码签名是指验证应用作者身份并确保其在分发前未被篡改的行为。

  • 公证是额外的验证步骤,其中应用会被发送到 Apple 服务器进行自动恶意软件扫描。

从 macOS 10.15(Catalina)开始,您的应用需要同时进行代码签名和公证,才能在用户的机器上运行,而无需禁用额外的操作系统安全检查。

Mac App Store (MAS) 应用除外,因为 MAS 提交过程会进行类似的自动检查,因此不需要公证。

先决条件

安装 Xcode

Xcode 是 Apple 用于在 macOS、iOS 和其他平台上进行开发的集成开发环境 (IDE)。

虽然 Electron 本身并没有与 IDE 紧密集成,但 Xcode 是安装代码签名证书(见下一节)的有用工具,并且是公证所必需的

获取签名证书

macOS 应用的代码签名证书只能通过 Apple 获取,方法是购买Apple Developer Program 的会员资格。

要签名 Electron 应用,您可能需要两个单独的证书

  • Developer ID Installer 证书用于分发到 Mac App Store 的应用。

  • Developer ID Application 证书用于分发到 Mac App Store 之外的应用。

拥有 Apple Developer Program 会员资格后,您首先需要将它们安装到您的机器上。我们建议通过 Xcode 加载它们

验证证书是否已安装

安装证书后,您可以使用以下 shell 命令在终端中检查可用的代码签名证书

security find-identity -p codesigning -v

配置 Forge

在 Electron Forge 中,macOS 应用在打包步骤中由 electron-packager 库进行签名和公证。在您的 Forge packagerConfig 中为每个设置都有一个单独的选项。

osxSign 选项

在后台,Electron Forge 使用 @electron/osx-sign 工具来对您的 macOS 应用进行签名。

要在 macOS 上启用代码签名,请确保 packagerConfig.osxSign 存在于您的 Forge 配置中。

forge.config.js
module.exports = {
  packagerConfig: {
    osxSign: {} // object must exist even if empty
  }
};

osxSign 配置具有开箱即用的默认值,因此我们建议您从空的配置对象开始。

有关完整配置选项列表,请参阅 Forge API 文档中的 OsxSignOptions 类型。有关如何配置这些选项的更详细的信息,请参阅 @electron/osx-sign 文档

自定义授权

修改默认 osxSign 配置的一个常见用例是自定义其授权。在 macOS 中,授权是授予应用某些功能的权限(例如访问相机、麦克风或 USB 设备)。这些权限存储在应用可执行文件的代码签名中的授权文件中。

默认情况下,@electron/osx-sign 工具带有一组授权,这些授权应该适用于 MAS 或直接分发目标。请参阅 GitHub 上的完整默认授权文件集

forge.config.js
module.exports = {
  // ...
  packagerConfig: {
    // ...
    osxSign: {
      optionsForFile: (filePath) => {
        // Here, we keep it simple and return a single entitlements.plist file.
        // You can use this callback to map different sets of entitlements
        // to specific files in your packaged app.
        return {
          entitlements: 'path/to/entitlements.plist'
        };
      }
    }
  }
  // ...
};

有关授权的更多信息,请参阅 Apple 开发人员文档中的以下页面

osxNotarize 选项

在后台,Electron Forge 使用 @electron/notarize 工具来对您的 macOS 应用进行公证。

notarytool 命令有三个身份验证选项,如下所述。请注意,您需要使用 forge.config.js 配置,以便您可以将环境变量加载到您的 Forge 配置中。

保持您的身份验证信息私密

您永远不应该在配置中以明文形式存储身份验证信息。在下面的示例中,凭据作为环境变量存储,并通过 Node.js process.env 对象访问。

选项 1:使用应用专用密码

您可以从 Apple 生成一个应用专用密码,以将您的凭据提供给 notarytool。如果您更改了 Apple ID 密码,则需要重新生成此密码。

如果您使用此策略,则 osxNotarize 有两个必填字段

字段类型描述

appleId

字符串

与您的 Apple Developer 帐户关联的 Apple ID

appleIdPassword

字符串

应用专用密码

teamId

字符串

您想要在其下进行公证的 Apple 团队 ID。您可以通过访问https://developer.apple.com/account/#/membership来查找您所属团队的团队 ID。

forge.config.js
module.exports = {
  // ...
  packagerConfig: {
    // ...
    osxNotarize: {
      appleId: process.env.APPLE_ID,
      appleIdPassword: process.env.APPLE_PASSWORD,
      teamId: process.env.APPLE_TEAM_ID
    }
  }
  // ...
};

尽管名称如此,appleIdPassword并非您 Apple ID 帐户的密码。

选项 2:使用 App Store Connect API 密钥

您可以生成 App Store Connect API 密钥来对notarytool进行身份验证,方法是访问App Store Connect 访问页面并使用“团队密钥”选项卡。此 API 密钥看起来类似于AuthKey_ABCD123456.p8,并且只能下载一次。

如果您使用此策略,则osxNotarize有三个必填字段。

字段类型描述

appleApiKey

字符串

指向 API 密钥文件的系统文件路径字符串。

appleApiKeyId

字符串

10 个字符的字母数字 ID 字符串。在前面的AuthKey_ABCD123456.p8示例中,这将是ABCD123456

appleApiIssuer

字符串

识别 API 密钥发行者的 UUID。您可以在生成 API 密钥的“密钥”选项卡中找到此 ID。

forge.config.js
module.exports = {
  // ...
  packagerConfig: {
    // ...
    osxNotarize: {
      appleApiKey: process.env.APPLE_API_KEY,
      appleApiKeyId: process.env.APPLE_API_KEY_ID,
      appleApiIssuer: process.env.APPLE_API_ISSUER
    }
  }
  // ...
};

选项 3:使用钥匙串

您可以选择使用包含任一组凭据(上述选项 1 或选项 2)的 macOS钥匙串,而不是向传递给notarytool的 Forge 配置传递环境变量。

您可以通过notarytool store-credentials命令直接在终端中执行此操作。有关用法信息,您可以参考notarytool的手册页。

man notarytool

如果您使用此策略,则osxNotarize有两个可用字段。

字段类型描述

keychainProfile

字符串

包含您的公证凭据的钥匙串配置文件的名称。

keychain(可选)

字符串

包含具有您的凭据的配置文件的钥匙串的名称(或路径)。

请注意,如果您使用notarytool store-credentials,则可以自动检测keychain参数。

forge.config.js
module.exports = {
  // ...
  packagerConfig: {
    // ...
    osxNotarize: {
      keychainProfile: 'my-keychain-profile'
    }
  }
  // ...
};

示例配置

以下是osxSignosxNotarize的最小 Forge 配置。

forge.config.js
module.exports = {
  packagerConfig: {
    osxSign: {},
    osxNotarize: {
      appleId: process.env.APPLE_ID,
      appleIdPassword: process.env.APPLE_PASSWORD,
      teamId: process.env.APPLE_TEAM_ID
    }
  }
};

上次更新于