从 Delphi 应用中去掉 OpenSSL DLL,过去是一个构建期的决定:打开一个编译器 define,重新构建,再给那些机器上不能有 OpenSSL 的客户交付第二个可执行文件。sgcWebSockets 2026.10 把它变成了一个运行时决定:一个全局变量,在启动时设置一次,就能决定应用的密码学运算是跑在 OpenSSL 上,还是跑在库内置的、用 Object Pascal 编写的密码学实现上。
连接这一侧,也就是不依赖 OpenSSL 的 TLS,已经在《不依赖 OpenSSL DLL 的 Delphi TLS 1.3》一文中介绍过。这篇文章讲的是其余的一切:你的应用在 TLS 握手之外所做的签名、哈希、密钥协商和加密。
启动时一行代码
单元 sgcBase_Helpers 声明了类型为 TsgcCryptoBackend 的全局变量 sgcCryptoBackend:
uses
sgcBase_Helpers;
begin
sgcCryptoBackend := cbNative;
sgcCheckNativeCrypto;
Application.Initialize;
...
这个开关是全局的,不是按组件设置的,所以要在创建任何组件之前设置好。sgcCheckNativeCrypto 是可选的:如果原生密码学相关的 unit 没有编译进去,它会在启动时就以一条清晰的提示信息报错,而不是等到你的应用尝试生成第一个签名时才出问题。
三个取值
cbOpenSSL是默认值。应用的密码学运算跑在 OpenSSL 上,和之前的版本完全一样,所以升级之后,只要你不设置这个变量,就什么都不会变。cbNative让应用的密码学运算跑在 Object Pascal 实现上,这种情况下完全不会加载 OpenSSL。cbAuto会在 OpenSSL 库可以加载时使用 OpenSSL,加载不了时使用原生代码。检测只运行一次,结果会被缓存。
cbAuto 适合那些两种方式都要部署的产品。有些客户会把 OpenSSL 库装在可执行文件旁边,有些则被禁止这样做,而同一个构建版本对两者都适用,不会再收到关于缺少 DLL 的支持工单。
// same executable, with or without the OpenSSL libraries beside it
sgcCryptoBackend := cbAuto;
这个开关涵盖的内容
这个开关涵盖了组件替你完成的下列密码学操作:
- 哈希和 HMAC 辅助函数;
- JWT 签名和验证,HS、RS 和 ES;
- OAuth2 和 DPoP 证明;
- AWS Signature V4 和 CloudFront 签名 URL;
- Web Push;
- 端到端加密(E2EE),不同后端之间可以互通,所以一个使用
cbNative的客户端可以和仍在使用 OpenSSL 的服务器通信; - WebAuthn 和 passkey;
- SAML 和 XML 签名;
- NTLM;
- AEAD、ML-KEM 和 HKDF;
- QUIC 数据包保护、DTLS 和 WebRTC 证书,以及 SRTP。
这些都不需要修改代码。昨天还是用 OpenSSL 签名的 JWT,今天就会因为这个变量的设置而改用原生方式签名。
连接是单独的设置
sgcCryptoBackend 不会改变组件的连接方式。一个连接使用的 TLS 是按组件单独选择的:在 TCP、HTTP 和 WebSocket 组件,以及构建在它们之上的一切(比如 MQTT)上,用 IOHandler = iohNativeTLS;在 QUIC 和 HTTP/3 上也是如此;WebRTC 上则是 DTLSOptions:
sgcCryptoBackend := cbNative; // application crypto
oClient.TLSOptions.IOHandler := iohNativeTLS; // this connection
保留 OpenSSL handler 的组件,在连接时仍然会加载 OpenSSL。在 QUIC、HTTP/3 和 DTLS 上使用 iohNativeTLS 时,无论开关怎么设置,都完全不会用到 OpenSSL。想要在任何地方都不用 OpenSSL 的应用需要同时设置两处:为密码学设置这个开关,并为每一个建立安全连接的组件设置 handler。
这个 Define 现在是可选的
在此之前,不使用 OpenSSL 运行就意味着要用 SGC_NATIVE_CRYPTO 这个编译器 define。现在已经不再需要了。不加这个 define,OpenSSL 和原生代码都会被编译进去,由这个变量在运行时决定用哪一个。
这个 define 仍然有一个用处:无论 sgcCryptoBackend 设置成什么,它都会强制使用原生后端,并把 OpenSSL 代码从应用的密码学 unit 中去掉,从而让可执行文件更小。如果你确定某个构建版本永远不会用到 OpenSSL,它仍然是正确的选择。如果你还不确定,就先不加这个 define,留到启动时再决定。
为什么这很重要
- 同一个构建版本。那些因为许可问题、要求交代每一个第三方二进制文件的安全审计,或者不允许任何人复制 DLL 的锁定机器,而必须在没有 OpenSSL 的情况下交付的客户,拿到的可执行文件和其他所有人一样。
- 一个回退方案。OpenSSL 库缺失时,
cbAuto能让应用继续正常工作,而不是在第一次签名时就失败。 - 移动端。在 iOS 和 Android 上,根本没有 OpenSSL 可以为应用的密码学打包进去。
- 各平台代码统一。这个开关在 Windows、Linux、macOS、iOS 和 Android 上行为一致,从 Delphi 7 到 Delphi 13 以及 C++Builder 都适用。
升级
在你设置这个变量之前,不会有任何变化,因为 cbOpenSSL 是默认值。要尝试原生后端,把上面那两行加到你的项目文件里,跑一遍测试,再做对比。受这个开关影响的完整列表,以及每个部分要实现运行时零 OpenSSL 各需要哪些设置,都在帮助主题《不使用 OpenSSL 运行》中。
延伸阅读
有问题、反馈,或需要迁移方面的帮助?欢迎联系我们。你会收到编写这些代码的人的回复。
