尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Ubuntu 22.04 源码编译支持国密SSL与HTTP/2的Curl实战指南

Ubuntu 22.04 源码编译支持国密SSL与HTTP/2的Curl实战指南 1. 项目概述与核心价值最近在做一个需要对接国内金融系统接口的项目对方明确要求通信必须支持国密SSLSM2/SM3/SM4协议栈同时为了性能考虑最好能启用HTTP/2。我手头的主力开发环境是Ubuntu 22.04 LTS系统自带的Curl虽然功能强大但默认并不支持国密算法。网上找了一圈现成的二进制包要么版本太旧要么编译选项不全总是不尽如人意。于是我决定自己动手从源码开始编译一个完全符合需求的“超级Curl”。这个“从零编译”的过程远不止是简单的./configure make。它涉及到多个关键依赖库的特定版本选择和编译配置比如用于国密支持的铜锁Tongsuo库、用于HTTP/2的nghttp2库还有zlib、openssl或与之兼容的库等。任何一个环节配置不当都会导致最终编译出的Curl功能缺失或运行异常。我把这次完整的踩坑和成功实践记录下来特别是那些官方文档里不会写的依赖库配置细节和编译参数背后的“为什么”希望能帮到有同样需求的开发者。无论你是需要在合规环境下进行安全通信还是单纯想深入了解如何定制一个强大的网络工具链这篇指南都能提供一条清晰的路径。2. 编译环境准备与核心依赖解析在开始编译主菜Curl之前我们必须先把“厨房”收拾好把需要的“食材”依赖库准备齐全。Ubuntu 22.04提供了一个比较干净的起点但默认仓库里的库版本往往是为了通用性我们需要的是特定功能或最新特性。2.1 系统基础环境搭建首先更新系统并安装编译所需的基础工具链。这一步确保我们有合适的编译器gcc、构建系统make、cmake和开发头文件。sudo apt update sudo apt upgrade -y sudo apt install -y build-essential autoconf automake libtool pkg-config注意build-essential这个元包非常重要它包含了gcc, g, make, libc-dev等核心编译工具。单独安装gcc可能还会缺一些必要的链接库。接下来我们需要为后续编译的库准备一个统一的安装目录。我不喜欢把自定义编译的库散乱地装到/usr/local下因为这会和系统包管理器管理的文件混在一起将来难以维护和清理。我更喜欢用一个独立的目录比如/opt/curl_custom。export CUSTOM_PREFIX/opt/curl_custom sudo mkdir -p $CUSTOM_PREFIX sudo chown -R $(whoami):$(whoami) $CUSTOM_PREFIX设置环境变量让编译器在接下来的步骤中能自动找到我们自定义安装的库。export PKG_CONFIG_PATH$CUSTOM_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH export LD_LIBRARY_PATH$CUSTOM_PREFIX/lib:$LD_LIBRARY_PATH export CPATH$CUSTOM_PREFIX/include:$CPATH export PATH$CUSTOM_PREFIX/bin:$PATH为了方便你可以把这几行export命令添加到你的~/.bashrc文件中然后执行source ~/.bashrc这样每次打开新终端都有效。但要注意如果你在同一个终端会话中完成所有编译中途不需要重新source。2.2 核心依赖库选型与作用剖析这是整个编译过程中最关键、最容易出错的部分。我们需要理解每个库的作用以及为什么必须选择特定的版本或编译选项。铜锁Tongsuo—— 国密算法的基石是什么铜锁是OpenSSL的一个分支由阿里云和蚂蚁集团维护它最大的特点就是原生并优先支持了中国商用密码算法标准SM2、SM3、SM4同时保持了与OpenSSL API的高度兼容。这意味着使用铜锁替换OpenSSL我们几乎不需要修改应用代码就能获得国密能力。为什么是它虽然也有其他国密库如GmSSL但铜锁的兼容性和活跃度目前看来是最好的。它被广泛应用于蚂蚁集团和阿里云的诸多产品中经过大规模生产环境验证。版本选择务必从官方GitHub仓库获取最新的稳定版或LTS版。我编译时使用的是Tongsuo-8.3.0。主分支的代码可能包含实验性功能对于生产环境稳定版是更安全的选择。nghttp2 —— HTTP/2协议的支持者是什么一个实现HTTP/2协议C库的库。Curl本身不实现HTTP/2协议它依赖于底层的库如nghttp2来提供此功能。为什么需要HTTP/2相比HTTP/1.1有多路复用、头部压缩、服务器推送等优势能显著提升网络性能尤其是在需要频繁请求小资源的场景下。现在很多主流API和网站都已支持HTTP/2。版本选择同样建议使用较新的稳定版本。我使用的是nghttp2-1.55.1。较旧的版本可能不支持最新的HTTP/2特性或存在已知漏洞。zlib —— 数据压缩的标配是什么一个广泛使用的无损数据压缩库。HTTP协议中常用的Content-Encoding: gzip/deflate就依赖于它。为什么需要即使系统已安装zlib我们仍然建议编译一个相同版本到自定义目录以确保所有库链接的一致性避免运行时出现版本冲突。使用系统自带的通常也没问题但为了环境的纯净和可复现我选择自己编译。版本选择选择一个稳定的版本即可如zlib-1.2.13。其他可选但推荐的库zstd一种比zlib更高效的压缩算法。如果目标服务器支持使用zstd可以进一步提升传输效率。Curl也支持编译时链接zstd。brotliGoogle推出的另一种压缩算法在Web领域应用广泛尤其与HTTP/2/3配合很好。libssh2 / libpsl分别用于SCP/SFTP协议和支持公共后缀列表Public Suffix List。根据你的实际需求决定是否编译。实操心得依赖库的编译顺序有时很重要。一个通用的原则是先编译基础库如zlib再编译那些依赖它的库如openssl/铜锁可能需要zlib做压缩支持最后编译上层应用Curl。虽然现代构建系统通常能处理好依赖但按顺序编译可以避免一些奇怪的链接错误。3. 分步编译依赖库实战理论讲完我们开始动手。请确保你始终在之前设置好环境变量的终端中操作。3.1 编译安装 zlibzlib是最基础的依赖我们先处理它。cd ~ wget https://zlib.net/zlib-1.2.13.tar.gz tar -xzf zlib-1.2.13.tar.gz cd zlib-1.2.13zlib的编译系统比较传统用的是configure脚本。我们需要指定安装前缀。./configure --prefix$CUSTOM_PREFIX make make test # 强烈建议运行测试确保编译正确 make install编译完成后检查一下是否安装成功ls -la $CUSTOM_PREFIX/lib/libz*应该能看到libz.so等库文件。3.2 编译安装 nghttp2接下来是HTTP/2的支持库。cd ~ wget https://github.com/nghttp2/nghttp2/releases/download/v1.55.1/nghttp2-1.55.1.tar.gz tar -xzf nghttp2-1.55.1.tar.gz cd nghttp2-1.55.1nghttp2默认用CMake构建我们创建一个单独的构建目录。mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX$CUSTOM_PREFIX \ -DENABLE_LIB_ONLYON # 只编译库文件不编译客户端、服务器等可执行程序 make -j$(nproc) # 使用多核并行编译加快速度 sudo make install # 这里可能需要sudo因为要向系统目录写入文件注意事项-DENABLE_LIB_ONLYON这个选项非常关键。我们只需要nghttp2的C库文件libnghttp2.so来给Curl使用不需要它的命令行工具。这样可以减少不必要的依赖和编译时间。3.3 编译安装铜锁Tongsuo这是支持国密的关键步骤配置选项需要格外仔细。cd ~ git clone https://github.com/Tongsuo-Project/Tongsuo.git cd Tongsuo # 切换到稳定版本标签例如 8.3.0 git checkout 8.3.0铜锁的配置脚本参数非常丰富。我们需要启用国密算法并指定安装路径和zlib的位置。./config --prefix$CUSTOM_PREFIX \ --with-zlib-include$CUSTOM_PREFIX/include \ --with-zlib-lib$CUSTOM_PREFIX/lib \ enable-sm2 enable-sm3 enable-sm4 enable-ntls # 关键参数解释一下关键的./config参数--prefix指定安装目录。--with-zlib-include/--with-zlib-lib明确告诉配置脚本我们自定义编译的zlib在哪里。如果不指定它可能会找到系统自带的zlib导致链接不一致。enable-sm2enable-sm3enable-sm4这三个是启用国密算法支持的核心开关必须加上。enable-ntls启用国密传输层安全协议National TLS这是基于国密算法的TLS协议扩展。如果你的对接方要求使用NTLS这个选项必须开启。配置完成后进行编译和安装。make -j$(nproc) make test # 运行测试套件验证国密算法功能是否正常这步很重要 make install_sw # 安装软件库和头文件不安装文档安装完成后铜锁的可执行文件如openssl命令行工具和库文件都会安装在$CUSTOM_PREFIX下。你可以验证一下国密算法是否可用$CUSTOM_PREFIX/bin/openssl version # 应该显示 Tongsuo 的版本信息 $CUSTOM_PREFIX/bin/openssl ecparam -list_curves | grep sm2 # 应该能看到 SM2 相关的曲线参数4. 编译安装定制化Curl所有依赖就绪现在可以编译主角Curl了。同样我们去官网下载源码。cd ~ wget https://curl.se/download/curl-8.6.0.tar.gz tar -xzf curl-8.6.0.tar.gz cd curl-8.6.0Curl使用autoconf构建系统配置脚本configure功能强大。我们需要通过参数告诉它使用我们自定义的铜锁库而不是系统OpenSSL并启用HTTP/2等功能。./configure --prefix$CUSTOM_PREFIX \ --with-ssl$CUSTOM_PREFIX \ --with-nghttp2$CUSTOM_PREFIX \ --with-zlib$CUSTOM_PREFIX \ --enable-http \ --enable-https \ --enable-ipv6 \ --enable-manual \ --enable-libcurl-option \ --enable-websockets \ --with-ca-bundle/etc/ssl/certs/ca-certificates.crt \ --with-ca-path/etc/ssl/certs逐项解释关键配置参数--with-ssl$CUSTOM_PREFIX这是最核心的参数。它指示Curl使用我们安装在/opt/curl_custom下的铜锁库。Curl的构建系统会在这个目录下寻找libssl.so、libcrypto.so和头文件。因为铜锁兼容OpenSSL API所以Curl会把它当作OpenSSL来链接和使用从而无缝获得国密能力。--with-nghttp2$CUSTOM_PREFIX指定我们自定义编译的nghttp2库路径启用HTTP/2支持。--with-zlib$CUSTOM_PREFIX指定自定义的zlib路径。--enable-http--enable-https启用HTTP和HTTPS协议支持这是默认的但显式声明更清晰。--enable-ipv6启用IPv6支持。--enable-websockets启用WebSockets协议支持按需选择。--with-ca-bundle--with-ca-path指定系统的CA证书存储位置这样编译出的Curl才能验证服务器的SSL证书。Ubuntu 22.04通常使用ca-certificates包提供的路径。运行./configure后请仔细查看输出摘要。你会看到类似下面的信息确认关键特性已启用... curl version: 8.6.0 ... SSL: enabled (OpenSSL) SSL version: OpenSSL/3.0.0 TLS 1.3: enabled HTTP2: enabled HTTP3: disabled (ngtcp2, nghttp3) HTTPS-proxy: enabled Brotli: disabled Zstd: disabled GSS-API: no TLS-SRP: no resolver: default (threaded) IPv6: enabled Unix sockets: enabled IDN: disabled Build libcurl: Sharedyes, Staticyes Built-in manual: enabled ...重点检查SSL: enabled (OpenSSL)和SSL version: OpenSSL/...这行它表明Curl将链接到我们指定的SSL库实际是铜锁。HTTP2: enabled表明nghttp2支持已启用。如果看到SM2/SM3/SM4相关的字样那更好但Curl的配置摘要通常不显示这个需要后续用命令测试。如果配置成功就可以开始编译了。make -j$(nproc) make test # 运行Curl的测试套件虽然不是必须但能帮你发现潜在问题 make install5. 验证与测试编译成果编译安装完成激动人心的验证时刻到了。5.1 基础功能验证首先检查我们新编译的Curl版本和功能列表。$CUSTOM_PREFIX/bin/curl --version输出会很长你需要关注几个关键点第一行curl 8.6.0 (x86_64-pc-linux-gnu) ...确认版本。Protocols:部分应该包含httphttps。Features:部分这是重点。你应该能看到AsynchDNS(异步DNS)HTTPS-proxy(HTTPS代理)IDN(国际化域名)IPv6Largefile(大文件支持)LibSSH(如果编译了)NTLM(NTLM认证)NTLM_WBSSL(必须存在)HTTP2(必须存在)UnixSocketsalt-svc(Alt-Svc支持)brotli(如果启用)gsasl(如果启用)hsts(HSTS)libz(zlib压缩)zstd(如果启用)注意Features列表里通常不会直接显示“国密”或“SMx”国密支持是通过底层的SSL库铜锁提供的对Curl来说是透明的。5.2 国密SSL连接测试这是最核心的测试。我们需要一个支持国密SSL的服务器来测试。如果没有现成的可以用铜锁自带的s_server工具在本地模拟一个。第一步生成国密SM2证书用于测试铜锁的openssl命令已经支持国密。我们生成一个自签名的SM2证书。cd /tmp # 1. 生成SM2私钥 $CUSTOM_PREFIX/bin/openssl ecparam -genkey -name SM2 -out sm2.key # 2. 生成证书签名请求(CSR) $CUSTOM_PREFIX/bin/openssl req -new -key sm2.key -out sm2.csr -subj /CCN/STBeijing/LBeijing/OTest/CNlocalhost # 3. 自签名生成证书 $CUSTOM_PREFIX/bin/openssl x509 -req -days 365 -in sm2.csr -signkey sm2.key -out sm2.crt第二步启动一个支持国密的HTTPS测试服务器在一个终端窗口运行$CUSTOM_PREFIX/bin/openssl s_server -accept 8443 \ -key /tmp/sm2.key \ -cert /tmp/sm2.crt \ -cipher SM2-WITH-SM4-SM3 \ -www # -www 选项让它对任何请求都返回一个状态页面这个命令会在localhost:8443启动一个TLS服务器并指定使用SM2-WITH-SM4-SM3这个国密套件。第三步用新编译的Curl连接测试在另一个终端窗口运行$CUSTOM_PREFIX/bin/curl -v -k https://localhost:8443解释参数-v输出详细过程方便我们观察。-k忽略证书验证因为我们用的是自签名证书。在-v的详细输出中你需要寻找这些关键信息* SSL connection using TLSv1.3 / xxxx * ALPN, server accepted to use h2 * Server certificate: * subject: CCN; STBeijing; LBeijing; OTest; CNlocalhost * start date: ... GMT * expire date: ... GMT * issuer: CCN; STBeijing; LBeijing; OTest; CNlocalhost * SSL certificate verify result: self-signed certificate (18), continuing anyway.如果连接成功并且你能看到服务器返回的OpenSSL s_server状态页面就说明Curl已经能够成功使用国密SSL套件与服务器进行握手和通信了。注意看SSL connection using这一行如果服务器和客户端都支持国密且协商成功这里可能会显示具体的密码套件取决于铜锁和Curl的详细输出级别。5.3 HTTP/2协议测试测试HTTP/2支持相对简单我们可以连接一个已知支持HTTP/2的公共网站如https://http2.akamai.com/或者用我们刚启动的本地服务器如果铜锁s_server支持但默认可能不支持。更简单的方法是使用Curl的--http2选项和-I只获取头部来测试一个支持HTTP/2的网站$CUSTOM_PREFIX/bin/curl -I --http2 https://nghttp2.org/ -v在详细输出中寻找* ALPN, offering h2 * ALPN, offering http/1.1 ... * ALPN, server accepted to use h2 * using HTTP/2看到using HTTP/2就说明HTTP/2协议协商成功功能正常。6. 常见问题排查与解决方案实录编译过程很少一帆风顺下面是我在多次编译中遇到的典型问题及解决方法。6.1 编译铜锁时遇到“undefined reference to zlibVersion’”问题现象在make铜锁时链接阶段报错提示找不到zlibVersion等zlib函数。原因分析这通常是因为./config时没有正确找到我们自定义编译的zlib。虽然我们指定了--with-zlib-include和--with-zlib-lib但有时配置脚本的查找逻辑有问题或者系统里存在多个zlib版本导致冲突。解决方案确保环境变量生效确认PKG_CONFIG_PATH和LD_LIBRARY_PATH包含了$CUSTOM_PREFIX的路径。可以echo $PKG_CONFIG_PATH检查。显式指定zlib在./config命令中除了--with-zlib-include和--with-zlib-lib还可以尝试更直接的参数./config ... -I$CUSTOM_PREFIX/include -L$CUSTOM_PREFIX/lib但注意铜锁的config脚本可能不直接识别-I和-L。最可靠的方法是清理重来彻底删除铜锁源码目录重新解压或克隆然后先确保zlib已正确安装到$CUSTOM_PREFIX再重新执行./config。有时旧的编译中间文件会导致问题。检查zlib安装用ls $CUSTOM_PREFIX/lib/pkgconfig/zlib.pc检查pkg-config文件是否存在。如果不存在可能是zlib编译安装时没有生成可以尝试重新编译zlib或者手动创建。6.2 编译Curl时configure报错“SSL libraries not found”问题现象运行Curl的./configure时提示找不到SSL库。原因分析--with-ssl$CUSTOM_PREFIX参数指定的路径下没有找到正确的libssl.so和libcrypto.so库或者找到了但版本不兼容比如找到的是系统自带的OpenSSL而不是铜锁。解决方案确认铜锁安装成功运行ls $CUSTOM_PREFIX/lib/libssl.so* $CUSTOM_PREFIX/lib/libcrypto.so*确认库文件存在。检查pkg-config运行pkg-config --libs --cflags libssl。这个命令应该能输出我们自定义铜锁库的链接和编译标志。如果它输出的是系统OpenSSL的信息说明PKG_CONFIG_PATH环境变量没有设置正确或者铜锁没有安装libssl.pc文件。可以手动检查$CUSTOM_PREFIX/lib/pkgconfig/下是否有libssl.pc和libcrypto.pc。强制指定路径如果pkg-config不工作可以尝试在configure时手动指定库路径但这不是推荐做法可能引发其他问题./configure ... --with-ssl$CUSTOM_PREFIX LDFLAGS-L$CUSTOM_PREFIX/lib CPPFLAGS-I$CUSTOM_PREFIX/include最根本的方法确保在编译铜锁时其make install步骤正确安装了.pc文件。有时需要以root权限或使用sudo make install_sw。6.3 运行自定义Curl时提示“error while loading shared libraries: libnghttp2.so.14: cannot open shared object file”问题现象编译安装成功但运行$CUSTOM_PREFIX/bin/curl时提示找不到某个共享库如libnghttp2.so.14。原因分析动态链接器ld没有在我们自定义的库目录$CUSTOM_PREFIX/lib中搜索这个库。虽然我们设置了LD_LIBRARY_PATH环境变量但它可能只在当前shell会话中有效或者在某些情况下比如通过sudo或cron调用不被继承。解决方案临时解决在当前终端确认LD_LIBRARY_PATH已设置并包含正确路径然后source ~/.bashrc或重新打开终端。永久解决推荐将自定义库路径添加到系统的库加载配置中。echo $CUSTOM_PREFIX/lib | sudo tee /etc/ld.so.conf.d/curl_custom.conf sudo ldconfig执行sudo ldconfig刷新动态链接器缓存。之后系统所有程序都能找到这个路径下的库了。静态链接高级在编译Curl时可以尝试静态链接一些核心依赖库避免运行时依赖问题。但这会显著增大Curl二进制文件的体积。在Curl的./configure中可以尝试传递--disable-shared来生成静态库但对于可执行文件更常用的方法是修改编译标志但这需要更深入的操作不推荐新手尝试。6.4 国密连接测试失败提示“no ciphers available”或“sslv3 alert handshake failure”问题现象用Curl连接国密测试服务器时握手失败。原因分析密码套件不匹配客户端Curl和服务器s_server没有协商出共同的国密密码套件。可能服务器端指定的套件如-cipher SM2-WITH-SM4-SM3客户端不支持或者反之。证书问题自签名证书不被信任且没有使用-k参数。协议版本问题国密套件可能只在特定的TLS版本下有效需要双方协商一致。解决方案检查服务器密码套件确保s_server启动时使用的-cipher参数是有效的国密套件。可以用$CUSTOM_PREFIX/bin/openssl ciphers -v查看铜锁支持的所有套件找到包含SM2、SM4、SM3的套件名称。指定客户端密码套件在Curl命令中用--ciphers选项指定尝试的国密套件。$CUSTOM_PREFIX/bin/curl -v -k --ciphers SM2-WITH-SM4-SM3 https://localhost:8443启用详细调试在Curl命令中加入--trace或--trace-ascii到一个文件可以获取最详细的SSL握手过程帮助定位问题。$CUSTOM_PREFIX/bin/curl -v -k --trace trace.txt https://localhost:8443然后查看trace.txt文件搜索“Cipher suite”等关键词。测试基础TLS连接先用一个普通的TLS套件如ECDHE-RSA-AES256-GCM-SHA384测试服务器是否正常排除非国密相关的问题。6.5 编译时间过长或内存不足问题现象make -j$(nproc)时系统卡顿甚至因内存不足OOM被杀死。原因分析$(nproc)会获取你CPU的核心数并行编译任务过多可能导致内存需求激增尤其是在编译铜锁或Curl这种大型项目时。解决方案减少并行任务数将-j$(nproc)改为-j2或-j4减少同时进行的编译任务。make -j2增加交换空间如果物理内存RAM较小可以临时增加交换文件swap。# 创建一个4GB的交换文件 sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # 编译完成后可以关闭并删除 # sudo swapoff /swapfile sudo rm /swapfile分步编译对于铜锁可以只编译必要的部分。但通常不推荐因为可能会缺失功能。7. 集成到系统与环境变量配置编译好的Curl放在/opt/curl_custom/bin/curl每次都要输入完整路径很麻烦。我们有几种方法把它集成到系统中。7.1 创建软链接最简单直接创建一个符号链接到系统路径/usr/local/bin/这样在任何地方都可以直接输入curl调用我们自定义的版本。sudo ln -sf $CUSTOM_PREFIX/bin/curl /usr/local/bin/curl优点简单粗暴立即生效。缺点会覆盖系统原有的/usr/local/bin/curl如果存在。如果系统是通过包管理器安装的curl它通常在/usr/bin/curl所以影响不大。但最好先检查一下which curl。7.2 调整PATH环境变量更优雅修改用户的PATH环境变量让shell优先搜索我们的自定义目录。编辑~/.bashrc文件在末尾添加export PATH/opt/curl_custom/bin:$PATH然后执行source ~/.bashrc。验证运行which curl应该输出/opt/curl_custom/bin/curl。运行curl --version确认版本是我们编译的8.6.0。优点不会覆盖系统命令只是优先使用我们的版本。可以通过完整路径/usr/bin/curl调用系统版本。缺点只对当前用户生效且需要source或重新登录。7.3 使用别名最灵活如果你只是偶尔需要使用自定义的Curl或者想保留系统Curl作为默认可以使用别名alias。在~/.bashrc中添加alias mycurl/opt/curl_custom/bin/curl然后source ~/.bashrc。之后就可以用mycurl命令来调用我们编译的版本了。优点完全不影响系统命令清晰区分。缺点需要记住并使用不同的命令名。7.4 验证系统集成无论采用哪种方式集成后都请进行最终验证版本验证curl --version | head -1确保输出的是curl 8.6.0。功能验证curl --version | grep Features确认HTTP2和SSL在特性列表中。国密测试再次运行之前章节的国密服务器和客户端测试但这次可以直接用curl命令如果用了软链接或PATH调整。经过以上步骤你就成功在Ubuntu 22.04系统上获得了一个从源码编译、深度定制、同时支持国密SSL和HTTP/2协议的强大Curl工具。这个过程虽然有些繁琐但带来的控制力和灵活性是直接安装二进制包无法比拟的。特别是对于有特定加密协议要求的开发和生产环境自己掌控编译链和依赖是保证环境一致性和安全性的重要一环。
返回列表