
1. 项目概述与核心价值在Linux开发与运维的日常里curl这个命令行工具几乎无处不在。从简单的API接口测试、文件下载到复杂的自动化脚本、服务健康检查它都是我们手中最趁手的“瑞士军刀”。然而标准发行版仓库里提供的curl往往是“通用版”为了兼容性它可能不会包含一些我们特定场景下急需的“高级特性”。比如当你的服务需要对接遵循国密GM/T标准的HTTPS接口时或者你需要利用HTTP/2协议的高并发特性来提升爬虫或API客户端的性能时一个“裸奔”的curl就显得力不从心了。最近在为一个金融科技项目搭建测试环境时我就遇到了这个痛点。项目后端要求所有通信必须启用国密SSL特别是SM2/SM3/SM4套件同时前端微服务间的大量内部API调用又希望能用上HTTP/2以减少延迟。Ubuntu 22.04 LTS自带的curl7.81.0版本默认并不支持国密HTTP/2的支持也依赖于特定版本的OpenSSL或Nghttp2库配置起来颇为麻烦。与其在各种第三方PPA里寻找不确定是否靠谱的预编译包不如自己动手从源码开始编译一个完全符合自己需求的curl。自己编译听起来有点“硬核”但其实整个过程就像组装一台定制电脑选对核心部件依赖库按照正确的顺序和参数进行组装配置编译选项最后就能得到一台性能强悍、功能专一的“工作站”。这篇文章我就来详细拆解在 Ubuntu 22.04 LTS 环境下如何从零开始编译出一个同时支持国密SSL和HTTP/2的curl可执行文件及开发库。我会把每一步的意图、可能遇到的坑以及我实测有效的解决方案都记录下来目标是让你看完后能独立、顺利地完成整个编译过程得到一个属于你自己的“强化版”curl。2. 编译环境准备与核心依赖解析工欲善其事必先利其器。编译一个功能定制的软件第一步不是急着下载源码而是搭建一个干净、完备的编译环境并彻底理解每个依赖库所扮演的角色。这能避免很多“编译到一半报错找不到头文件”的尴尬情况。2.1 系统基础环境搭建首先确保我们在一个全新的 Ubuntu 22.04 系统上或者至少是一个可以自由安装开发包的环境。打开终端我们先更新软件源并安装最基础的编译工具链。sudo apt update sudo apt upgrade -y sudo apt install build-essential -ybuild-essential这个元包会安装gcc,g,make,libc-dev等核心编译工具这是后续所有编译工作的基石。接下来我们需要为curl及其依赖库的编译安装一些通用的开发库例如用于处理压缩的zlib以及pkg-config一个帮助我们管理编译和链接标志的工具。sudo apt install autoconf automake libtool pkg-config zlib1g-dev -y注意pkg-config非常关键。后续在配置curl时它会通过pkg-config来查询openssl、nghttp2等库的安装路径、库文件以及编译标志。如果缺少它./configure脚本很可能无法自动找到你已经安装好的依赖库导致编译出的curl不支持这些功能。2.2 核心依赖库选型与作用剖析curl的强大在于其模块化设计它对许多网络协议和加密标准的支持是通过外部库实现的。我们要支持国密SSL和HTTP/2就需要在编译前准备好对应的库。这里是我们本次编译的“四大核心依赖”OpenSSL / Tongsuo铜锁这是curl进行 SSL/TLS 加密通信的引擎。标准的curl使用 OpenSSL。但为了支持国密算法我们有更优的选择——Tongsuo。Tongsuo 是 OpenSSL 的一个重要分支由国内企业和社区共同维护在完全兼容 OpenSSL API 的基础上原生并优先支持了国密算法SM2、SM3、SM4以及相应的国密 TLS 协议如 GM/T 0024-2014。因此我们将用 Tongsuo 替代标准的 OpenSSL。Nghttp2这是curl支持 HTTP/2 协议的“翻译官”。HTTP/2 是一个二进制协议与 HTTP/1.x 的文本协议截然不同。curl需要借助libnghttp2这个库来封装和解封装 HTTP/2 的帧Frames处理多路复用、服务器推送等特性。没有这个库curl即使链接了支持 ALPN应用层协议协商的 SSL 库也无法实际使用 HTTP/2。Zlib用于支持 HTTP 内容压缩如Content-Encoding: gzip。虽然对于核心的国密和 HTTP/2 功能不是必须的但一个完整的curl通常都会包含它而且很多网络服务默认返回压缩数据没有它会导致解压失败。我们之前已经通过zlib1g-dev安装了开发版。c-ares这是一个异步 DNS 解析库。curl默认使用系统的同步 DNS 解析器如getaddrinfo。使用 c-ares 可以让curl在解析 DNS 时不阻塞对于需要同时发起大量连接的高并发场景这正是 HTTP/2 的优势场景有性能提升。这是一个可选但推荐的优化项。理解了这些我们的依赖安装路线图就清晰了先编译安装Tongsuo再编译安装Nghttp2和可选的c-ares最后用它们来配置和编译curl。3. 依赖库的编译与安装实战依赖库的编译安装是整个过程里最需要耐心和细心的环节。顺序很重要因为后编译的软件需要能找到先安装的库。3.1 编译安装支持国密的 Tongsuo 库Tongsuo 的源码可以从其 GitHub 仓库获取。我们选择一个稳定的长期支持版本比如1.1.1分支的某个标签。# 1. 安装一些 Tongsuo 编译可能需要的额外依赖 sudo apt install perl -y # OpenSSL/Tongsuo 的配置脚本是用 Perl 写的 # 2. 下载 Tongsuo 源码 (以 1.1.1w 版本为例这是一个包含安全修复的版本) cd /tmp wget https://github.com/Tongsuo-Project/Tongsuo/archive/refs/tags/1.1.1w.tar.gz tar -xzf 1.1.1w.tar.gz cd Tongsuo-1.1.1w # 3. 配置编译选项 ./config --prefix/usr/local/tongsuo \ --openssldir/usr/local/tongsuo/ssl \ shared \ zlib-dynamic \ enable-ntls \ enable-sm2 \ enable-sm3 \ enable-sm4 \ no-idea \ no-mdc2 \ no-rc5 \ no-ssl3 \ no-weak-ssl-ciphers关键配置参数解读--prefix/usr/local/tongsuo指定安装目录。将其安装到/usr/local下是 Unix 系统的惯例用于存放本地编译的软件与系统自带的包管理器apt管理的/usr区隔开避免冲突。--openssldirSSL 相关文件如证书、私钥的存放目录。shared编译生成动态链接库.so文件这样curl可以动态链接它节省磁盘空间和内存。zlib-dynamic动态链接 zlib 库。enable-ntls启用国密 TLS 协议支持。这是最关键的一个选项它开启了符合 GM/T 0024-2014 标准的双证书体系签名证书和加密证书。enable-sm2/enable-sm3/enable-sm4启用具体的国密算法。通常enable-ntls会隐含启用这些但显式指定更稳妥。一系列no-*选项禁用一些老旧、不安全的算法和协议如 SSLv3, RC5, IDEA这是安全加固的最佳实践。编译与安装# 4. 编译。这里的 -j$(nproc) 表示使用所有可用的 CPU 核心并行编译能显著加快速度。 make -j$(nproc) # 5. 安装到之前 prefix 指定的目录 sudo make install安装后的重要步骤安装后Tongsuo 的库文件在/usr/local/tongsuo/lib头文件在/usr/local/tongsuo/include。为了让系统动态链接器能找到它我们需要更新动态库缓存。# 将 Tongsuo 的库路径加入到动态链接器的配置中 echo /usr/local/tongsuo/lib | sudo tee /etc/ld.so.conf.d/tongsuo.conf # 更新缓存 sudo ldconfig现在你可以用openssl version系统自带的和/usr/local/tongsuo/bin/openssl version来对比一下。后者应该会显示 “Tongsuo” 字样并且通过openssl ciphers -v | grep -i SM可以查看是否支持国密套件。3.2 编译安装 Nghttp2 库接下来是 HTTP/2 的支持库。我们同样从源码编译以获得与当前系统架构和 Tongsuo 库的最佳兼容性。cd /tmp # 下载最新稳定版 nghttp2 (以 1.58.0 为例) wget https://github.com/nghttp2/nghttp2/releases/download/v1.58.0/nghttp2-1.58.0.tar.gz tar -xzf nghttp2-1.58.0.tar.gz cd nghttp2-1.58.0 # 配置、编译、安装 ./configure --prefix/usr/local/nghttp2 --enable-lib-only make -j$(nproc) sudo make install参数解读--prefix/usr/local/nghttp2同样安装到/usr/local下。--enable-lib-only这个选项非常重要。Nghttp2 项目也包含一个客户端/服务器工具但我们只需要它的库libnghttp2来给curl用。这个选项可以避免编译那些不必要的工具和依赖如libev,libc-ares等让编译过程更简洁。同样安装后需要更新链接器缓存echo /usr/local/nghttp2/lib | sudo tee /etc/ld.so.conf.d/nghttp2.conf sudo ldconfig3.3 可选编译安装 c-ares 库对于高并发场景我强烈建议加上这个库。cd /tmp # 下载 c-ares (以 1.27.0 为例) wget https://github.com/c-ares/c-ares/releases/download/cares-1_27_0/c-ares-1.27.0.tar.gz tar -xzf c-ares-1.27.0.tar.gz cd c-ares-1.27.0 ./configure --prefix/usr/local/c-ares make -j$(nproc) sudo make install echo /usr/local/c-ares/lib | sudo tee /etc/ld.so.conf.d/c-ares.conf sudo ldconfig至此所有核心依赖库都已就位并且系统已经知道去哪里找它们。4. Curl 的编译配置与安装主角curl终于要登场了。我们将使用最“纯净”的配置方式明确指定每一个依赖库的路径确保编译出的curl精确链接到我们刚刚安装的版本。4.1 获取源码与配置首先从 curl 官网下载最新稳定版的源码。这里以 8.8.0 为例。cd /tmp wget https://curl.se/download/curl-8.8.0.tar.gz tar -xzf curl-8.8.0.tar.gz cd curl-8.8.0接下来是最关键的./configure步骤。我们将通过环境变量PKG_CONFIG_PATH来指导pkg-config到我们自定义的安装目录下寻找库信息。# 设置 pkg-config 的搜索路径让它能找到我们自编译的库 export PKG_CONFIG_PATH/usr/local/tongsuo/lib/pkgconfig:/usr/local/nghttp2/lib/pkgconfig:/usr/local/c-ares/lib/pkgconfig:$PKG_CONFIG_PATH # 运行配置脚本 ./configure --prefix/usr/local/curl \ --with-openssl/usr/local/tongsuo \ --with-nghttp2/usr/local/nghttp2 \ --with-ca-bundle/etc/ssl/certs/ca-certificates.crt \ --with-zlib \ --with-ca-path/etc/ssl/certs \ --enable-ares \ --enable-http \ --enable-ftp \ --enable-tftp \ --enable-file \ --enable-ldap \ --enable-ldaps \ --enable-rtsp \ --enable-proxy \ --enable-dict \ --enable-telnet \ --enable-tftp \ --enable-pop3 \ --enable-imap \ --enable-smtp \ --enable-gopher \ --enable-manual \ --enable-libcurl-option \ --enable-ipv6 \ --enable-threaded-resolver \ --enable-verbose \ --enable-sspi \ --enable-crypto-auth \ --enable-tls-srp \ --enable-unix-sockets \ --enable-cookies \ --enable-symbol-hiding \ --enable-dnsshuffle \ --with-libssh2 \ --with-librtmp核心配置参数深度解析--with-openssl/usr/local/tongsuo这是支持国密的灵魂所在。它明确告诉curl使用位于/usr/local/tongsuo的 OpenSSL 兼容库即我们的 Tongsuo作为 TLS 后端。curl的./configure脚本会去该路径下的lib和include目录寻找库和头文件。--with-nghttp2/usr/local/nghttp2这是支持 HTTP/2 的关键。同样指定我们自编译的 nghttp2 库路径。--enable-ares启用异步 DNS 解析前提是你安装了 c-ares 并且pkg-config能找到它。--with-ca-bundle指定系统的 CA 证书捆绑包路径。这对于curl能够验证 HTTPS 证书的合法性至关重要。Ubuntu 通常使用这个路径。其他众多的--enable-*选项这些是启用curl对各种协议的支持。你可以根据实际需要精简但为了通用性我通常会把常用的都加上。--enable-libcurl-option和--enable-manual对于开发和使用很有帮助。配置成功验证运行./configure后请务必仔细查看输出的“Configuration summary”部分。你需要重点关注以下几行... TLS backends: OpenSSL version: Tongsuo 1.1.1w ... HTTP2: enabled (nghttp2) ... AsynchDNS: enabled (c-ares) ...如果TLS backends显示OpenSSL且版本信息包含Tongsuo同时HTTP2和AsynchDNS如果配置了显示为enabled那么恭喜你配置完全正确4.2 编译、安装与系统集成配置成功后编译和安装就是标准流程了。# 编译 make -j$(nproc) # 安装到 /usr/local/curl sudo make install安装完成后我们自编译的curl二进制文件位于/usr/local/curl/bin/curl库文件在/usr/local/curl/lib。为了能在终端中直接使用curl命令而不是输入完整路径我们需要将其加入到系统的PATH环境变量中。方法一推荐用户级配置修改当前用户的~/.bashrc文件。echo export PATH/usr/local/curl/bin:$PATH ~/.bashrc echo export LD_LIBRARY_PATH/usr/local/curl/lib:$LD_LIBRARY_PATH ~/.bashrc source ~/.bashrc方法二系统级配置创建软链接到系统目录。sudo ln -sf /usr/local/curl/bin/curl /usr/local/bin/curl现在在终端中直接输入curl --version你应该能看到令人兴奋的输出curl 8.8.0 (x86_64-pc-linux-gnu) libcurl/8.8.0 OpenSSL/1.1.1w Tongsuo zlib/1.2.11 nghttp2/1.58.0 c-ares/1.27.0 Release-Date: 2024-05-22 Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp Features: alt-svc AsynchDNS HSTS HTTP2 HTTPS-proxy IPv6 Largefile libz NTLM NTLM_WB SSL threadsafe TLS-SRP UnixSockets请特别关注OpenSSL/1.1.1w Tongsuo和Features: ... HTTP2 ...这两行它们确认了国密 SSL 和 HTTP/2 支持已成功集成。5. 功能验证与实战测试编译安装完成不代表万事大吉。我们必须用实际的测试来验证功能是否真的生效特别是国密支持因为它是非标准特性。5.1 验证 HTTP/2 支持HTTP/2 的验证相对简单。我们可以使用curl访问一个明确支持 HTTP/2 的网站如 https://nghttp2.org/并通过-vverbose参数查看详细的协议协商过程。curl -v --http2 https://nghttp2.org/ 21 | grep -i http/2在输出中你应该能看到类似* using HTTP/2和* Connection state changed (HTTP/2 confirmed)的信息。更直观的方法是使用--http2-prior-knowledge直接要求使用 HTTP/2或者访问一个只支持 HTTP/2 的服务。5.2 验证国密 SSL 支持这是验证的重点。由于公网上纯粹的国密 TLS 服务还不普遍我们需要搭建一个简单的测试环境。这里介绍两种方法方法一使用支持国密的本地测试服务如果你有现成的支持国密 TLS 的服务端如基于 Tongsuo 或 GmSSL 编译的 Nginx可以直接用curl连接其国密端口通常是 8443并使用-v查看握手细节。curl -v --tlsv1.2 --ciphers ECC-SM2-WITH-SM4-SM3 https://your-gmtls-server:8443/在* SSL connection using这一行你应该能看到类似于TLS_ECC_SM4_SM3的国密套件名称。方法二使用openssl s_client进行反向验证一个更稳妥的方法是用我们刚安装的、支持国密的 Tongsuo 的openssl工具来验证curl使用的库。首先写一个简单的 C 程序test_curl_ssl.c#include stdio.h #include curl/curl.h int main() { curl_global_init(CURL_GLOBAL_DEFAULT); CURL *curl curl_easy_init(); if(curl) { const char *ssl_version curl_version_info(CURLVERSION_NOW)-ssl_version; printf(cURL linked SSL version: %s\n, ssl_version); curl_easy_cleanup(curl); } curl_global_cleanup(); return 0; }编译并运行它gcc test_curl_ssl.c -o test_curl_ssl $(pkg-config --libs --cflags libcurl) LD_LIBRARY_PATH/usr/local/curl/lib:$LD_LIBRARY_PATH ./test_curl_ssl如果输出中包含 “Tongsuo” 字样则铁证如山curl确实链接了我们编译的国密库。5.3 完整功能测试脚本为了全面测试我通常会准备一个简单的 Bash 脚本一次性测试多个特性#!/bin/bash echo cURL 功能综合测试 echo 1. 版本信息 curl --version | head -2 echo echo 2. 支持的协议 curl --version | grep Protocols echo echo 3. 支持的特性 curl --version | grep Features echo echo 4. 测试 HTTP/2 连接 curl -s -o /dev/null -w HTTP 状态码: %{http_code}\n使用的协议: %{http_version}\n --http2 https://httpbin.org/get echo echo 5. 列出支持的 SSL 密码套件包含国密 # 注意此命令输出很长且国密套件名称可能因 Tongsuo 版本而异 /usr/local/tongsuo/bin/openssl ciphers -v | grep -E SM|GM | head -5运行这个脚本可以快速确认编译成果。6. 常见问题排查与经验心得即便按照步骤操作你也可能会遇到一些“坑”。下面是我在多次编译过程中总结出来的常见问题及其解决方案。6.1 编译依赖库时的问题问题1./configure提示找不到openssl或nghttp2。原因pkg-config没有找到对应的.pc文件。解决确认依赖库是否成功安装到--prefix指定的目录。确认PKG_CONFIG_PATH环境变量是否已正确设置包含了类似/usr/local/tongsuo/lib/pkgconfig的路径。可以在运行./configure前用echo $PKG_CONFIG_PATH检查。手动检查对应目录下是否存在.pc文件例如ls /usr/local/tongsuo/lib/pkgconfig。问题2编译 Tongsuo 时perl相关错误。原因Tongsuo 的配置脚本依赖 Perl但系统可能缺少必要的 Perl 模块。解决安装基础的 Perl 模块包sudo apt install perl-modules。6.2 编译 cURL 时的问题问题3make编译 cURL 时链接阶段报错undefined reference toSSL_CTX_set_ntls...。原因这是最典型的国密支持问题。curl的源码中调用了 Tongsuo 特有的国密函数如SSL_CTX_set_ntls但链接时找不到定义。根本原因是./configure阶段没有正确检测到 Tongsuo 的国密支持。解决确保 Tongsuo 编译时启用了enable-ntls。可以进入 Tongsuo 源码目录运行./config -t查看配置摘要。在配置 cURL 时显式指定 Tongsuo 路径--with-openssl/usr/local/tongsuo并且确保PKG_CONFIG_PATH设置正确。清理 cURL 源码目录 (make distclean或直接删除解压的目录重新解压)然后从头开始配置和编译。问题4运行新编译的curl时提示error while loading shared libraries: libcurl.so.4: cannot open shared object file。原因动态链接器找不到libcurl库。我们将其安装在了非标准路径 (/usr/local/curl/lib)。解决将库路径加入LD_LIBRARY_PATH环境变量如之前所述修改~/.bashrc。或者将库文件链接到系统库目录sudo ln -s /usr/local/curl/lib/libcurl.so.4 /usr/lib/不推荐可能引起系统包管理混乱。最规范的做法在编译 cURL 时将其安装到系统路径比如./configure --prefix/usr。但这可能会覆盖系统自带的curl需要谨慎。6.3 使用与调试心得心得1关于pkg-config的“魔法”pkg-config是管理编译标志的利器。它通过读取.pc文件提供了--cflags编译时头文件路径和--libs链接时库文件路径的标准输出。在配置 cURL 时设置PKG_CONFIG_PATH就是告诉它去哪里找这些.pc文件。你可以用pkg-config --list-all查看所有已注册的库。心得2版本兼容性是隐形的杀手务必注意依赖库之间的版本兼容性。例如一个非常新的nghttp2库可能依赖高版本的 C 运行时而一个较旧的curl源码可能尚未适配其 API。我的建议是对于生产环境尽量选择各项目的LTS长期支持版本或稳定版并在编译前查阅一下curl源码目录中的RELEASE-NOTES或docs/INSTALL.md看看其对依赖库版本的明确要求。心得3分离开发与运行环境如果你只是在某个特定项目中使用这个定制版curl可以考虑不将其安装到系统路径也不修改全局PATH。而是在项目的构建脚本如Makefile或CMakeLists.txt中通过设置CURL_INCLUDE_DIR和CURL_LIBRARY变量或者直接在编译命令中指定-I/usr/local/curl/include和-L/usr/local/curl/lib -lcurl来使用它。这样能保持系统环境的干净。心得4备份你的配置命令整个编译过程最宝贵的产出物除了最终的curl二进制文件就是你那一长串精心调试后的./configure命令。务必将它保存到脚本或文档中。下次在另一台机器上部署或者升级版本时它能帮你节省大量重新摸索的时间。编译自己的工具链看似复杂但一旦走通你对软件构建、依赖管理的理解会上一个台阶。这个支持国密和 HTTP/2 的curl不仅仅是完成了一个任务更是你应对未来更复杂定制化需求的一次成功预演。当你在命令行中敲下那个强化版的curl命令并看到它流畅地处理国密加密和 HTTP/2 流时那种对系统掌控的满足感是直接安装二进制包无法比拟的。