跳到内容

在 macOS/Linux 上签名 Windows 应用程序

信息

仅当您拥有 EV 代码签名证书时,才需要描述的设置和配置。常规证书开箱即用

支持在 Unix 上签名 Windows 应用程序。 有多种方法可以实现此目的。 基本上,您需要一个能够使用 PKCS 11 签名代码的应用程序。 有一种使用 Java 的方法可以在 Linux 和 Mac 上运行。 您也可以在这两个系统上使用 osslsigncode,文档中包含 Linux 和 Mac 的操作指南。 本文档最后总结了一些备注以及如何将签名过程与 Electron Builder 集成。

在 Mac/Linux 上使用 JSign 签名 Windows 应用程序

此方法需要 Java。 在尝试此解决方案之前,请确保您已安装 Java。

  1. 通过运行 java -v 确保已安装 Java
  2. 下载 JSign
  3. 创建一个名为 hardwareToken.cfg 的文件。 用以下内容填充它。
  4. 检查库链接以确保您拥有正确的 PKCS 模块。 此链接可能因令牌而异。 在 Linux 上,您可以在 /lib 中找到它,而在 Mac 上,您可以在 /Library/Frameworks/usr/local/lib 中找到它。
  5. 安装 Mac 的令牌驱动程序,导出证书(如果是 .cer,则将其转换为 pem)
  6. 使用正确的参数运行 java -jar jsign-2.1.jar

hardwareToken.cfg

name = HardwareToken
library = /Library/Frameworks/eToken.framework/Versions/A/libeToken.dylib
slotListIndex = 0

链接:- 来自 Github 的 JSign

完整的签名命令

java -jar jsign-2.1.jar --keystore hardwareToken.cfg --storepass "your password here" --storetype PKCS11 --tsaurl http://timestamp.digicert.com --alias /link/to/cert.pem /link/to/app.exe

在 Mac 上使用 osslsigncode 签名 Windows 应用程序

主要问题是 brew 中缺少支持 OpenSSL 1.1 的引擎 PKCS 11。 当前版本仅支持 OpenSSL 1.0。 因此,您需要自己编译大多数应用程序。

  1. 使用 brew 安装一些应用程序,例如 autoconfautomakelibtoolpkg-configgnu-tar
  2. 创建类似 /usr/local/mac-dev 的文件夹,授予当前用户权限,在编译期间使用该文件夹作为前缀。
  3. 下载 OpenSSL 1.1.1 tar.gz,请参阅下面的链接,解压并编译:./config --prefix=/usr/local/mac-dev && make && make install
  4. 导出 OpenSSL 1.1 变量以编译以下应用程序
  5. export LDFLAGS="-L/usr/local/mac-dev/lib"
  6. export CPPFLAGS="-I/usr/local/mac-dev/include"
  7. export PATH="/usr/local/mac-dev/bin:$PATH"
  8. export PKG_CONFIG_PATH="/usr/local/mac-dev/lib/pkgconfig"
  9. 安装 opensc (.dmg 文件)
  10. 下载 libp11 .tar.gz,解压并编译:./configure --prefix=/usr/local/mac-dev && make && make install
  11. 安装 Mac 的令牌驱动程序,导出证书(如果是 .cer,则将其转换为 pem)
  12. 下载 osslsigncode .tar.gz,解压并编译:./autogen.sh && ./configure --prefix=/usr/local/mac-dev && make && make install (之后将二进制文件符号链接到 /usr/local/bin)
  13. 通过运行 pkcs11-tool --module /usr/local/lib/libeTPkcs11.dylib -l -O 找出密钥 ID
  14. 使用正确的模块、引擎和密钥 ID 运行 osslsigncode

链接:- 来自其网站的 OpenSSL 1.1 - 来自 Github 的 OpenSC - 来自 Github 的 Libp11 - 来自 Github 的 osslsigncode

完整的签名命令,pkcs11module 参数可能因令牌而异。

osslsigncode sign -verbose -pkcs11engine /usr/local/mac-dev/lib/engines-1.1/libpkcs11.dylib -pkcs11module /usr/local/lib/libeTPkcs11.dylib -h sha256 -n app-name -t https://timestamp.verisign.com/scripts/timestamp.dll -certs /link/to/cert.pem -key 'key-id-here' -pass 'password' -in /link/to/app.exe -out /link/to/app.signed.exe

在 Ubuntu 18.04 上使用 osslsigncode 签名 Windows 应用程序

这些步骤也适用于其他 Linux 操作系统,但软件包的名称可能不同。

  1. 使用 APT 更新软件包。 sudo apt-get update
  2. 使用 APT 安装软件包 sudo apt-get install -y openssl libcurl4-openssl-dev libssl-dev libengine-pkcs11-openssl curl libcurl4 git automake libtool pkg-config wget libccid libpcsclite1 pcscd usbutils opensc
  3. 下载 osslsigncode .tar.gz,解压并编译:./autogen.sh && ./configure && make && make install
  4. 安装 Linux 的令牌驱动程序,导出证书(如果是 .cer,则将其转换为 pem)
  5. 通过运行 pkcs11-tool --module /lib/libeToken.so -l -O 找出密钥 ID。 检查路径以确保您拥有正确的 PKCS 模块。 此路径可能因令牌而异。
  6. 使用正确的模块、引擎和密钥 ID 运行 osslsigncode

完整的签名命令

osslsigncode sign -verbose -pkcs11engine /usr/lib/x86_64-linux-gnu/engines-1.1/pkcs11.so -pkcs11module /lib/libeToken.so -h sha256 -n app-name -t https://timestamp.verisign.com/scripts/timestamp.dll -certs /link/to/cert.pem -key 'key-id-here' -pass 'password' -in /link/to/app.exe -out /link/to/app.signed.exe

链接:- 来自 Github 的 osslsigncode

备注

当事情不顺利时,请考虑以下事项。

  • 确保您使用正确的 PKCS 11 引擎和模块。 如果您收到 no slot with a token was found 或一些错误,例如 sc connect card errorCard is invalid or cannot be handled,则说明您没有使用正确的模块,请确保使用正确的模块。
  • 如果您自己编译了 OpenSSL,请确保您使用的引擎也是为 OpenSSL 编译的。 否则,您将遇到*兼容性*问题。
  • 使用 URL 列表中提到的 osslsigncode。 还有更多 forks/版本可供找到。 这里包含的是实际维护的库,需要 OpenSSL 1.1。

将签名与 Electron Builder 集成

根据您选择的方法,您可以通过创建 sign.js 文件来自动化签名过程。 然后在您的 package.json 中指向此文件。

package.json

...
    "win": {
      "target": "nsis",
      "signtoolOptions": {
        "sign": "./sign.js"
      }
    },
...

sign.js

exports.default = async function(configuration) {
  // do not include passwords or other sensitive data in the file
  // rather create environment variables with sensitive data
  const CERTIFICATE_NAME = process.env.WINDOWS_SIGN_CERTIFICATE_NAME;
  const TOKEN_PASSWORD = process.env.WINDOWS_SIGN_TOKEN_PASSWORD;

  require("child_process").execSync(
    // your commande here ! For exemple and with JSign :
    `java -jar jsign-2.1.jar --keystore hardwareToken.cfg --storepass "${TOKEN_PASSWORD}" --storetype PKCS11 --tsaurl http://timestamp.digicert.com --alias "${CERTIFICATE_NAME}" "${configuration.path}"`,
    {
      stdio: "inherit"
    }
  );
};