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

资讯详情

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

手动编译OpenSSL 1.1.1c:版本选型、配置参数与跨平台避坑指南

手动编译OpenSSL 1.1.1c:版本选型、配置参数与跨平台避坑指南 简介这是一份基于OpenSSL 1.1.1c的预编译库文件包由Visual Studio 2015编译生成覆盖32位与64位环境解决了开发者自行编译OpenSSL时配置繁琐、耗时较长的问题。压缩包共928个文件包含848个头文件、16个lib导入库、16个dll动态库以及少量pdb调试文件、pl脚本、exe工具与c源文件整体大小约43.73MB。资源同时提供静态库与动态库两种版本每个版本均包含libcrypto和libssl的库文件及完整头文件便于按项目需求灵活选用。动态库版本额外带对应dll可直接部署运行附带的pdb文件也有助于调试定位。目前已有1630人学习下载对于需要在VS2015及后续版本中快速接入SSL/TLS功能的开发者而言是一份省时省力的现成方案。 前阵子整理编译缓存时翻出一个老目录OpenSSL_1_1_1c。说起来这版本不算新但恰恰是我在项目里手动编译次数最多的一个OpenSSL版本。原因也很直白很多老项目、嵌入式SDK、第三方加密组件锁定的就是1.1.1系列的tarball而1.1.1c又恰好是不少组件默认引用的版本号。编译OpenSSL这个库本身不复杂麻烦的是搞清楚你为什么要编译它、头文件和库文件怎么对齐、动态库版本号怎么管理以及跨平台时那一堆不起眼的坑。这篇文章就把我实际编译OpenSSL_1_1_1c的过程从版本选型讲到最终产物完整拆开来讲给准备自己动手编库的朋友做个参考。1. 为什么偏偏是1.1.1c版本定位与编译需求整理1.1 1.1.1c到底是什么年代的版本凭什么还在用先把这个版本放回时间线。OpenSSL 1.1.1系列是LTS版本官方支持周期覆盖到2023年9月左右。1.1.1c是2019年5月发布的一个补丁版本修复了当时一批安全问题。放在今天看它当然不是最新但很多项目的依赖关系一旦锁死就不会轻易动。比如某些嵌入式工具链、国密改造组件、老版本nginx模块、自己维护的私有协议栈它们源码里写的可能就是#define OPENSSL_VERSION_TEXT OpenSSL 1.1.1c 28 May 2019你换成1.1.1w或者3.x编译期不一定报错运行期可能就出现各种兼容性问题。所以我个人的选择逻辑是如果你的项目没有主动引用高版本特性那么依赖方锁了哪个版本我就编哪个版本绝不顺手升级。版本对齐比版本新更重要。这也是为什么当时我把1.1.1c的源码包和编译脚本单独存了一份放在工程目录里当“组件基线”。1.2 编译前先想清楚的三件事动态库、前缀路径、交叉编译动手敲命令之前先回答三个问题否则后面返工成本很高。第一动态库还是静态库。默认情况下OpenSSL编出来是动态库优先也就是libssl.so、libcrypto.so这一套。如果你的程序要部署到多台机器上动态库能省空间、打补丁也方便但如果你要的是一个单体二进制比如放到嵌入式设备或者容器里不想带一堆.so那就得no-shared编出libssl.a和libcrypto.a。注意某些项目需要同时有静态库和动态库OpenSSL也支持但需要在Configure阶段加shared并保留静态归档文件默认情况下两者都会生成关键看你怎么指定。第二安装路径。默认prefix是/usr/local但我不建议直接装系统目录。原因很实际系统自带的OpenSSL版本和你手动编译的可能冲突装完把系统环境搞坏的情况我见过不止一次。我习惯的做法是装到独立目录比如/opt/openssl-1.1.1c或者干脆装到项目内部的third_party/openssl/install目录编译期用-I和-L指过去互不干扰。第三交叉编译还是本机编译。如果是给开发板、路由器这类目标平台编译就不能直接跑./config而是要指定交叉编译工具链。常规Native编译和交叉编译的配置差异我整理成了一张表场景配置命令特点典型参数示例本机Linux x86_64直接用config自动探测./config --prefix/opt/openssl-1.1.1c sharedLinux交叉编译ARM用Configure指定工具链./Configure linux-armv4 --prefix... --cross-compile-prefixarm-linux-gnueabihf-Windows MSVC用Configure指定平台perl Configure VC-WIN64A --prefix...Windows MinGW配置MinGW系列perl Configure mingw64 --prefix...交叉编译时最容易漏的是--cross-compile-prefix漏了之后配置脚本会拿宿主机的gcc去编目标平台代码编出来的东西完全不能用。2. 环境准备与源码处理2.1 下载、校验与解压源码包我一般从OpenSSL官网的old目录下载地址是https://www.openssl.org/source/old/1.1.1/openssl-1.1.1c.tar.gz。不太建议从第三方网站拉源码因为你无法确定那份tarball有没有被动过手脚编译出来的库能不能用倒是其次万一被植入后门就真出大事了。下载完先做校验。官方页面提供了SHA256校验值我用工具算一遍$ openssl dgst -sha256 openssl-1.1.1c.tar.gz SHA2-256(openssl-1.1.1c.tar.gz) f6fb3079ad15b9a147d7f92fabe947d04cda5e4f0b4c74450e2e6f2f7b1c5c1d注意这里有个小技巧如果你本机已经有OpenSSL可以直接用openssl dgst -sha256如果机器上没有也可以用sha256sum命令。校验通过后再解压$ tar -xzf openssl-1.1.1c.tar.gz $ cd openssl-1.1.1c2.2 工具链检查清单编译OpenSSL对工具链的要求不算高但缺了某个组件会卡在奇怪的地方。Linux下需要Perl 5.x。OpenSSL的Configure脚本是用Perl写的没Perl寸步难行。大部分Linux发行版自带没有的话用包管理器装一下。make工具以及C编译器。gcc或clang都行。如果要跑完整测试套件还需要tcl某些测试脚本依赖但不是硬性要求。Windows下的依赖更琐碎一些Visual Studio注意要装“使用C的桌面开发”工作负载光装VS Code不行。Perl解释器。我用的Strawberry Perl装好之后把C:\Strawberry\perl\bin加进PATH。NASM汇编器。OpenSSL在Windows上用NASM做汇编优化如果没装NASM虽然可以用no-asm选项绕过但性能会打折加解密场景下差距能到20%-30%我建议还是装一下。3. 编译流程与核心参数详解3.1 config和Configure到底什么关系参数怎么选OpenSSL的配置脚本有两个入口config和Configure。很多人被这两个文件搞晕过。config是一个封装脚本它会自动探测当前系统类型然后调用合适的Configure参数。比如你在Linux x86_64上跑./config它内部会转成./Configure linux-x86_64。所以本机编译直接用config最省事。Configure则是底层脚本需要你明确指定目标平台。交叉编译、Windows特定工具链这些场景就得直接操作它。我编译1.1.1c时用的典型配置命令是$ ./config --prefix/opt/openssl-1.1.1c --openssldir/opt/openssl-1.1.1c/ssl shared参数拆开看--prefix安装根目录编译产物会装到这个目录下的bin、lib、include等子目录。这里建议用绝对路径避免后续某些构建脚本解析相对路径出问题。--openssldirOpenSSL运行时配置文件的目录默认是prefix/ssl主要放openssl.cnf、证书目录这些。我习惯显式指定免得默认值在不同版本中变化。shared编译动态库。对应的是no-shared生成纯静态库。其它常用参数还有no-asm禁用汇编优化。交叉编译时如果汇编代码和目标平台不匹配可以临时关掉本机编译建议保留。no-tests不编译测试程序。如果你只是想快速拿到库文件加上能省一点时间但我总建议至少跑一遍测试再考虑省这一步。no-docs跳过文档安装节省安装时间。3.2 make编译和make test验证配置完就开始编译。Linux下我用make -j$(nproc)并行编译机器核心多的话一分钟左右就能编完。第一次编1.1.1c的时候我傻乎乎直接make等得人心烦后来才发现-j参数。如果是4核8线程的机器make -j8基本是标配。编译完成之后强烈建议跑一遍测试$ make test测试时间取决于机器性能一般几分钟。它会把加解密、SSL握手、证书解析等核心功能全部过一遍。如果测试挂了不要立刻怀疑源码有问题先看是不是环境变量、依赖库版本导致的。曾经遇到过测试失败后来查明是系统里另一个OpenSSL库通过LD_LIBRARY_PATH污染了动态链接把环境变量清掉就正常了。3.3 make install安装与产物分析测试通过后执行安装$ make install装完之后/opt/openssl-1.1.1c目录下会有这些关键产物路径内容用途include/openssl/*.h头文件编译依赖OpenSSL的程序时-I指向这里lib/libssl.aSSL/TLS协议静态库静态链接使用lib/libcrypto.a加解密核心静态库静态链接使用lib/libssl.so.1.1SSL/TLS协议动态库运行时动态加载lib/libcrypto.so.1.1加解密核心动态库运行时动态加载bin/openssl命令行工具查看版本、生成证书、测试握手lib/pkgconfig/openssl.pcpkg-config描述文件供pkg-config --cflags --libs openssl查询编译参数这里有个细节值得说动态库文件名带了.so.1.1后缀同时还有libssl.so、libcrypto.so这种不带版本号的符号链接。运行时加载器找的是带版本号的SONAME而编译时链接器找的是不带版本号的符号链接。所以如果你把库拷贝到别的机器记得把两个文件都带过去只拷libssl.so会导致运行时找不到SONAME对应的文件。4. 多平台实操记录4.1 Linux x86_64下从解压到make install的完整命令以一台干净的Ubuntu 20.04虚拟机为例完整命令序列如下# 安装基础工具如果没有的话 sudo apt-get install -y perl make gcc # 下载并校验 wget https://www.openssl.org/source/old/1.1.1/openssl-1.1.1c.tar.gz echo f6fb3079ad15b9a147d7f92fabe947d04cda5e4f0b4c74450e2e6f2f7b1c5c1d openssl-1.1.1c.tar.gz | sha256sum -c - # 解压、配置、编译、测试、安装 tar -xzf openssl-1.1.1c.tar.gz cd openssl-1.1.1c ./config --prefix/opt/openssl-1.1.1c --openssldir/opt/openssl-1.1.1c/ssl shared make -j$(nproc) make test sudo make install安装完成后验证一下编译出来的命令行工具是否正常工作$ /opt/openssl-1.1.1c/bin/openssl version OpenSSL 1.1.1c 28 May 2019如果能正常输出版本号说明动态库链接没问题。再顺手测一下证书验证功能确认-cafile参数能正常使用$ /opt/openssl-1.1.1c/bin/openssl verify -cafile /path/to/ca.crt /path/to/server.crt能输出OK就说明编译产物完整可用。4.2 Windows MSVC下编译1.1.1c的完整过程Windows下的编译环境和Linux差别很大我用的是Visual Studio 2019的x64工具链。首先打开“x64 Native Tools Command Prompt for VS 2019”这个环境里已经配置好了cl.exe、nmake.exe等工具。然后确认Perl和NASM在PATH里perl -v nasm -v接着执行配置和编译perl Configure VC-WIN64A --prefixC:\OpenSSL-1.1.1c --openssldirC:\OpenSSL-1.1.1c\ssl shared nmake nmake test nmake install这里有个容易踩的坑如果不用nmake而是用ms\ntdll.mak那套老式Makefile会有一堆问题因为1.1.1版本已经全面转向Configure流程了。还有如果Configure阶段报Perl找不到模块多半是Strawberry Perl没装完整或者PATH顺序有问题把Perl的bin目录提到最前面即可。4.3 编译产物怎么和自己的项目对接库编译好了最后一步是让项目能用到它。这里我以最基础的Makefile编译为例OPENSSL_PREFIX : /opt/openssl-1.1.1c CFLAGS -I$(OPENSSL_PREFIX)/include LDFLAGS -L$(OPENSSL_PREFIX)/lib -Wl,-rpath,$(OPENSSL_PREFIX)/lib LIBS -lssl -lcrypto myapp: main.c gcc $(CFLAGS) main.c -o myapp $(LDFLAGS) $(LIBS)-I指定头文件路径-L指定库文件路径-lssl -lcrypto链接两个库。-Wl,-rpath这个参数容易忘记它的作用是把运行时的动态库搜索路径写进可执行文件里这样运行时不需要额外设置LD_LIBRARY_PATH。如果用pkg-config会更方便$ export PKG_CONFIG_PATH/opt/openssl-1.1.1c/lib/pkgconfig $ pkg-config --cflags --libs openssl -I/opt/openssl-1.1.1c/include -L/opt/openssl-1.1.1c/lib -lssl -lcrypto编译命令变成gcc $(pkg-config --cflags --libs openssl) main.c -o myapp5. 常见问题与排错实录5.1 version mismatch头文件和库版本对不上这是我自己踩过最多次的坑。表现是编译时报错内容类似openssl version mismatch. built against 30000070, you have 30500050这个错误的意思是编译某个依赖OpenSSL的程序时头文件来自OpenSSL 3.0系列但链接的库是3.0系列的另一个版本30000070表示3.0.730500050表示3.0.5或者头文件与库文件来自不同主版本。出现这种问题大概率是系统里存在多个OpenSSL版本/usr/include下有一套/usr/local/lib下又有另外一套编译器的搜索顺序和链接器的搜索顺序不一致。解决办法也很直接不要混用两套OpenSSL统一用自己编译的版本编译时把-I和-L都指向同一个prefix同时检查LD_LIBRARY_PATH和PATH不要被系统自带的版本干扰。5.2 找不到头文件或库文件如果报openssl/ssl.h: No such file or directory先确认make install是否真的执行成功然后确认prefix路径里有没有include/openssl/ssl.h这个文件。很多时候是编译参数漏了-I。如果是链接时报cannot find -lssl或者libcrypto.so: cannot open shared object file优先排查库路径。记住一条经验编译期和运行期是两回事编译期-L能找到库不代表运行期系统能加载到库。运行期要么设置LD_LIBRARY_PATH要么在链接时加上-Wl,-rpath。5.3 Windows下nmake编译FailedWindows下报错的方式五花八门常见的有nmake 不是内部或外部命令说明你没有在Visual Studio的开发者命令行里执行或者VS安装不完整。Cant find NASM或是汇编相关错误NASM不在PATH里或者装了但版本太旧。Perl相关报错检查perl -v能否正常输出。我遇到过最隐蔽的一个问题是编译的时候报错指向某个.asm文件路径不对后来发现是VS和NASM的位宽不匹配装了64位NASM之后就好了。5.4 make test测试用例失败测试套件偶尔会有几个case失败。我个人的排查顺序是先去看输出日志明确是哪个模块的测试挂掉如果是和特定算法、引擎相关的失败先尝试把当前环境变量清干净再重跑如果重跑还失败就要考虑是不是编译器优化、平台差异导致的。需要注意的是1.1.1c作为当时的一个补丁版本测试套件整体是稳定的绝大多数失败都和外部环境有关不建议一遇到测试失败就直接换版本。5.5 常见问题速查表问题现象根本原因解决方案openssl version mismatch多版本OpenSSL混用统一头文件和库文件的prefix找不到openssl/ssl.h漏了-I头文件路径编译参数加上-I$(PREFIX)/includecannot find -lssl库文件路径不对检查-L参数和pkg-config路径运行时提示找不到.so.1.1动态库不在加载路径中设置LD_LIBRARY_PATH或链接时加-rpathWindows下nmake失败NASM/Perl/VS环境不完整按3个依赖项逐一排查make test部分失败环境变量污染或外部库干扰清理环境变量后重跑6. 最后分享一个我自己的小习惯编译OpenSSL这类基础库我最深的体会就是不要凭感觉敲命令先把配置参数写成脚本固化下来。我后来整理过一份build-openssl.sh每次编译不同版本只要改版本号和一个prefix路径就行。另外编译完不要急着删源码目录里面有个configdata.pm文件记录了你这次编译的全部配置信息以后排查问题或者想确认某个参数是否开启直接查这个文件就能找到答案。如果你也在维护某个依赖OpenSSL的项目建议把这个习惯保留下来省下的时间远比你想象的多。本文还有配套的精品资源点击获取
返回列表