14、Electron 专题(macOS 代码签名)

基本概念

什么是代码签名

代码签名(Code Signing)是 macOS 系统中的一种安全机制,用于验证应用程序的完整性和来源。通过数字签名,可以确保:

  1. 完整性验证:确认应用程序没有被篡改
  2. 身份验证:验证应用程序的开发者身份
  3. 信任建立:让用户和系统信任应用程序
  4. 分发安全:确保应用程序分发的安全性

为什么需要代码签名

在 macOS 系统中,未签名的应用程序会遇到以下问题:

  1. Gatekeeper 阻止:系统会显示安全警告,阻止应用运行
  2. 用户信任度低:用户对未签名应用缺乏信任
  3. 分发限制:无法通过正常渠道分发应用
  4. 系统功能限制:某些系统功能无法使用

签名原理

数字签名技术

代码签名基于公钥密码学原理:

  1. 私钥签名:开发者使用私钥对应用进行签名
  2. 公钥验证:系统和用户使用公钥验证签名
  3. 证书链:通过证书链验证开发者身份
  4. 时间戳:确保签名的时效性

签名内容

macOS 代码签名会对以下内容进行签名:

  1. 可执行文件:应用的主执行文件和所有二进制文件
  2. 资源文件:应用包内的所有资源文件
  3. 元数据:应用的配置信息和权限声明
  4. 依赖关系:应用依赖的框架和库

签名流程

1. 开发者证书申请

首先,加入 Apple Developer Program:注册开发者账号(需支付年费约 $99)

# 在 Apple Developer 网站申请证书
# 1. 登录 https://developer.apple.com
# 2. 进入 Certificates, Identifiers & Profiles
# 3. 创建 Developer ID Application 证书

2. 证书安装

# 下载证书文件 (.cer)
# 双击安装到钥匙串访问

# 查看已安装的证书
security find-identity -v -p codesigning

3. 应用签名(针对 macOS)

以下是 macOS 应用签名的方式,但是 electron 应用有自己的一套签名配置,但是其底层原理,和以下内容是一致的。

codesign 是 macOS 用于应用签名的官方命令工具。

codesign 是 macOS 开发者工具链中的核心命令,主要用于对应用程序、安装包等文件进行签名。通过指定证书和权限配置,可生成符合苹果安全要求的数字签名,避免应用被系统识别为潜在风险。

# 基本签名命令
codesign --sign "Developer ID Application: Your Name (TEAM_ID)" \
         --options runtime \
         --entitlements entitlements.plist \
         YourApp.app

# 验证签名
codesign --verify --verbose YourApp.app

4. 公证(Notarization)

以下是 macOS 应用的公证方式,而针对 electron,它自动实现了一套公证方案,我们只需要进行简单配置即可,不过其原理和下面的命令公证是一样的。

# 上传应用到 Apple 进行公证
xcrun notarytool submit YourApp.zip \
       --apple-id "your-email@example.com" \
       --password "app-specific-password" \
       --team-id "TEAM_ID"

# 装订公证票据
xcrun stapler staple YourApp.app

Electron 中的代码签名配置

electron-builder 配置

electron-builder 配置,我们在前面的章节中,已经详细的讲解过。不过我这里要针对 macOS 签名、公证相关的字段进行简单说明。

  • identity,签名证书。
  • notarize,公证相关配置,一般需要配置三个字段。(teamId、appleId、appleIdPassword)
  • appBundleId,在 electron-builder 的配置中,appId 就是 appBundleId。

一般而言,我们也会在 afterSign 钩子中,进行公证配置。

electron-builder.ymlpackage.json 中配置代码签名:

# electron-builder.yml
appId: com.example.electrontodo

mac:
  # 代码签名身份
  identity: "Developer ID Application: Your Name (TEAM_ID)"

  # 启用加固运行时
  hardenedRuntime: true

  # 禁用 Gatekeeper 评估(用于开发测试)
  gatekeeperAssess: false

  # entitlements 文件路径
  entitlements: build/entitlements.mac.plist
  entitlementsInherit: build/entitlements.mac.plist

  # 公证配置
  notarize:
    teamId: "TEAM_ID"

package.json 配置

{
  "build": {
    "mac": {
      "identity": "Developer ID Application: Your Name (TEAM_ID)",
      "hardenedRuntime": true,
      "gatekeeperAssess": false,
      "entitlements": "build/entitlements.mac.plist",
      "entitlementsInherit": "build/entitlements.mac.plist",
      "notarize": {
        "teamId": "TEAM_ID"
      }
    }
  }
}

