在跨平台开发中,我们经常会使用到一些与平台无关的三方库如 libcurl、libwebsocket 等类似的库来做网络传输,在 SSL/TLS 的流程中必定是需要对服务器的证书、域名做验证,虽然关闭验证能让代码很快的让代码跑起来,但安全问题是重中之重,像中间人攻击、域名欺骗等情况就无法避免了。所以要完整的走完 SSL/TLS 流程客户端必须要有完整的 CA 的根证列表以完成连接对象的验证。
而 CA 根证的管理在各个平台下均有差异,本文将以 libcurl 举例来了解各平台下对 CA 根证的管理及维护措施,以及跨平台开发中推荐的根证管理方案。
基本情况
默认情况下 libcurl 使用 openssl 作为 SSL/TLS 后端(所有支持的后端列表),但 openssl 不会自动加载系统 CA 根证列表,如果你自己编译的 libcurl 在没有指定任何 CA 文件列表的情况下对一个 https 请求开启了 CURLOPT_SSL_VERIFYPEER
和 CURLOPT_SSL_VERIFYHOST
,那么很不幸 libcurl 将返回错误码 60 和错误:SSL certificate problem: unable to get local issuer certificate
。这表示没有可用的 CA 根证可以去校验服务器的信息。
要解决这个问题有多种手段,我还是以平台作为区分先介绍一下平台下的能力。
Windows
在 Windows 系统下实际会维护一份 CA 的信息,我们可以通过 certmgr.msc 查看当前系统包含了哪些 CA:

这些 CA 会跟随 Windows Update 或计划任务进行更新,Windows 计划任务中可以看到相关的任务:

但这些 CA 文件如果我们要访问需使用 Windows SSL/TLS Schannel SSP 后端,它具备访问系统 CA 证书的能力。libcurl 允许指定一个编译选项来在 Windows 下默认启用 SSL/TLS Schannel SSP 后端而不使用默认的 openssl,通过 github 可以找到其实现:https://github.com/curl/curl/blob/master/lib/vtls/schannel.c,以下是不同的工具链的配置参数:
- AutoTools 通过
--with-schannel
参数来启用 - CMake 通过
CURL_USE_SCHANNEL
来启用
启用 Schannel 后端以后,再次发起请求将自动从 Windows 系统管理的 CA 列表去校验服务器信息,但这也有一些缺点:
- Schannel 系列 API 依赖高版本的操作系统(Windows 10 以上)
- Schannel 相关动态库文件很容易在一些精简版 Windows 系统找不到(Windows 生态你懂得)
- 有些精简版 Windows 系统会彻底将 Windows Update 或计划任务能力删除,导致证书无法及时更新
所以虽然是一种可行方案,但存在较多问题。
Apple like
同样在 Apple 生态下,系统也会维护自己的 CA 证书列表,新版本的 macOS 系统你可以打开 /System/Library/CoreServices/Applications/Keychain Access.app
查看系统中已有的 CA 列表。

libcurl 同样允许启用平台下的 SSL/TLS 后端,它就是 Secure Transport,通过 github 同样可以看到其对应实现文件:https://github.com/curl/curl/blob/master/lib/vtls/sectransp.c,通过以下参数来配置开启 Secure Transport:
- AutoTools 通过
--with-secure-transport
参数来启用 - CMake 通过
CURL_USE_SECTRANSP
参数来启用
这样编译 libcurl 后,不仅体积会减小而且 Secure Transport 会自动请求系统钥匙串(Keychain)来获取可用的 CA 列表,这样也可以完成 https 的安全请求。虽然 macOS、iOS 不像微软操作系统一样那么容易被修改且对历史应用的兼容性很好,但是!!!由于 Secure Transport 仅支持 TLS 1.2 及以下传输的能力,无法支持 TLS 1.3,在 2025 年 5 月(也就是本月)libcurl 将移除 Secure Transport 相关代码的支持。这是相关博文:https://daniel.haxx.se/blog/2025/01/14/secure-transport-support-in-curl-is-on-its-way-out/
所以使用 Secure Transport 让其帮我们自助管理的希望也破灭了。
Linux like
其他平台就不多说了,因为像 Linux(包括 Android)这样的系统发行版数量众多,根本不像 Windows 或 macOS 一样有一个自己的 CA 管理系统,以 Deb、Ubuntu 举例,需要人为的手动安装并更新 CA 才行(sudo apt-get install -y ca-certificates
),我们不可能依赖 C 端用户去帮我们更新 CA 证书。
推荐方案
从上面的描述大家不难看出,CA 的管理在不同平台上差异很大,尤其是 Linux 或一些嵌入式系统。这对我们跨平台开发安全传输的客户端软件来说带来了一些挑战。如果不依赖平台下的 SSL/TLS 后端我们只能通过动态指定 CA 文件或文件列表的方式让 libcurl 可以在不同平台下行为一致。
从 curl 的官网可以下载到 Mozilla 的 CA 根证列表:https://curl.se/docs/caextract.html。
如果你是应用开发者,那么携带一个 CA 的 pem 文件并让其实现自动更新不是什么大问题,但如果是 SDK,我们很难说服用户将除 SDK 文件以外的证书文件也同时携带到应用程序中发布并能及时的得到更新。我们目标是将其嵌入到我们的应用程序或 SDK 动态库中,在运行时传递给 libcurl 做服务器的验证工作。并且在每次 SDK 迭代更新时都去更新最新的 CA 根证列表嵌入到 SDK 文件中,这虽然会带来一些体积上的影响但对比无法正常建立连接,我们宁可牺牲一些体积。
由于 CA 的字符串内容很长,大部分编译器不可能一次性编译通过,做分割的方案也很难实现自动化更新,对于资源文件嵌入到程序的方案这里推荐一个 CMake 的开源项目:cmrc,他可以让你在程序中像访问本地资源一样访问嵌入的资源数据,通过这个工具你可以很方便的在 C、C++ 生态管理资源文件。
总结
到这里我们基本上把可行的方案、可能的措施都介绍到了。核心思想是:
1、建议自行维护 CA 文件随应用或嵌入到 SDK 发布
2、如果你不需要跨平台,可尝试使用操作系统的 SSL/TLS 后端
虽然通过平台下 native 开发语言可以很容易做到这些事情,但跨平台开发的场景还是必定面临这些问题的。希望本文介绍的一些资料和方案对大家有帮助,如果有更好的方案也欢迎通过本文评论回复。