从签名到公证:一站式解决MacOS应用分发安全难题
1. 为什么MacOS应用需要签名和公证几年前我发布第一个Mac应用时用户安装后总是弹出无法验证开发者的警告。那种感觉就像开了一家店每个顾客进门都被保安拦下盘问。后来才知道这是缺少代码签名惹的祸。代码签名就像开发者的数字身份证。当用户双击应用时系统会检查这个身份证是否由苹果认证的开发者签发以及应用是否被篡改过。没有签名的应用会被GatekeeperMac的安全防护系统直接拦截。但故事还没结束。2019年Catalina系统发布后苹果引入了更严格的公证Notarization要求。即使应用有签名如果没有经过苹果服务器的公证用户依然会看到红色警告框。公证过程相当于苹果对应用进行快速安全检查确保没有恶意代码。2. 准备工作证书配置全攻略2.1 获取开发者账号首先需要加入苹果开发者计划年费99美元。这个账号不仅是签名公证的门票还能获取各种开发资源。建议直接注册组织账号比个人账号更灵活。2.2 生成必备证书在开发者后台我们需要两类证书Developer ID Application用于签名.app应用程序Developer ID Installer用于签名.pkg安装包创建证书时常见坑点密钥类型要选Mac App Distribution必须勾选代码签名能力证书下载后要双击安装到钥匙串我曾经因为漏选代码签名选项折腾了三小时找不到签名失败原因。教训是创建证书时要像检查购物清单一样逐项确认。2.3 配置钥匙串访问权限安装证书后打开钥匙串访问应用左侧选择系统-我的证书找到你的开发者证书展开详情右键选择获取信息在访问控制标签页选择允许所有应用程序访问这个设置允许命令行工具访问证书密钥否则签名时会报权限拒绝错误。3. 应用签名实战指南3.1 签名.app应用程序基础签名命令如下codesign --force --sign Developer ID Application: Your Name (ABCD123456) \ --entitlements AppName.entitlements \ --optionsruntime \ /path/to/YourApp.app参数解析--force强制替换现有签名--sign指定证书名称注意要完全匹配--entitlements权限配置文件沙盒、iCloud等能力--optionsruntime启用强化运行时Hardened Runtime验证签名是否成功codesign --verify --verbose /path/to/YourApp.app如果看到valid on disk和satisfies its Designated Requirement说明签名有效。3.2 处理第三方库签名很多应用会包含动态库.dylib或框架.framework。这些都需要单独签名我常用这个脚本批量处理find /path/to/YourApp.app/Contents \ -name *.dylib -o -name *.framework \ | while read file; do codesign --force --sign Developer ID Application: Your Name (ABCD123456) $file done4. 打包与安装包签名4.1 使用pkgbuild创建组件包pkgbuild --component /path/to/YourApp.app \ --identifier com.yourcompany.pkg \ --version 1.0 \ --install-location /Applications \ /output/YourAppComponent.pkg4.2 使用productbuild创建产品包productbuild --package /output/YourAppComponent.pkg \ /output/YourAppProduct.pkg4.3 签名安装包productsign --sign Developer ID Installer: Your Name (ABCD123456) \ /output/YourAppProduct.pkg \ /output/YourApp_Signed.pkg验证安装包签名pkgutil --check-signature /output/YourApp_Signed.pkg5. 公证流程深度解析5.1 配置公证凭证推荐使用notarytoolaltool已弃用xcrun notarytool store-credentials ACME_Credentials \ --apple-id youremail.com \ --team-id ABCD123456 \ --password abcd-efgh-ijkl-mnop密码需要到苹果账号后台生成应用专用密码。5.2 提交公证xcrun notarytool submit /output/YourApp_Signed.pkg \ --keychain-profile ACME_Credentials \ --wait--wait参数会让命令阻塞直到公证完成。不加这个参数的话需要手动查询状态xcrun notarytool history --keychain-profile ACME_Credentials5.3 处理公证失败如果公证被拒查看详细日志xcrun notarytool log xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \ --keychain-profile ACME_Credentials常见拒绝原因签名无效或过期包含可执行脚本使用私有API没有启用强化运行时6. 装订公证票据公证成功后将票据装订到安装包xcrun stapler staple /output/YourApp_Signed.pkg验证装订状态xcrun stapler validate /output/YourApp_Signed.pkg这个步骤很关键但常被忽略。没有装订票据的应用在离线环境下会触发警告因为系统无法联网验证公证状态。7. 自动化脚本实战手动操作容易出错这里分享我的自动化脚本框架#!/bin/bash # 配置变量 APP_NAMEYourApp APP_PATH./build/$APP_NAME.app PKG_PATH./build/$APP_NAME.pkg ENTITLEMENTS./AppName.entitlements # 1. 签名应用 codesign --force --sign Developer ID Application: Your Name (ABCD123456) \ --entitlements $ENTITLEMENTS \ --optionsruntime \ $APP_PATH # 2. 构建安装包 productbuild --component $APP_PATH \ --identifier com.yourcompany.pkg \ --version 1.0 \ --install-location /Applications \ $PKG_PATH # 3. 签名安装包 productsign --sign Developer ID Installer: Your Name (ABCD123456) \ $PKG_PATH \ ${PKG_PATH%.*}_signed.pkg # 4. 提交公证 xcrun notarytool submit ${PKG_PATH%.*}_signed.pkg \ --keychain-profile ACME_Credentials \ --wait # 5. 装订票据 xcrun stapler staple ${PKG_PATH%.*}_signed.pkg8. 常见问题解决方案问题1签名时报错code object is not signed at all解决检查应用包内所有可执行文件是否都已签名包括主可执行文件插件PlugIns帮助工具Helpers动态库.dylib问题2公证失败Invalid signature解决确认使用的证书类型正确检查签名时是否启用了强化运行时--optionsruntime验证entitlements文件是否包含必要权限问题3用户安装后应用闪退解决检查控制台日志Console.app确认entitlements配置正确测试在不同系统版本上的表现记得第一次完整走通签名公证流程时那种成就感就像解开了魔法谜题。现在每次提交新版本我都会把这个检查清单贴在显示器上[ ] 所有二进制文件已签名[ ] 安装包双重验证[ ] 公证日志无错误[ ] 票据装订成功