环境变量配置

为了安全起见,建议使用环境变量存储敏感信息:

# .env 文件
APPLE_ID=your-email@example.com
APPLE_PASSWORD=app-specific-password
TEAM_ID=YOUR_TEAM_ID
# electron-builder.yml
mac:
  notarize:
    teamId: "${TEAM_ID}"
    appleId: "${APPLE_ID}"
    appleIdPassword: "${APPLE_PASSWORD}"

证书类型详解

Developer ID Application

用于外部分发的应用程序:

# 证书名称格式
Developer ID Application: Your Name (TEAM_ID)

# 用途
- 外部分发(非 Mac App Store)
- 需要公证
- 用户下载后可直接运行

Mac App Store 证书

用于提交到 Mac App Store 的应用程序:

# 证书名称格式
3rd Party Mac Developer Application: Your Name (TEAM_ID)

# 用途
- Mac App Store 分发
- 不需要公证
- 必须通过 App Store 分发

开发证书

用于开发阶段的应用程序:

# 证书名称格式
Mac Developer: Your Name (TEAM_ID)

# 用途
- 开发测试
- 本地运行
- 调试应用

Entitlements 配置

基础 Entitlements

创建 build/entitlements.mac.plist 文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <!-- Electron 应用必需的权限 -->
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>

    <!-- 网络访问权限 -->
    <key>com.apple.security.network.client</key>
    <true/>

    <!-- 文件访问权限 -->
    <key>com.apple.security.files.user-selected.read-write</key>
    <true/>
  </dict>
</plist>

高级 Entitlements 配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <!-- Electron 核心权限 -->
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>

    <!-- 网络权限 -->
    <key>com.apple.security.network.client</key>
    <true/>
    <key>com.apple.security.network.server</key>
    <true/>

    <!-- 文件系统权限 -->
    <key>com.apple.security.files.user-selected.read-write</key>
    <true/>
    <key>com.apple.security.files.downloads.read-write</key>
    <true/>
    <key>com.apple.security.assets.pictures.read-only</key>
    <true/>

    <!-- 硬件权限 -->
    <key>com.apple.security.device.camera</key>
    <true/>
    <key>com.apple.security.device.microphone</key>
    <true/>

    <!-- 个人信息权限 -->
    <key>com.apple.security.personal-information.location</key>
    <true/>
  </dict>
</plist>

加固运行时 (Hardened Runtime)

什么是 Hardened Runtime

Hardened Runtime 是 macOS 的安全特性,对应用程序施加额外的安全限制:

  1. 代码完整性保护:防止代码注入攻击
  2. 库验证:确保加载的库是可信的
  3. 调试限制:限制调试器附加
  4. 内存保护:防止某些类型的内存攻击

启用 Hardened Runtime

# electron-builder.yml
mac:
  hardenedRuntime: true
  entitlements: build/entitlements.mac.plist
  entitlementsInherit: build/entitlements.mac.plist

Hardened Runtime 必需的 Entitlements

当启用 Hardened Runtime 时,必须明确声明应用需要的权限:

<!-- JIT 编译权限 - Electron 必需 -->
<key>com.apple.security.cs.allow-jit</key>
<true/>

<!-- 未签名可执行内存权限 - Electron 必需 -->
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>

<!-- 禁用库验证 - 加载原生模块需要 -->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>

公证 (Notarization)

什么是公证

公证是 Apple 对应用程序的安全检查过程:

  1. 恶意软件扫描:检查应用是否包含恶意代码
  2. 安全验证:验证应用的安全配置
  3. 兼容性检查:确保应用与 macOS 兼容
  4. 信任建立:为用户提供额外的安全保障

公证流程

1. 创建应用专用密码
# 在 Apple ID 账户设置中创建应用专用密码
# https://appleid.apple.com/account/manage
2. 配置公证
# electron-builder.yml
mac:
  notarize:
    teamId: "TEAM_ID"
    appleId: "your-email@example.com"
    appleIdPassword: "app-specific-password"
3. 自动公证
# electron-builder 会自动处理公证流程
npm run build:mac

手动公证流程

如果需要手动控制公证过程:

# 1. 创建 ZIP 文件
ditto -c -k --keepParent YourApp.app YourApp.zip

