苹果系统运行着一些现有的最大和最赚钱的软件应用程序生态系统。理论上,要进入这些生态系统,传统上需要使用macOS,并加入苹果开发者计划(Apple Developer Program)。
如果你想为 Apple 操作系统开发应用程序,你可能会使用 Apple 的操作系统和 Apple 的官方工具进行开发和分发。但对于开源开发人员通常希望以最小的努力分发跨平台应用程序。在整个编程语言生态系统中,你运行的操作系统被抽象为许多应用程序的实现细节。通过创建 macOS、iOS 等开发需要直接访问 macOS 和通常高于市场价格的Apple 硬件的要求,苹果软件生态系统强加的分发要求是有效的排他性,并阻止利益相关方对进入其生态系统。
在苹果平台上发布软件的一个问题是代码签署和公证,即你需要:
1.在应用程序中嵌入加密签名,有效地证明来自 Apple Developer Program 关联帐户的真实性。 (这是签名。)
2.将你的应用程序上传到 Apple,以便他们对其进行检查,验证它符合要求,可能还会存储一个副本。然后,苹果会发布自己的加密签名,即公证书,然后需要将其嵌入到正在分发的应用程序上,这样苹果的操作系统才能信任它。(这是公证。)
从历史上看,这些步骤需要 Apple 专有软件专门从 macOS 运行。这意味着,即使你在 Rust、Go 等软件生态系统或 Web 平台中,你可以在不直接访问 macOS 的情况下交叉编译应用程序(测试显然是另一回事),如果你愿意,你仍然需要 macOS签署和公证你的申请。由于默认的安全设置,在macOS上需要有效的签名和公证。在 iOS 等移动平台上,除非你运行的是越狱设备,否则不可能发布未经签名和公证的应用程序。
如果我不需要macOS来构建我的应用程序,为什么我要被迫把苹果设备作为我的软件发布过程的一部分?为什么在发布的时候,我必须签署和公证我的申请,它不能更简化吗?
如果能重新实现 Apple 代码签名,以便开发人员有更多的灵活性和机会将应用程序分发到 Apple 的生态系统。其最终目标是扩大 Apple 生态系统对更多开发者的访问。
首先,得益于 rcodesign 0.14.0的发布。这是我第一次发布 rcodesign 的预构建二进制文件(Linux、Windows 和 macOS)。
macOS rcodesign 可执行文件是自签名的,它由 GitHub Actions Linux 运行程序使用 YubiKey 独有的代码签名证书进行签名。
2021年,apple-codesign 项目/Rust crate 发生了很大变化!只需查看更改日志!
这个项目从tugger-apple-codesign改名而来。
如果你是通过 cargo install 安装的,你需要 cargo install --force apple-codesign 来强制 Cargo 用另一个 crate 中的一个来覆盖 rcodesign 可执行文件。
rcodesign CLI可执行文件仍然存在,而且比以往任何时候都更强大。你仍然可以在Linux、Windows、macOS和任何其他平台上对Apple应用程序进行签名,你可以在这些平台上编译Rust程序。
这与 PyOxidizer 的文档一起发布在 readthedocs.io 上(因为我使用的是 monorepo)。那里有一些通用文档,例如有关如何通过将你自己的替代代码签名 PKI 部署到并行 Apple 来选择性地绕过 Gatekeeper 的指南。
支持签名包、DMG 和 .pkg 安装程序
当我去年宣布这个项目时,只有 Mach-O 二进制文件和非常简单的 .app 包是可签名的。即便如此,也有很多微妙的问题。
Rcodesign sign现在可以对更复杂的包进行签名,包括许多嵌套的包。有报道称iOS应用包签名正确。
该工具还支持签名.dmg磁盘映像文件和.pkg扁平封装包安装程序。
已知的签名限制现在记录在 Sphinx 文档中。
我相信 rcodesign 现在支持对用于 Apple 软件分发的所有主要文件格式进行签名。
支持 Linux、Windows 和 macOS 上的公证
苹果发布了一个名为Transporter的Java工具,可以让你上传文物到苹果进行公证。它们使这个工具可用于Linux、Windows,当然还有macOS。
虽然这个工具不是开源的,但使用这个工具可以让你在 Linux 和 Windows 上进行公证,同时仍然使用 Apple 的官方工具与他们的服务器通信。
rcodesign 现在支持调用 Transporter 并将工件上传到 Apple 进行公证。我们现在支持公证包、.dmg 磁盘映像和 .pkg 扁平封装安装程序包。我已经成功地从 Linux 公证了所有这些应用程序类型。
由于支持对所有应用程序类型进行签名和公证,现在可以在没有 macOS 参与发布过程的情况下发布 Apple 软件。
YubiKey 集成
我尝试尽可能多地使用我的 YubiKey,因为存储在 YubiKey 上的密钥或私钥可能比位于某个文件系统上的密钥或私钥更安全。如果你破解了我的设备,你很可能会获得我的私钥。但是你需要物理访问我的 YubiKey 并强迫或强迫我解锁它,以获得访问其私钥的权限。
rcodesign 现在支持使用 YubiKeys 进行签名操作。
这确实需要默认的智能卡 Cargo 功能。因此,如果手动构建,你将需要例如cargo install --features smartcard apple-codesign.。
YubiKey 集成来自 yubikey 。这个 crate 将直接与 macOS 和 Windows 中内置的智能卡 API 对话。所以如果你有一个启用了 YubiKey 支持的 rcodesign 构建,YubiKeys 应该可以工作。通过插入 YubiKey 并运行 rcodesign smartcard-scan 进行尝试。
我甚至实现了一些命令来轻松管理 YubiKey 上的代码签名证书。例如,你可以直接在设备上运行 rcodesign smartcard-generate-key --smartcard-slot 9c 以生成新的私钥,然后 rcodesign generate-certificate-signing-request --smartcard-slot 9c --csr-pem-path csr.pem将该证书导出到证书签名请求 (CSR),你可以在 developer.apple.com 上交换 Applie 颁发的签名证书。这意味着你可以轻松创建其私钥直接在硬件设备上生成且永远无法导出的代码签名证书。以这种方式生成密钥比将密钥存储在软件库(比如苹果的Keychains)中更安全。
远程代码签名
我最感兴趣的功能是我称之为远程代码签名的功能。
我在GitHub托管的Linux GitHub Actions运行程序上签署了一个macOS通用Mach-O可执行文件,使用的YubiKey物理连接到我桌子旁边的Windows 11设备上。未在计算机之间复制签名的应用程序。
我有一个调用 rcodesign sign --remote-signer 的 GitHub Actions 工作流程。我手动触发了该工作流程,并开始使用浏览器观察近乎实时的作业输出。rcodesign sign --remote-signer 打印出一些指令(包括一堵 base64 编码数据的墙)以指示下一步该做什么。重要的是,它要求其他人运行 rcodesign remote-sign 以继续签名过程。
该日志向我们展示了使用 YubiKey 进行连接和身份验证,以及一些关于与远程服务器对话的状态更新。
远程签名使我能够从 GitHub 运营的 GitHub Actions 运行程序签署 macOS 应用程序,同时使用安全存储在我的 YubiKey 上的代码签名证书,该证书插入到距 GitHub Actions 运行程序数百公里的 Windows 设备上。
这里发生的是 2 个 rcodesign 进程通过中央中继服务器桥接的 websocket 相互通信。我免费操作一个默认服务器。服务器是开源的,如果你想运行你自己的服务器,你可以使用terrform模块,希望只需要花几分钟时间。当启动设备希望创建签名时,它向请求加密签名的签名者发送一条消息。然后将签名发送回发起者,发起者将其合并。
我设计这个特性时考虑到了CI系统的自动发布(比如GitHub Actions)。我想要一种方法,可以简化应用程序的代码签名和发布过程,而不必给CI中的低信任设备无限访问我的私人签名密钥的权限。这可能在许多其他场景中都是有用的。你是否曾经因为没有苹果发行的代码签名证书而通过电子邮件或下拉框将应用程序发送给别人签名?现在你有了一个不需要复制文件的替代解决方案。只要你可以看到启动设备的日志输出,或者将输出传递给你(例如通过聊天应用程序或电子邮件),你就可以在另一台设备上远程签署文件!
关于远程签名安全性
Websockets 通过由第三方操作的中央服务器?!授予远程设备访问权限以对任意内容执行代码签名?!你的恐惧和怀疑是 100% 有道理的:我也会这么想!
促进远程代码签名的服务会成为一个非常有利可图的攻击目标,如果被滥用,它可能被用来强制使用有效的代码签名证书的各方签署不想要的代码,如恶意软件。实现这样的功能有很多很多很多错误的方法。
远程代码签名设计和安全注意事项包含了我的一些高级设计目标和安全评估。远程代码签名协议详细介绍了通信协议,包括所涉及的加密(实际的加密,而不是流行的加密)。关键在于协议和服务器的设计使恶意服务器或中间人无法伪造签名请求。签名会话在几分钟后过期,第三方或服务器无法注入会导致不需要签名的恶意消息。通过初始握手获得会话临时共享加密密钥,并从那里使用对称加密密钥,因此对等方之间的所有有意义的消息都是端到端加密的。恶意服务器可以做的最糟糕的事情就是进行拒绝服务。
我相信我的远程签名实现比许多常见做法更安全,因为今天的常见做法需要复制私钥并让低信任度设备(如 CI 工作人员)访问私钥或者文件在没有加密监管链的情况下被复制以证明不会被篡改。是的,远程签名为远程访问引入了使用签名密钥的向量。但是按照我的意图进行实践,远程签名可以消除复制私钥或授予对它们的无限访问权限的需要。从威胁建模的角度来看,我认为密钥访问中的网络限制使得远程签名比现在许多人使用的私钥管理实践更安全。
话虽如此,这里的星号是我实现了我自己的密码系统来实现端到端消息安全。如果在设计或实现中存在漏洞,密码系统可能会崩溃,从而带来对消息伪造的防御。届时,恶意服务器或特权网络攻击者可能会强迫某人签署不需要的软件。但这很可能是损害的程度:对签名密钥的脱机攻击是不可能的,因为签名需要存在,而且私钥永远不会通过网络传输。即使没有端到端加密,该系统也比将你的私有密钥作为一个容易被窃取的CI秘密(或类似的)留在周围更安全。
Apple 钥匙串支持
从0.14 版本开始,我们现在可以提前支持使用存储在 Apple 钥匙串中的代码签名证书进行签名!如果你在 Keychain Access 或 Xcode 中创建了 Apple 代码签名证书,那么这可能就是你的代码签名证书所在的位置。
我拖延了很长一段时间,因为我没有意识到这有什么好处:如果你使用macOS,只需使用 苹果的官方工具。但是随着rcodesign获得了对远程代码签名的支持,以及其他一些功能,这些功能可以让它成为所有平台上苹果工具的有力替代品,我认为我们应该提供这个功能,这样我们就不会再阻止人们从keychain导出私钥了。
Apple 的代码签名很复杂。 苹果的工具和rcodesign之间很容易出现细微差别。
rcodesign 现在有 print-signature-info 和 diff-signatures 命令来转储和比较与代码签名相关的 YAML 元数据,以便更容易地比较代码签名实现甚至多个签名操作之间的行为。
本文翻译自:https://gregoryszorc.com/blog/2022/04/25/expanding-apple-ecosystem-access-with-open-source,-multi-platform-code-signing/如若转载,请注明原文地址。