Skip to content
On this page

Electron 应用自动化构建

前言

作为一位前端开发对 Electron 一定不会陌生,Electron 的应用目前来说越来越多,作为程序员最熟悉的应该就是 VSCode。

使用 Web 技术栈即可构建一个跨平台的桌面客户端可谓十分方便,如果你正在开发一款 Electron 应用,并打算将其打包发布,那你就应该为其添加代码签名。但是对于 Electron 上线发布来说,如何自动化的打包和签名 (Windows & macOS 需要签名) 来提高我们的生产力和减少人为产生的 Bug 是一个非常重要的问题

接下去我们结合一款最高 30w+ 日活的桌面产品聊聊如何做自动化的打包和签名

以下 Electron 的打包库我们使用 electron-builder (签名和公证等流程其他打包库同样适用)

macOS

macOS 的打包签名必须使用 macOS 系统完成,除了这一要求外,还需要一下两个额外条件

  1. 加入 Apple Developer Program(需要缴纳年费)
  2. 生成,下载,然后安装 签名证书(signing certificates)

签名

对于 electron-builder 来说,macOS 的代码签名我们只需提供 CSC_LINKCSC_KEY_PASSWORD 这两个信息即可(官方文档: https://www.electron.build/code-signing)

shell
# 提供证书信息
export CSC_LINK="/path/to/certificate"
export CSC_KEY_PASSWORD="xxx"

# 开始打包 ...
yarn install
yarn build
  • CSC_LINK 指向了签名证书的路径(可以是 https 或者 file:// 或者本地路径)
  • CSC_KEY_PASSWORD 是上面提供的证书的密码

签名这个步骤的详细流程可以在 electron-builder 的开源代码中找到 packages/app-builder-lib/src/codeSign/macCodeSign.ts 里面找到

大致流程是通过提供的签名证书创建一个 keychain (即钥匙串), 然后通过 codesign 命令调用新创建的钥匙串进行代码签名

1. 提供证书变量

shell
export CSC_LINK="/path/to/certificate"
export CSC_KEY_PASSWORD="xxx"

2. 创建新的 keychain

shell
# keychain 路径 (通过一定逻辑生成的文件路径,可以认为是一个随机的临时路径)
keychain_file_path='~/xxx'
# keychain 密码 (同样是临时生成的随机密码)
keychain_password='xxx'
# 生成 keychain 前会判断 keychain_file_path 是否已存在 keychain
# 有的话就删除然后走下面的流程重新创建
security create-keychain -p $keychain_password $keychain_file_path
security unlock-keychain -p $keychain_password $keychain_file_path
security set-keychain-settings $keychain_file_path
security list-keychains -d user -s $keychain_file_path

3. 导入证书到 keychain 中

shell
security import $CSC_LINK -k $keychain_file_path -T /usr/bin/codesign -T /usr/bin/productbuild -P $CSC_KEY_PASSWORD

security set-key-partition-list -S apple-tool:,apple: -s -k $CSC_KEY_PASSWORD $keychain_file_path

4. 查看是否有代码签名证书

查找签名证书并返回名称

shell
security find-identity -v -p codesigning $keychain_file_path

也可以不指定 keychain_file_path 来查找

shell
security find-identity -v -p codesigning

5. 签名

shell
# name 是上一步查找证书返回的名称
codesign --deep --force --sign $name --keychain $keychain_file_path ElectronApp.app

公证

在 macOS 上面比较特殊,除了需要对应用签名外还需要进行公证,公证的详细流程可以参考: macOS 签名公证,这里我们就不过多赘述

公证这一步我们可以使用 Electron 官方提供的 electron/notarize 来进行自动化的公证

下面是官方提供的简单实用方法:

javascript
import { notarize } from '@electron/notarize';

async function packageTask () {
  // Package your app here, and code sign with hardened runtime
  await notarize({
    appBundleId,
    appPath,
    appleId,
    appleIdPassword,
    ascProvider, // This parameter is optional
  });
}

electron-builder 中,我们可以在 afterSign 这个 hook 里面完成这一步

javascript
const { notarize } = require('@electron/notarize')

const config = {
  // ...,
  afterSign: async (context) => {
    const { electronPlatformName, appOutDir } = context;
    if (electronPlatformName !== 'darwin') {
      // 如果不是 macOS,跳过公证
      return;
    }

    const appName = context.packager.appInfo.productFilename;

    return await notarize({
      appBundleId: yourAppBundleId,
      appPath: `${appOutDir}/${appName}.app`,
      // 我们将公证需要用到的信息放在 process.env 中
      appleId: process.env.NOTARIZE_APPLE_ID,
      appleIdPassword: process.env.NOTARIZE_APPLE_PASSWORD,
      ascProvider: process.env.NOTARIZE_APPLE_TEAM_ID,
    });
  },
}

module.exports = config;

Windows

相对于 macOS 来说,Windows 的打包流程少了公证这步,但 Windows 的自动化流程可能是很多 Electron 应用实现打包自动化最大的拦路虎,这中间很大一部分障碍是: 我们无法直接拿到 Windows 代码签名的证书 (无法直接拿到 .pfx 或者 .cer 证书)

Windows 的代码签名证书简单说可以分为: EV 代码签名证书非 EV 代码签名证书, EV 代码签名证书是安全性最高的,系统和杀毒软件会无条件信任经过 EV 代码签名证书签名后的应用。而没有经过 EV 代码签名证书签名过的应用打开会有安全警告。EV 代码签名证书的更多信息可以查看:https://www.globalsign.com/en/code-signing-certificate/ev-code-signing-certificates

以下 Windows 的签名打包流程我们只讨论 EV 代码签名证书的打包流程

EV 代码签名证书安全性最高,那么它自然会被特殊对待:我们无法直接拿到 EV 代码签名的证书文件 (类似 .pfx 结尾的证书文件)

当我们去购买 EV 代码签名证书时,证书颁发机构 (CA) 会将证书放置在一个物理 USB 里面,并将这个物理 USB 邮寄给我们。然后我们还需要下载安装 CA 提供的驱动程序后才能使用这个签名证书

物理 USB 签名流程

当证书和驱动程序等环境都配置正确后,我们需要将证书信息提供给 electron-builder

javascript
const config = {
  // ...
  win: {
    target: 'nsis',
    // 官方文档:https://www.electron.build/configuration/win
    certificateSubjectName: '你的证书名称',
  },
};

module.exports = config;

可以通过在 PowerShell 上面执行以下命令拿到代码签名的证书信息

ps
Get-ChildItem -Recurse Cert: -CodeSigningCert | Select-Object -Property Subject,PSParentPath,Thumbprint

当证书信息配置完成后直接执行打包命令,在需要代码签名时,会自动弹出一个窗口,在窗口中输入签名证书的密码后,electron-builder 就会自动对我们的程序进行代码命令

关于 Windows 的签名流程可以在 packages/app-builder-lib/src/codeSign/windowsCodeSign.ts 中找到,我们同样简单看下它做了什么

找到签名证书

electron-builder 会通过 PowerShell 执行一下命令拿到本机上面全部的打包证书并将输出转换为 json 格式

ps
Get-ChildItem -Recurse Cert: -CodeSigningCert | Select-Object -Property Subject,PSParentPath,Thumbprint | ConvertTo-Json -Compress

在上面我们提供了 certificateSubjectName 这个信息,那么通过匹配全部的证书列表就能找到对应的代码签名证书的指纹(上面输出信息中的 Thumbprint)

代码签名

拿到证书指纹 (Thumbprint) 后就可以开始代码签名了

ps
signtool.exe sign /v /fd sha256 /sha1 <thumbprint> /sm /as electron-app.exe

直接执行 signtool.exe 会报错,需要进入 signtool 的安装目录,默认安装位置在

C:\Program Files (x86)\Windows Kits\<SDK version>\bin\<version number>\<CPU architecture>\signtool.exe

完成后可以右键查看生成的 exe 文件属性的 Digital Signatures 来查看是否有签名成功

Windows 签名属性

使用 HSM 版本签名证书实现自动化

上面因为证书存放在物理 USB 中,并且打包签名过程需要人工输入密码等原因,无法很好的实现自动化流程。而直接将证书导出为文件发送给我们是非常危险的行为。像这种问题很多云服务厂商提供了 HSM 帮我们解决这类问题(HSM 全称为 Hardware security module,意思为 硬件安全模块,可以很好的帮我们保护证书安全,当然 HSM 在云服务上面需要另收费)

但除了物理 USB 包装的证书外,主流的 CA 都会提供 HSM 版本的 EV 代码签名证书,以 GlobalSign 为例,提供了 USB 版本和 HSM 版本

EV Code Signing certificates

购买和使用

关于 HSM 如何申请和使用,以 AWS 的 CloudHSM 为例,AWS 提供了非常完善的搭建和使用文档 https://docs.aws.amazon.com/zh_cn/cloudhsm/latest/userguide/introduction.html

购买和安装证书可以搭配 GlobalSign 文档食用:https://support.globalsign.com/code-signing/download-and-install-ev-code-signing-certificate

打包签名

HSM 的所有环境都搭建完成后,无须做任何调整即可直接开始打包(打包过程中也不再需要输入密码)

注意:需要提供 admin 权限给 PowerShell,否则可能会遇见类似这样的报错

SignTool Error: No certificates were found that met all the given criteria.

The following certificates were considered:
    Issued to: xxxxx
    Issued by: GlobalSign GCC R45 EV CodeSigning CA 2020
    Expires:   Fri Dec 01 06:58:19 2023
    SHA1 hash: xxxx

After EKU filter, 1 certs were left.
After expiry filter, 1 certs were left.
After Hash filter, 1 certs were left.
After Private Key filter, 0 certs were left.

Linux

Linux 是最简单的,无须签名等流程