十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Cypress 二进制代码签名实战:macOS 与 Windows 证书轮换及 electron-builder 签名链路解析

Cypress 二进制代码签名实战:macOS 与 Windows 证书轮换及 electron-builder 签名链路解析 Cypress 二进制代码签名实战macOS 与 Windows 证书轮换及 electron-builder 签名链路解析【免费下载链接】cypressFast, easy and reliable testing for anything that runs in a browser.项目地址: https://gitcode.com/GitHub_Trending/cy/cypressCypress 在 CI 中构建 Windows 与 Mac 发行版时会执行代码签名签名的具体工作由electron-builder在create-build-artifacts构建任务中完成。本文基于仓库内的 code-signing 指南 展开完整覆盖 MacApple Developer ID 证书与 WindowsSSL.com 证书两条签名密钥的轮换流程并结合 electron-builder.json、windows-sign.js 等仓库源码深入讲解签名配置、公证notarize与远程签名委托的实际实现读完后可理解 Cypress 发布产物签名 → 公证 → 校验的完整链路及其安全设计。何时进行代码签名CI 构建流程中的定位code-signing 指南 开宗明义Cypress 的Windows 和 Mac 发行版在 CI 构建时执行代码签名签名动作由electron-builder在create-build-artifacts任务中承担。指南同时声明了一个适用前提读者应已熟悉 electron-builder 官方 Code Signing 文档中关于CSC_LINK、CSC_KEY_PASSWORD等环境变量的一般约定——下文正是在这个前提下把 Cypress 仓库中证书如何申请、密钥如何存放、构建时如何消费三件事讲透。从源码结构看这条签名链路的调用入口是 scripts/binary/build.ts 中的electronBuilder.build(...)调用await electronBuilder.build({ publish: never, config: { electronVersion, directories: { app: appFolder, output: outputFolder, }, icon: iconFilename, // for now we cannot pack source files in asar file // because electron-builder does not copy nested folders // from packages/*/node_modules asar: false, }, })根目录 package.json 中声明的依赖为electron-builder: ^25.1.8因此本文所述的签名行为均针对该版本的 electron-builder API。构建流程中签名并非孤立步骤而是嵌在如下顺序里lerna run build/lerna run build-prod构建各 package见 build.ts把各 package 的产物复制到 dist 目录electronBuilder.build(...)打包并签名Windows 走委托脚本macOS 走CSC_LINK证书自动签名触发 electron-builder.json 中声明的afterPack/afterSign钩子完成后续加工与公证。buildCypressApp的选项接口中还暴露了skipSigning?: boolean见 build.ts允许本地构建时跳过签名——签名失败时若设置了该选项会吞掉异常build.ts这为本地打包调试与CI 正式发布两种场景做了区分。electron-builder.json签名相关配置一览仓库根目录的 electron-builder.json 是签名行为的配置中枢关键项如下{ productName: Cypress, appId: com.electron.cypress, mac: { target: zip, forceCodeSigning: true, hardenedRuntime: true, entitlements: ./scripts/entitlements.mac.inherit.plist, entitlementsInherit: ./scripts/entitlements.mac.inherit.plist, type: distribution }, win: { signingHashAlgorithms: [sha256], sign: ./scripts/windows-sign.js, target: dir }, afterPack: ./scripts/after-pack-hook.js, afterSign: ./scripts/after-sign-hook.js }逐项说明mac.forceCodeSigning: true强制要求提供签名证书即上文CSC_LINK/CSC_KEY_PASSWORD否则打包直接失败保证 CI 产物不会静默产出未签名包。mac.hardenedRuntime: true启用 macOS 强化运行时这是通过 Apple 公证的前提。mac.entitlements/mac.entitlementsInherit都指向 scripts/entitlements.mac.inherit.plist。该文件授予三项能力com.apple.security.cs.allow-jit—— 允许 JITElectron/V8 运行时所需com.apple.security.cs.allow-unsigned-executable-memory—— 允许未签名可执行内存com.apple.security.cs.allow-dyld-environment-variables—— 允许 dyld 环境变量Cypress 二进制依赖环境变量注入如云协议相关配置从源码中大量的process.env消费可以推断其必要性。win.signingHashAlgorithms: [sha256]仅使用 SHA-256 摘要算法签名符合当前 Windows Authenticode 要求。win.sign: ./scripts/windows-sign.js把 Windows 签名委托给自定义脚本详见后文远程签名一节而不是由 electron-builder 用本地证书直接签。afterPack/afterSign分别指向 after-pack-hook.js 与 after-sign-hook.js。前者负责把各 package 的node_modules拷入产物、翻转 Electron Fuses、生成 V8 快照等打包后加工与签名无直接关系但会改变最终进签/进公证的对象后者负责 macOS 公证。轮换 macOS 签名密钥从 Apple 证书到 CircleCI 上下文这部分完整继承 code-signing 指南 中 Rotating the Mac code signing key 的 4 步流程在 Mac 上用 Cypress 的 Apple 开发者计划身份登录 Xcode。按 Apple 官方Create, export, and delete signing certificates文档操作先执行 View signing certificates 查看现有证书执行 Create a signing certificate提示选择类型时选择Developer ID Application注意用于应用分发签名的是 Developer ID Application 证书而非 App Store 证书执行 Export a signing certificate导出时设置一个强口令该口令之后会成为环境变量CSC_KEY_PASSWORD。把导出的、已加密的.p12文件上传到 Google Drive 的 Code Signing 文件夹并生成一个公共的直接下载链接。在 CircleCI 的test-runner:sign-mac-binary上下文中把CSC_LINK设为该直接下载 URL把CSC_KEY_PASSWORD设为加密 p12 时使用的口令。几点值得强调的工程细节凭据存放采用外部密文文件 下载链接模式私钥从不落进代码仓库或 CI 明文环境变量CSC_LINK只指向加密后的.p12CSC_KEY_PASSWORD单独存放。electron-builder 构建时会自动从CSC_LINK下载证书文件并用口令解密后签名。上下文按平台隔离macOS 与 Windows 分别使用test-runner:sign-mac-binary与test-runner:sign-windows-binary两个独立上下文密钥轮换互不影响也便于按平台最小化授权。轮换时口令变更必须同步CSC_KEY_PASSWORD与.p12的加密口令是绑定的二者不同步会导致 CI 签名阶段解密失败。源码纵深签名之后还要公证notarizemacOS 上仅签名并不够还需向 Apple 提交公证。Cypress 通过 after-sign-hook.js 在 electron-builder 的afterSign阶段完成逻辑如下平台守卫process.platform ! darwin时直接跳过日志提示not Mac, skipping after sign hook逃生开关设置了SKIP_NOTARIZATION环境变量时跳过公证便于调试构建定位应用在params.appOutDir下查找productFilename.app找不到则抛错appId硬编码为com.electron.cypress与 electron-builder.json 中的appId一致三项必需凭据NOTARIZE_APP_APPLE_ID、NOTARIZE_APP_PASSWORD、NOTARIZE_APP_TEAM_ID任一缺失立即抛错注意公证用的是 Apple ID App 专用密码 Team ID与签名用的 Developer ID 证书是两套凭据调用electron/notarize的notarize(...)提交公证失败会打印could not notarize application并向上抛出使构建失败。此外build.ts 在 darwin 平台且未skipSigning时会用 macOS 自带的 Gatekeeper 校验工具对产物做一次自证const args [-a, -vvvv, appFolder] console.log(cmd: spctl ${args.join( )}) const sp spawn(spctl, args, { stdio: inherit })即执行spctl -a -vvvv 产物路径退出码非 0 则构建失败——相当于在 CI 中模拟了用户机器上 Gatekeeper 是否放行的终检。轮换 Windows 签名密钥CSR、SSL.com 证书与 PFX 转换这部分完整继承 code-signing 指南 中 Rotating the Windows code signing key 的 6 步流程。第 1 步用openssl生成私钥与 CSR# generate a new private key openssl genrsa -out win-code-signing.key 4096 # create a CSR using the private key openssl req -new -key win-code-signing.key -out win-code-signing.csr第 2 步把 CSR 提交给 SSL.com使用 Cypress 的 SSL.com 账户换取证书若是续期renewing按 SSL.com 官方的 Renewing EV/OV and IV Certificates 指引操作若是轮换rotating需联系 SSL.com 支持申请重新签发。第 3 步从 SSL.com 控制台获取完整证书链以 ASCII-armored PEM 格式保存为win-code-signing.crt内容形如-----BEGIN CERTIFICATE-----等块。第 4 步用openssl把明文 PEM 的私钥与证书转为二进制的 PKCS#12/PFX 并加密口令同样是强口令之后成为CSC_KEY_PASSWORD➜ openssl pkcs12 -export -inkey win-code-signing.key -in win-code-signing.crt -out encrypted-win-code-signing.pfx Enter Export Password: password Verifying - Enter Export Password: password第 5 步把encrypted-win-code-signing.pfx上传到 Google Drive 的 Code Signing 文件夹获取公共直接下载链接。第 6 步在 CircleCI 的test-runner:sign-windows-binary上下文中把CSC_LINK设为该直接下载 URL把CSC_KEY_PASSWORD设为加密 pfx 的口令。源码纵深为什么 Windows 不直接用 pfx 本地签而是远程签名按 electron-builder 的一般约定CSC_LINK指向的 pfx 会被拉取后在构建机上执行 Authenticode 签名。但 Cypress 的 windows-sign.js 文件头注释说明了偏离这一默认做法的原因This signing procedure only runs on windows binary builds to leverage remote signing in order to fullfil new requirements around OV and IV code signing.即为了满足 SSL.com 自 2023 年 6 月起对 OV/IV 证书密钥存储的新要求私钥不能长期离开受控环境Cypress 改为把签名委托给 SSL.com 的远程签名服务。这与 electron-builder.json 中sign: ./scripts/windows-sign.js的委托配置相呼应完整链路见 windows-sign.js 的sign(configuration)函数读取四个远程签名凭据均来自 CI 环境变量任一缺失则打印缺失项并process.exit(1)WINDOWS_SIGN_USER_NAME、WINDOWS_SIGN_USER_PASSWORD—— SSL.com 账户凭据WINDOWS_SIGN_CREDENTIAL_ID—— SSL.com 侧证书/凭据标识WINDOWS_SIGN_USER_TOTP—— TOTP 二次验证密钥。下载并校验 SSL.com 的 CodeSignTool脚本把工具版本钉死为 v1.3.2并预置了下载包的 SHA-256 校验和const CODE_SIGN_TOOL_VERSION v1.3.2 const CODE_SIGN_TOOL_SHA256 4afc32e8b7f79bbe1de7e4e7049aaad4e0f754357613b9bbec0e3052f06fd36b下载后先计算实际 SHA-256与预期值不符立即抛错Downloaded CodeSignTool archive checksum ... does not match expected ...。注释还贴心地给出升级工具时的校验和再生成方法curl -fSL CODE_SIGN_TOOL_URL | shasum -a 256。这保证了能签我们二进制的工具只能经受控的版本提升才会变化防止供应链篡改。调用 CodeSignTool 完成远程签名childProcess.execSync(CodeSignTool.bat sign -input_file_path${configuration.path} -output_dir_path${TEMP_DIR} -credential_id${CREDENTIAL_ID} -username${USER_NAME} -password${USER_PASSWORD} -totp_secret${USER_TOTP})回写产物由于 CodeSignTool 无法在无交互确认的情况下原地覆盖文件脚本先签出到临时目录os.tmpdir()/release/tmp再把签名后的文件移回原位置覆盖mv ${tempFile} ${dir}对 electron-builder 而言签名对象路径不变。从源码结构看configuration.path由 electron-builder 在逐个签包文件时传入因此该委托脚本只对 Windows 可执行文件生效与electron-builder.json中win.target: dir产出目录而非压缩包相配合。小结两套密钥、两条链路、统一的 CI 凭据模型维度macOSWindows证书来源Apple Developer ID Application 证书SSL.com 签发的 OV/IV 证书密钥形态加密.p12加密.pfxPKCS#12凭据上下文test-runner:sign-mac-binarytest-runner:sign-windows-binary核心环境变量CSC_LINK、CSC_KEY_PASSWORDCSC_LINK、CSC_KEY_PASSWORD另加WINDOWS_SIGN_USER_NAME/_PASSWORD/_CREDENTIAL_ID/_USER_TOTP签名方式electron-builder 本地签名forceCodeSigning委托脚本走 SSL.com 远程签名win.sign签名后动作afterSign钩子公证NOTARIZE_APP_*spctl自证—两个平台共享同一套加密密钥文件存 Google Drive、CSC_LINK指下载链接、CSC_KEY_PASSWORD存口令的凭据模型差异在于 Windows 因密钥托管合规要求额外走了一条版本钉死、校验和校验的远程签名通道。对需要维护自签发布流程的 Electron 项目而言这套证书轮换 SOP 委托签名 公证钩子 Gatekeeper 自证的组合是一个可以直接对照仓库文件复现参考的完整范本。【免费下载链接】cypressFast, easy and reliable testing for anything that runs in a browser.项目地址: https://gitcode.com/GitHub_Trending/cy/cypress创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表