# 2. 提交公证
xcrun notarytool submit YourApp.zip \
       --apple-id "your-email@example.com" \
       --password "app-specific-password" \
       --team-id "TEAM_ID"

# 3. 检查公证状态
xcrun notarytool info "submission-id" \
       --apple-id "your-email@example.com" \
       --password "app-specific-password" \
       --team-id "TEAM_ID"

# 4. 装订公证票据
xcrun stapler staple YourApp.app

# 5. 验证装订
xcrun stapler validate YourApp.app

签名验证

验证签名状态

# 基本验证
codesign --verify --verbose YourApp.app

# 详细验证信息
codesign --verify --verbose=4 YourApp.app

# 显示签名信息
codesign --display --verbose YourApp.app

# 显示 entitlements
codesign --display --entitlements :- YourApp.app

验证公证状态

# 验证公证票据
spctl --assess --verbose --type execute YourApp.app

# 检查公证装订
xcrun stapler validate YourApp.app

验证 Gatekeeper

# Gatekeeper 评估
spctl --assess --type execute YourApp.app

# 详细 Gatekeeper 信息
spctl --assess --verbose --type execute YourApp.app

常见问题与解决方案

1. 签名失败

问题:应用签名失败

解决方案

# 检查证书是否有效
security find-identity -v -p codesigning

# 检查证书是否过期
security find-certificate -c "Developer ID Application" -p | openssl x509 -text -noout

# 重新安装证书
security import certificate.cer -k ~/Library/Keychains/login.keychain

2. 公证失败

问题:公证过程中失败

解决方案

# 检查应用专用密码
# 确保密码正确且未过期

# 检查网络连接
# 确保能够访问 Apple 服务器

# 查看详细错误信息
xcrun notarytool info "submission-id" \
       --apple-id "your-email@example.com" \
       --password "app-specific-password" \
       --team-id "TEAM_ID"

3. Gatekeeper 阻止

问题:用户下载后无法运行应用

解决方案

# 确保应用已签名
codesign --verify --verbose YourApp.app

# 确保应用已公证
spctl --assess --verbose --type execute YourApp.app

# 装订公证票据
xcrun stapler staple YourApp.app

4. Entitlements 错误

问题:应用因权限问题崩溃

解决方案

# 检查 entitlements 配置
codesign --display --entitlements :- YourApp.app

# 确保所有必需权限都已声明
# 特别是 Electron 应用需要的三个核心权限

最佳实践

1. 证书管理

  • 定期更新证书:在证书过期前及时更新
  • 安全存储私钥:使用安全的密钥管理
  • 备份证书:保留证书的备份
  • 团队协作:建立证书共享机制

2. 签名流程

  • 自动化签名:使用 CI/CD 自动化签名流程
  • 测试验证:在签名后立即验证
  • 版本管理:为不同版本维护不同的签名配置
  • 文档记录:记录签名配置和流程

3. 安全考虑

  • 最小权限原则:只申请必需的权限
  • 定期审查:定期审查 entitlements 配置
  • 安全测试:在不同 macOS 版本上测试
  • 监控更新:关注 Apple 政策变化

4. 分发策略

  • 多渠道分发:支持多种分发方式
  • 用户指导:为用户提供安装指导
  • 技术支持:建立技术支持机制
  • 反馈收集:收集用户反馈并改进

总结

macOS 代码签名是 Electron 应用分发的重要环节,正确配置代码签名可以:

  1. 提高用户信任度:让用户相信应用的安全性
  2. 确保应用完整性:防止应用被篡改
  3. 满足系统要求:通过 Gatekeeper 检查
  4. 支持自动更新:为应用更新提供安全保障

通过合理配置代码签名、entitlements 和公证流程,可以确保 Electron 应用在 macOS 上的安全分发和运行。记住要遵循 Apple 的最佳实践,定期更新证书和配置,以保持应用的安全性和兼容性。

结语

技术学习是一场持续的旅程,我很荣幸能与你同行。关注这个专栏,让我们共同探索 Electron 的奥秘,在实践中成长,在交流中进步。期待与你一起,从基础概念到项目实战,一步步构建出优秀的桌面应用程序!

关于本专栏

本专栏的所有内容和代码,github地址:https://github.com/techLaoLi/electron-tutorial
关于作者,前大厂技术专家,现大前端技术负责人,公众号:老李说技术。 欢迎大家的关注。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老李说技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值