如何公证(notarize)你的Electron应用

为什么打包后的Electron应用在macOS上会提示无法打开,版本更新之后,接到了部分用户的反馈,提示无法安装我们的App,情况如下:
未公证

原因

macOS 10.14.5版本开始,所有macOS程序不仅需要签名,还需要经过苹果公证,当然上架AppStore的应用苹果会进行审核和公证,但那些不上架市场的应用也需要向苹果公证你的应用,才能正常使用,否则GateKeeper会阻止用户打开应用。

如何公证Electron App

我这里使用的是`[email protected][email protected],另外进行苹果公证还需要一个必要的前置条件,那就是需要有Apple Developer ID`也就是苹果开发者账号。已有开发者账号请继续往下看。

1. 构建应用开启强化运行时

苹果在添加公证时,要让你的App有一个强化的运行时,不懂什么意思?其实就是默认给你的应用更少的权限。
Electron启用强化运行时,在你的构建配置下,添加"hardenedRuntime": true

"build": {
  "mac": {
    "hardenedRuntime": true
  }
}

开启该选项时,需要申请一定的权限,macOS下申请权限可使用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>
    <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.allow-dyld-environment-variables</key>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
  </dict>
</plist>

主要申请的权限为第二条,允许未签名的可执行内存授权

使用plist,修改上面的强化运行时构建代码:

"mac": {
  "entitlements": "scripts/entitlements.mac.plist",
  "entitlementsInherit": "scripts/entitlements.mac.plist"
}

2. 苹果开发者账号及完整性检验

需要公证你的应用,必须要有有效的开发者账号才能进行公证。有关开发者账号的申请,证书的创建、导出、删除请自行搜索解决。

electron-builder使用的签名工具electron-osx-sign会验证签名是否成功,在macOS10.14.5之前,签名应用会返回true,而该版本及该版本之后将会返回false,因为虽然应用签名了,但它没有进行公证。所以,我们最好时禁用这个完整性检查,可添加"gatekeeperAssess": false到mac构建下,及完整的mac构建配置至少需要以下内容:

"mac": {
  "hardenedRuntime": true,
  "gatekeeperAssess": false,
  "entitlements": "scripts/entitlements.mac.plist",
  "entitlementsInherit": "scripts/entitlements.mac.plist"
}

另外,如果你需要导出dmg的macOS安装包,并且你的electron-builder版本小于20.43.0,你则需要添加额外的配置:

"dmg": {
  "sign": false
}

因为Gatekeeper会对你的程序所以签名内容进行完整性校验,实际上对dmg进行签名和公证仍会出现上诉错误,应该是苹果的bug或有其他原因。所以我们最好使用未签名、未工作的dmg,因为安装程序后,Gatekeeper检验的应用内容,发现经过公证,同样可以通过检查并且使用。

3. 对你的应用进行公证

Electron官方提供了人一个npm包electron-notarize用来对electron程序进行公证,所以我们需要安装一下这个依赖:

yarn add electron-notarize --dev
// or
npm install electron-notarize --save-dev

我们需要在应用签名后,打包dmg镜像之前对应用进行公证,electron-builder提供了一个hooks afterSign,该钩子将在签名完成后执行指定的函数或脚本。我们在根目录下新建scripts目录,并且新建notarize.js文件:

mdkir scripts
cd scripts
touch notarize.js

代码如下:

const fs = require('fs')
const electronNotarize = require('electron-notarize')

module.exports = async (params) => {
        // params由afterSign传入,可参考上面的链接
    const { electronPlatformName, appOutDir } = params
    // 打包非mac平台无需公证
    if (electronPlatformName !== 'darwin') {
        return;
    }

    const appName = params.packager.appInfo.productFilename;
    const appPath = `${appOutDir}/${appName}.app`

    if (!fs.existsSync(appPath)) {
        throw new Error(`Cannot find application at: ${appPath}`);
    }

    console.log(`start to notarize application `)
    try {
        await electronNotarize.notarize({
            appBundleId: 'your app bundle Id',
            appPath: appPath,
            appleId: `your apple ID`,
            appleIdPassword: `your apple id password`
        })
    } catch (error) {
        console.log(error)
    }
    console.log(`notarize app : ${appName} Done.`)
}

appleIdappleIdPassword较为敏感,建议不要使用明文,可考虑使用.env或环境变量等方式注入。appleIdPassword建议不要使用常规密码,可生成一个特定的密钥用来公证,生成特定程序密钥

应用签名后,会自动运行该脚本,最后打包生成dmg镜像,就可以正常安装并通过GateKeeper的检测了。

其他

该上传到公证过程时间可能较长,熟悉iOS开发者应该比较清楚,国内上传安装包到苹果,可能需要比较长的时间,安心等待即可。不过我尝试过全局科学上网甚至无法上传,使用wifi也会长时间处于等待,并且这个过程看不到进度。我亲试使用手机热点上传会比较快,接受不了长时间等待可以尝试手机热点上传。