
简介openssl-1.1.1k.tar.gz 是 OpenSSL 1.1.1 稳定分支的源码压缩包面向需要集成 SSL/TLS 加密能力的开发者、运维与系统管理员可用于搭建安全通信环境、排查证书链路以及编译定制加密库。内容围绕 TLS 1.0 至 1.3 协议支持、AES/RSA/ECC 等对称与非对称算法、哈希与 HMAC、X.509 证书和密钥管理展开并附带 s_client、s_server、genpkey、req、x509 等命令行工具便于测试连接、生成密钥与处理证书C API 也方便嵌入自有服务。压缩包约 9.37MB文件总数暂显示为 0类型明细暂无数据解压后可按标准配置、编译、安装流程适配不同平台并借助 AES-NI 等硬件加速关注性能表现。已有 413 人学习适合希望理解 OpenSSL 组成、完成安全加固与版本升级的中高级读者参考。1. 拿到 openssl-1.1.1k.tar.gz 先别输入 ./config1.1.1 已经停止维护为什么还要编译它某天 A 同学把一个老网关的构建脚本丢过来日志只有一行libssl.so.1.1: cannot open shared object file。他第一反应是缺包安装了系统自带的开发包问题反而更乱头文件来自一个版本运行库来自另一个版本undefined reference to SSL_CTX_set_ciphersuites报错一直出现。openssl-1.1.1k.tar.gz就是给这种场景用的资源它是 OpenSSL 1.1.1 分支第 k 个补丁版的完整源码包2021 年 3 月发布修的是两个安全公告里的问题一个是重协商 ClientHello 里处理 signature_algorithms 扩展时的空指针一个是开启 X509_V_FLAG_X509_STRICT 时证书链校验可被绕过。1.1.1 分支在 2023 年 9 月已经完成整个支持周期新项目没有理由再用它。但这个包的价值不在“新”在“可控”一套自己编译、自己定路径、自己决定裁剪哪些算法和协议的 TLS 实现能原地放进几十个不愿重新编译的老产物里。适合还在维护锁定 1.1.1 生态的中间件、要给某个不兼容 3.x 的第三方 SDK 配运行库、或者需要静态链接进单个二进制的场景。反过来如果是全新服务直接使用 3.x 分支不要在 1.1.1 上继续增加投入。2. 从 tar.gz 到可用的 Makefile解包、依赖自检与 configure这一章只做一件事把压缩包变成一份能make的构建树。听起来是三行命令实际上九成编译失败都发生在这三步之内——依赖没装齐、用错了解包目录、把./config当成万能入口。2.1 解包前后的一次依赖自检先确认工具链齐不齐比编到一半报perl: command not found体面得多。OpenSSL 的构建系统完全由 Perl 驱动没有 Perl 连 Makefile 都生成不出来。# 解包注意不要在 /tmp 这种小分区里做中间产物很快膨胀到几百兆 tar -xzf openssl-1.1.1k.tar.gz cd openssl-1.1.1k # 四项最低依赖缺哪个补哪个 perl -v | head -2 # 需要 5.10 以上低了 Configure 直接罢工 gcc --version | head -1 # 建议 4.8 以上太老的 gcc 对 -fPIC 与 TLS 支持有坑 make --version | head -1 # 必须是 GNU makeBSD make 会挂 ld --version | head -1 # 交叉编译时还要额外准备目标平台的 binutilsperl -v那一行输出里重点看版本号This is perl 5, version 30这类信息说明没问题。make要确认是 GNU 版本某些精简系统里make指向的是别的实现$(nproc)之类的写法会失效。如果要开zlib选项让 TLS 层支持压缩证书传输还需要 zlib 的头文件。Debian 系叫zlib1g-devRPM 系叫zlib-devel。不开这个选项就不用装no-zlib是默认行为之一。提示编译前先df -h .看一眼剩余空间。完整编译加测试构建树占用会明显超过源码包本身空间不够时make会在链接阶段报No space left on device看起来像链接器出问题其实是磁盘满了。2.2 ./config、./Configure 与 configdata.pm 的关系很多人把./config当成唯一入口其实它只是./Configure的一层薄包装./config会先猜当前平台的 target 名字比如linux-x86_64再带着这个 target 去调./Configure。真正的构建逻辑、选项解析、Makefile 生成都发生在./Configure里。这个区别在两种情况下很关键。第一种是交叉编译自动猜出来的 target 一定是错的必须显式给./Configure linux-aarch64 ...。第二种是排查参数有没有生效——执行完./config之后构建树里会多出一个configdata.pm它记录了本次配置的全部结论# 把本次配置的最终结果打印出来包括 target、编译选项、哪些算法被关掉 perl configdata.pm --dump | head -60我一般会把这条命令的输出重定向到一个build-config.txt存档。半年后有人问“这套库到底支不支持 TLS 1.3、开没开 shared”翻这个文件比翻构建脚本可靠因为脚本可能被改过而configdata.pm是当时那一刻的快照。--dump里要重点看三段Command line确认选项原样进来了Enabled features确认shared、zlib在列Disabled features确认你砍掉的算法确实被砍掉了——有时候选项拼错比如写成no_weak_ssl_ciphersConfigure不报错但也不生效只能从这里看出来。2.3 一套能照着敲的完整流程下面这套参数是我在多数 Linux 服务器上会用的版本注释里写了每一项的理由你可以按需删减。./config \ --prefix/usr/local/openssl-1.1.1k \ --openssldir/usr/local/openssl-1.1.1k/ssl \ shared \ zlib \ no-ssl3 \ no-comp \ no-weak-ssl-ciphers \ -fPIC \ -Wl,-rpath,/usr/local/openssl-1.1.1k/lib \ -Wl,-z,relro -Wl,-z,now # -j 并发数按核数给别开太大OpenSSL 的某些测试有顺序依赖 make -j$(nproc) # 测试是可选但强烈建议的一步HARNESS_JOBS 控制测试并发 HARNESS_JOBS4 make test # install_sw 只装库、头文件、可执行文件和 pc 文件不装 man 手册 make install_sw逐项说明--prefix决定安装根目录这里故意带上版本号openssl-1.1.1k好处是以后要升级到 1.1.1w 时可以并存切换只改环境变量不用卸载重装。--openssldir决定配置文件openssl.cnf、证书目录、私钥目录的位置。它默认会跟随--prefix但显式写出来更清楚尤其是当你想把配置和二进制分离到不同分区的时候。shared生成libssl.so.1.1和libcrypto.so.1.1。动态库的 SONAME 只带主次版本号不带补丁号所以 1.1.1 系列内部升级是二进制兼容的这也是为什么libssl.so.1.1这个文件名能撑这么多年。-Wl,-rpath,...把库搜索路径直接写进可执行文件的 ELF 头里这是后面第 4 章要展开的关键一招。-Wl,-z,relro -Wl,-z,now是链接时的加固选项让 GOT 表在启动后只读属于顺手加上、成本几乎为零的防御。make test跑完会打印一行汇总形如Result: PASS。如果出现FAILED先别急着make install第 5 章专门讲这类失败怎么定位。HARNESS_JOBS4在核多的机器上能把测试时间压下来但某些老内核上并发跑 TLS 测试会有端口抢占报错看起来像随机失败这时候降到 1 重跑一遍就能确认。3. 编译参数逐项拆解--prefix、shared、no-xxx 到底在改什么参数不是照抄就完事。同一份源码不同参数组合出来的库在行为、体积、甚至安全边界上完全不同。这章把最常改的几个开关拆开讲包括为什么有些开关在 1.1.1 里已经是默认值、写了等于白写。3.1 --prefix 与 --openssldir为什么要拆成两个目录这两个参数经常被混为一谈。--prefix是安装根决定bin/、lib/、include/去哪--openssldir是运行时数据目录决定openssl.cnf、certs/、private/、misc/去哪。默认情况下后者等于$prefix/ssl。拆开的价值在于权限和生命周期不同。二进制和库更新频繁配置和证书基本不动把它们放在同一棵树下升级时要么整棵树覆盖配置被冲掉要么小心翼翼地挑着删容易漏。我的习惯是把--prefix放在系统盘--openssldir指到数据盘上独立的ssl目录升级时只替换bin和lib配置原地不动。配置目录还有一层影响openssl version -a输出里的OPENSSLDIR就是它。很多“改了 cnf 却不生效”的问题根因是改了/etc/ssl/openssl.cnf而二进制实际读的是$prefix/ssl/openssl.cnf。先看一眼OPENSSLDIR指向哪再动手改文件。3.2 shared 与 no-shared动态库还是静态库不写shared时默认只产出.a静态库。这对打包成单文件二进制的场景很友好——产物扔到任何同架构机器上都能跑不依赖目标机装了什么版本的 OpenSSL。代价是安全更新困难。静态链接意味着库代码已经烧进二进制哪天 1.1.1 分支再发补丁版你得把所有链了它的程序重新编译一遍。1.1.1 已经停止维护这个代价现在体现得没那么明显但如果你的产物生命周期很长这一点必须在设计阶段就考虑清楚。shared生成动态库好处是多个程序共享同一份代码、修复只需换.so文件坏处是运行时找不到库第 5 章第一个坑就是这个。还有一点常被忽略开了shared之后-fPIC是自动加的如果只编静态库又想让静态库能被链接进so需要手动补-fPIC否则链接阶段会报relocation R_X86_64_32 against ... can not be used when making a shared object。这个报错信息很不直观第一次见容易卡半天。我的默认组合是shared同时用 rpath 把路径固定到二进制里兼顾升级便利和运行可靠性。只有当产物需要分发到不受控机器上时才切到no-shared。3.3 算法与协议开关砍掉的每一项都要能说清代价no-xxx系列开关看着像“精简优化”实际上每一刀都在改变协议能力边界砍错了会在联调时表现为“对面连不上”而不是编译报错。开关实际作用砍掉的代价建议no-ssl3移除 SSLv3 协议实现1.1.1 里默认已关写了主要是显式声明写上无副作用no-comp移除 TLS 层压缩极少数老客户端依赖压缩写上压缩本身有 CRIME 类风险no-weak-ssl-ciphers移除 RC4、3DES 等弱套件需要兼容极老设备时会握手失败内网新系统写老设备兼容场景别写no-tls1_31.1.1 里不存在这个开关想关 TLS 1.3 只能靠代码或配置层别写写了也不生效enable-ec_nistp_64_gcc_128用 128 位整数加速特定曲线需要编译器支持__int128x86_64 上建议开性能提升可感知表格最后一行值得单独说。enable-ec_nistp_64_gcc_128不是默认开启的因为它在 32 位平台上没有意义、在部分编译器上会编不过。x86_64 GCC 环境下加上它P-256 曲线上的 ECDHE 运算会明显加快握手延迟能降下来一点。加之前先确认编译能过编不过就是编译器太老删掉这个选项即可。no-tls1_3那行是很多人踩过的3.x 分支确实提供了这个开关1.1.1 分支没有。想限制 TLS 1.3 只能在openssl.cnf里设MaxProtocol TLSv1.2或者在应用代码里调SSL_CTX_set_max_proto_version。把 3.x 的编译参数照搬到 1.1.1 上Configure会静默忽略不认识的选项你以为关掉了实际还开着。3.4 交叉编译与多架构target 名字怎么选换个平台编译./config的自动猜测必然失败必须走./Configure target。常用的几个目标名target适用场景额外注意linux-x86_64常规 64 位 x86 服务器自动猜测通常就是这个linux-x8632 位 x86需要-m32和多架构运行库支持linux-aarch64ARM 64 位服务器需要对应的aarch64-linux-gnu-gcclinux-armv432 位 ARM 嵌入式常配-marcharmv7-a一起给mingw64交叉编译到 Windows需要 MinGW 工具链符号导出行为不同交叉编译时还要显式指定工具链前缀否则Configure仍然会用本机的gcc# 以 AArch64 为例CC/CROSS_COMPILE 都要给缺一个就会用错编译器 CROSS_COMPILEaarch64-linux-gnu- \ CCaarch64-linux-gnu-gcc \ ./Configure linux-aarch64 \ --prefix/opt/openssl-1.1.1k-arm64 \ --openssldir/opt/openssl-1.1.1k-arm64/ssl \ shared no-comp no-weak-ssl-ciphers make -j$(nproc)编完不要在本机跑make test——产物是 ARM 指令本机 x86 跑不起来会报Exec format error。测试要么放到目标机上跑要么用 qemu-user 起一个用户态模拟环境后者对make test里涉及网络端口的用例支持不完整容易假失败建议只做冒烟验证把libssl.so.1.1拷到目标机用它的openssl version -a确认OPENSSLDIR和平台信息正确即可。4. 让程序真正找到这套库三种链接方式与装完必跑的验证make install_sw成功的那一刻最容易产生错觉以为万事大吉。实际上编译成功和运行成功是两件事中间隔着一整套动态链接器查找规则。这一章解决“库装好了但程序找不到”的问题并给出装完就该跑的三条验证命令。4.1 三种让程序找到新库的方式方式一环境变量LD_LIBRARY_PATH。最直接也最容易翻车。# 只对当前 shell 生效换个终端或者被 systemd 拉起就没了 export LD_LIBRARY_PATH/usr/local/openssl-1.1.1k/lib:$LD_LIBRARY_PATH这个变量的作用域是进程及其子进程通过su切换到其他用户、被systemd拉起、被cron定时执行时全都不继承。线上服务用这种方式某次重启后就会出现libssl.so.1.1: cannot open shared object file而你在终端里手动跑一遍又是好的——这就是它最让人头疼的地方。方式二/etc/ld.so.conf.d/加配置文件 ldconfig。系统级生效重启后仍在。# 新增一行库路径然后刷新缓存让动态链接器认识这个目录 echo /usr/local/openssl-1.1.1k/lib /etc/ld.so.conf.d/openssl-1.1.1k.conf ldconfig ldconfig -p | grep libssl最后那条ldconfig -p是必须的验证它列出动态链接器缓存里所有已知的libssl。如果输出的顺序里系统自带的/lib/x86_64-linux-gnu/libssl.so.1.1排在前面那么程序仍然会加载旧库——缓存是按顺序匹配的不是按版本号挑新的。方式三rpath把路径写进 ELF。最可靠但需要重新编译。# 编译期就把搜索路径固化到产物里运行时优先级高于 ld.so.conf readelf -d /usr/local/openssl-1.1.1k/bin/openssl | grep -i rpath # 预期输出0x000000000000000f (RPATH) Library rpath: [/usr/local/openssl-1.1.1k/lib]三种方式里我一般对新编译的程序用方式三编译期就带上-Wl,-rpath对已经编译好、不能重编的第三方二进制用方式二。方式一只用于临时调试绝不写进启动脚本——它是最常见的问题来源。4.2 让第三方库和构建系统指向这套 OpenSSL自编译的 OpenSSL 装好之后下一个问题是让别的库找到它。不同构建系统的写法不一样写错了就会出现“头文件是新版、链的是旧库”的错配报错形式通常是未定义符号# Python 生态先确认解释器当初链接的是哪套库 python3 -c import ssl; print(ssl.OPENSSL_VERSION) # CMake 项目给根目录让它自己找 include 和 lib cmake -DOPENSSL_ROOT_DIR/usr/local/openssl-1.1.1k \ -DOPENSSL_USE_STATIC_LIBSFALSE .. # 走 pkg-config 的项目让 pkg-config 优先看新版目录 export PKG_CONFIG_PATH/usr/local/openssl-1.1.1k/lib/pkgconfig:$PKG_CONFIG_PATH pkg-config --modversion openssl pkg-config --cflags --libs opensslPython那个坑要单独提醒解释器是编译期静态链死 OpenSSL 的装一套新的.so对它毫无影响。要让ssl模块用上 1.1.1k只能重新编译 Python或者装一个本来就链这套库的解释器。很多人是在pip install报OpenSSL version mismatch时才发现这一点然后反复装openssl-devel却毫无进展。CMake那边要注意OPENSSL_ROOT_DIR是根目录不是 lib 目录指向lib会让它找不到头文件。找不到时的排查顺序是先看CMakeCache.txt里OPENSSL_CRYPTO_LIBRARY、OPENSSL_INCLUDE_DIR的实际取值再看有没有被系统路径抢先命中。还有一类是自带 OpenSSL 源码的构建系统比如某些 Web 服务器和数据库的--with-openssl参数它要的是源码目录而不是安装目录。两者写混了的表现是编译能过但版本没变因为对方从源码目录自己编了一份你之前make install的那套根本没参与。4.3 装完必须跑的三条验证命令第一条确认版本和路径/usr/local/openssl-1.1.1k/bin/openssl version -a输出里重点看四个字段OpenSSL 1.1.1k、built on编译日期、OPENSSLDIR配置目录、compiler。如果OPENSSLDIR指向/usr/lib/ssl而不是你给的--openssldir说明这个二进制不是你以为的那个。第二条确认动态库真的来自新目录ldd /usr/local/openssl-1.1.1k/bin/openssl | grep -E libssl|libcrypto # 期望看到 /usr/local/openssl-1.1.1k/lib/libssl.so.1.1如果这里出现/lib/x86_64-linux-gnu/libssl.so.1.1说明 rpath 没生效或缓存顺序被抢占回到 4.1 处理。第三条做一次真实握手确认协议栈能用# 用变量避免命令行重复-brief 只打印握手结论 TARGET_PORT443 TARGET_HOSTyour-service.internal /usr/local/openssl-1.1.1k/bin/openssl s_client \ -connect ${TARGET_HOST}:${TARGET_PORT} \ -servername ${TARGET_HOST} \ -brief /dev/null-brief会输出CONNECTION ESTABLISHED、Protocol version、CIPHER is三行核心信息。如果卡在CONNECTED之后不动通常是对方不支持 TLS 1.3 且默认协商失败加-tls1_2再试一次就能区分是协议问题还是网络问题。4.4 性能基线speed 与 s_time 怎么读换库之后如果怀疑性能有变化用自带的两条命令做基线别凭感觉。# 对称加密与摘要算法吞吐-evp 走 EVP 接口更接近真实调用路径 /usr/local/openssl-1.1.1k/bin/openssl speed -evp aes-256-gcm -seconds 3 # TLS 握手速率-new 表示每个连接都完整握手不复用会话 /usr/local/openssl-1.1.1k/bin/openssl s_time \ -connect ${TARGET_HOST}:${TARGET_PORT} -new -time 10speed的读数单位是kB/s或k ops/s比较两次结果时要保证-seconds一致、机器负载接近否则波动会盖过差异。s_time输出的是connections/user sec这个数字受网络 RTT 影响极大跨机房比较没有意义只适合在同一个客户端上对比换库前后的变化。5. 避坑与排查五个真实场景的现象、原因与解决这一章的每一条都来自“明明编过了却跑不起来”的现场。写法统一按现象、原因、解决三段走照着比对能省掉大量猜测时间。5.1 装完 openssl version 还是旧版本现象make install_sw输出全绿敲openssl version仍然显示系统自带的小版本号which openssl指向/usr/bin/openssl。原因PATH里/usr/local/.../bin排在/usr/bin之后或者根本没加。shell 有命令路径哈希缓存即使改了PATH当前会话可能仍走旧缓存。解决先hash -r清缓存再检查echo $PATH。想让改动长期生效在/etc/profile.d/下放一个脚本导出PATH同时确认没有别的脚本在后面又把它覆盖回去。验证方式是type -a openssl它会按优先级列出所有同名命令。5.2 libssl.so.1.1: cannot open shared object file现象编译期一切正常运行时立刻退出报错指向libssl.so.1.1找不到。用ldd看二进制那一行显示not found。原因编译时没带 rpath运行时也没有LD_LIBRARY_PATH动态链接器只在默认目录和ld.so.cache里找。被systemd拉起的服务不继承登录 shell 的环境变量所以“我手动跑是好的”这个观察会误导排查方向。解决优先加/etc/ld.so.conf.d/配置文件后ldconfig如果程序是自己编的重编时加-Wl,-rpath,lib目录更彻底。改完必须用ldd复核只看ldconfig -p的输出不够因为缓存里有不代表这个二进制会选中它。5.3 make test 挂一两个用例现象make test汇总显示FAILED失败用例集中在一两个比如 TLS 相关的 recipe重跑有时过有时不过。原因常见有三类。一是并发跑测试时端口冲突HARNESS_JOBS设太大二是容器或精简系统里熵池不足/dev/random阻塞导致测试超时三是文件系统不支持某些操作比如某些网络挂载上的O_TMPFILE。解决先用HARNESS_JOBS1 make test串行重跑过了就是并发问题把并发降到 2 或 4。还挂的话单独跑失败的那个 recipe看具体断言。熵不足的表现是测试卡住不动而不是报错这时候看cat /proc/sys/kernel/random/entropy_avail的数值太低就换台机器编别在构建阶段折腾熵池。5.4 第三方库编译时报未定义符号现象链接阶段报undefined reference to SSL_CTX_set_ciphersuites或类似的新 API 符号。原因头文件来自新装的 1.1.1k链接时却命中了系统自带的旧libssl。构建系统的搜索顺序里/usr/lib排在自编译目录前面。解决不要试图去改系统库。用构建系统自己的开关把路径钉死CMake 用OPENSSL_ROOT_DIRautotools 用--with-opensslpkg-config 用PKG_CONFIG_PATH。改完删掉构建缓存重新configure很多构建系统会把第一次探测到的路径缓存下来不清理缓存改了参数也不生效。5.5 系统包管理器把自编译版本覆盖掉现象跑了几个月没问题某次系统更新后服务集体起不来openssl version又变回旧版。原因包管理器升级时重写了/usr/lib下的同名库或者更新了ld.so.conf里的顺序。自编译版本装在/usr/local下本不该受影响但如果当初为了省事把库拷进了/usr/lib就会正面冲突。解决自编译产物永远装在自己的--prefix下一个文件都不要往系统目录拷。已经拷过的用包管理器重装对应包恢复原状再按 4.1 的方式二或方式三接管路径。做完之后把升级流程里加一步ldd复核防止下次更新再踩。6. 进阶用 openssl.cnf 与 s_client 把 1.1.1k 的安全边界确定下来装好只是起点。1.1.1k 这套库默认允许什么协议、什么套件、什么安全等级是可以在不动应用代码的前提下用配置文件调的这一点比重新编译划算得多。配置入口是--openssldir下的openssl.cnf。给所有走这套库的程序加一段系统级默认值# $OPENSSLDIR/openssl.cnf 片段 openssl_conf default_conf [default_conf] ssl_conf ssl_sect [ssl_sect] system_default system_default_sect [system_default_sect] MinProtocol TLSv1.2 CipherString ECDHEAESGCM:ECDHECHACHA20:DHEAESGCM:!aNULL:!MD5MinProtocol一设任何忘了自己调SSL_CTX_set_min_proto_version的程序都会被强制抬到 TLS 1.2这是防止老代码留 SSLv3 口子最省力的办法。CipherString的冒号是优先级顺序!前缀是永久排除SECLEVEL2这类后缀可以放在末尾统一给。调完必须验证不然很容易把本来能连的对端也挡了# 逐个协议版本探测能连上的版本会打印协议与套件 for v in -tls1 -tls1_1 -tls1_2 -tls1_3; do printf %s: $v /usr/local/openssl-1.1.1k/bin/openssl s_client \ -connect ${TARGET_HOST}:${TARGET_PORT} \ -servername ${TARGET_HOST} $v -brief /dev/null 21 | grep -m1 -E Protocol version|error done这段跑完哪个版本被拒、哪个被接受一目了然比翻文档猜配置有没有生效可靠得多。还有个容易被忽略的边界1.1.1k 是 2021 年 3 月的补丁版之后 1.1.1 分支又陆续出了若干补丁版。如果你的合规要求是“已知问题必须有修复”那 1.1.1k 只是当时的安全线不是终点线。我的做法是把版本号写进--prefix像第 2 章那样升级时新建一个目录并排安装改环境变量或软链接切过去出问题一条命令回滚。这套流程一开始觉得麻烦后来发现它救过我两次——一次是新版本导致某个老客户端握手失败切回去只花了十秒。从那以后我每次编完这类基础库都会强制走一遍version -a、ldd、s_client -brief这三步再存一份configdata.pm --dump的输出了事。看起来啰嗦但它把“我以为装好了”变成了“我确认过它是好的”。希望帮到你。本文还有配套的精品资源点击获取