Windows编译OpenSSL

准备工作

OpenSSL 源码(使用的 1.1.1k 版本)下载地址:链接
Perl for MS Windows 下载地址:链接
VS2019,自己找吧
nasm 下载地址:链接

安装编译环境

当前使用的Windows 10 x64版本,所以编译环境也都选择了x64的版本。32位的版本没做测试。

安装VS2019

步骤略。

安装 Perl

下载并运行安装包后,一路下一步,安装完成后,打开cmd,输入perl -V,确认是否安装成功。安装成功会输出下列信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Microsoft Windows [版本 10.0.19042.928]
(c) Microsoft Corporation。保留所有权利。

C:\Users\Admin>perl -V
Summary of my perl5 (revision 5 version 32 subversion 1) configuration:

Platform:
osname=MSWin32
osvers=10.0.19042.746
archname=MSWin32-x64-multi-thread
// 省略
Built under MSWin32
Compiled at Jan 24 2021 15:05:42
@INC:
C:/Strawberry/perl/site/lib
C:/Strawberry/perl/vendor/lib
C:/Strawberry/perl/lib

如果没有成功,检查系统环境变量path是否存在下面的路径:

1
2
3
C:\Strawberry\c\bin;
C:\Strawberry\perl\site\bin;
C:\Strawberry\perl\bin;

根据真实环境补全 perl 的所在目录。

安装 nasm

下载并运行安装包后,一路下一步,安装完成后,打开cmd,输入nasm -v,确认是否安装成功。安装成功会输出下列信息:

1
2
3
4
5
Microsoft Windows [版本 10.0.19042.928]
(c) Microsoft Corporation。保留所有权利。

C:\Users\Admin>nasm -v
NASM version 2.15.05 compiled on Aug 28 2020

如果没有成功,检查系统环境变量path是否存在下面的路径:

1
C:\Program Files\NASM

根据真实环境补全 nasm 的所在目录。

编译步骤

这里以编译x64版本的OpenSSL为例。

步骤1 启动编译环境

在开始菜单中找到Visual Studio 2019目录中的x86_x64 Cross Tools Command Prompt for VS 2019以管理员身份运行
cd进入OpenSSL源码的根目录:

1
2
3
4
5
6
7
8
9
**********************************************************************
** Visual Studio 2019 Developer Command Prompt v16.9.4
** Copyright (c) 2021 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x86_x64'

C:\Windows\System32>cd C:\Users\Admin\source\openssl-OpenSSL_1_1_1k

C:\Users\Admin\source\openssl-OpenSSL_1_1_1k>

步骤2 生成编译配置文件

运行命令perl Configure VC-WIN64A,生成编译配置文件,成功后输出信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
C:\Users\Admin\source\openssl-OpenSSL_1_1_1k>perl Configure VC-WIN64A
Configuring OpenSSL version 1.1.1k (0x101010bfL) for VC-WIN64A
Using os-specific seed configuration
Creating configdata.pm
Creating makefile

**********************************************************************
*** ***
*** OpenSSL has been successfully configured ***
*** ***
*** If you encounter a problem while building, please open an ***
*** issue on GitHub <https://github.com/openssl/openssl/issues> ***
*** and include the output from the following command: ***
*** ***
*** perl configdata.pm --dump ***
*** ***
*** (If you are new to OpenSSL, you might want to consult the ***
*** 'Troubleshooting' section in the INSTALL file first) ***
*** ***
**********************************************************************

C:\Users\Admin\source\openssl-OpenSSL_1_1_1k>

步骤3 开始编译

运行命令nmake,开始编译,编译时间大约10分钟,成功后输出信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        cl  /Zi /Fdapp.pdb /Gs0 /GF /Gy /MD /W3 /wd4090 /nologo /O2 /I "include" -D"OPENSSL_SYS_WIN32" -D"WIN32_LEAN_AND_MEAN" -D"UNICODE" -D"_UNICODE" -D"_CRT_SECURE_NO_DEPRECATE" -D"_WINSOCK_DEPRECATED_NO_WARNINGS" -D"OPENSSL_USE_APPLINK" -D"NDEBUG"  /Zs /showIncludes "test\x509_time_test.c" 2>&1 > test\x509_time_test.d
IF EXIST test\x509_time_test.exe.manifest DEL /F /Q test\x509_time_test.exe.manifest
link /nologo /debug /subsystem:console /opt:ref /nologo /debug /out:test\x509_time_test.exe @C:\Users\Admin\AppData\Local\Temp\nmC906.tmp
IF EXIST test\x509_time_test.exe.manifest mt -nologo -manifest test\x509_time_test.exe.manifest -outputresource:test\x509_time_test.exe
cl /Zi /Fdapp.pdb /Gs0 /GF /Gy /MD /W3 /wd4090 /nologo /O2 /I "include" -D"OPENSSL_SYS_WIN32" -D"WIN32_LEAN_AND_MEAN" -D"UNICODE" -D"_UNICODE" -D"_CRT_SECURE_NO_DEPRECATE" -D"_WINSOCK_DEPRECATED_NO_WARNINGS" -D"OPENSSL_USE_APPLINK" -D"NDEBUG" -c /Fotest\x509aux.obj "test\x509aux.c"
x509aux.c
cl /Zi /Fdapp.pdb /Gs0 /GF /Gy /MD /W3 /wd4090 /nologo /O2 /I "include" -D"OPENSSL_SYS_WIN32" -D"WIN32_LEAN_AND_MEAN" -D"UNICODE" -D"_UNICODE" -D"_CRT_SECURE_NO_DEPRECATE" -D"_WINSOCK_DEPRECATED_NO_WARNINGS" -D"OPENSSL_USE_APPLINK" -D"NDEBUG" /Zs /showIncludes "test\x509aux.c" 2>&1 > test\x509aux.d
IF EXIST test\x509aux.exe.manifest DEL /F /Q test\x509aux.exe.manifest
link /nologo /debug /subsystem:console /opt:ref /nologo /debug /out:test\x509aux.exe @C:\Users\Admin\AppData\Local\Temp\nmCACC.tmp
IF EXIST test\x509aux.exe.manifest mt -nologo -manifest test\x509aux.exe.manifest -outputresource:test\x509aux.exe
"C:\Strawberry\perl\bin\perl.exe" "-I." -Mconfigdata "util\dofile.pl" "-omakefile" "apps\CA.pl.in" > "apps\CA.pl"
"C:\Strawberry\perl\bin\perl.exe" "-I." -Mconfigdata "util\dofile.pl" "-omakefile" "apps\tsget.in" > "apps\tsget.pl"
"C:\Strawberry\perl\bin\perl.exe" "-I." -Mconfigdata "util\dofile.pl" "-omakefile" "tools\c_rehash.in" > "tools\c_rehash.pl"

C:\Users\Admin\source\openssl-OpenSSL_1_1_1k>

步骤4 测试编译结果

运行命令nmake test,测试编译结果,耗时大约10分钟,成功后输出信息如下:

1
2
3
4
5
6
7
test\recipes\99-test_ecstress.t ................. ok
test\recipes\99-test_fuzz.t ..................... ok
All tests successful.
Files=158, Tests=2545, 451 wallclock secs ( 1.86 usr + 0.50 sys = 2.36 CPU)
Result: PASS

C:\Users\Admin\source\openssl-OpenSSL_1_1_1k>

步骤5 安装

运行命令nmake install,安装文件,x64版本默认安装到C:\Program Files\OpenSSLx86版本默认安装到C:\Program Files (x86)\OpenSSL,耗时大约3分钟,成功后输出信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
C:\Program Files\OpenSSL\html\man7\bio.html
C:\Program Files\OpenSSL\html\man7\crypto.html
C:\Program Files\OpenSSL\html\man7\ct.html
C:\Program Files\OpenSSL\html\man7\des_modes.html
C:\Program Files\OpenSSL\html\man7\Ed25519.html
C:\Program Files\OpenSSL\html\man7\Ed448.html -> C:\Program Files\OpenSSL\html\man7\Ed25519.html
C:\Program Files\OpenSSL\html\man7\evp.html
C:\Program Files\OpenSSL\html\man7\ossl_store-file.html
C:\Program Files\OpenSSL\html\man7\ossl_store.html
C:\Program Files\OpenSSL\html\man7\passphrase-encoding.html
C:\Program Files\OpenSSL\html\man7\proxy-certificates.html
C:\Program Files\OpenSSL\html\man7\RAND.html
C:\Program Files\OpenSSL\html\man7\RAND_DRBG.html
C:\Program Files\OpenSSL\html\man7\RSA-PSS.html
C:\Program Files\OpenSSL\html\man7\scrypt.html
C:\Program Files\OpenSSL\html\man7\SM2.html
C:\Program Files\OpenSSL\html\man7\ssl.html
C:\Program Files\OpenSSL\html\man7\X25519.html
C:\Program Files\OpenSSL\html\man7\X448.html -> C:\Program Files\OpenSSL\html\man7\X25519.html
C:\Program Files\OpenSSL\html\man7\x509.html

C:\Users\Admin\source\openssl-OpenSSL_1_1_1k>

编译多个版本

如果还需要编译其它版本,例如要编译x86版本,需要以管理员身份运行 x86 版本的VS2019的命令行工具x86 Native Tools Command Prompt for VS 2019,进入源码目录后,先执行清理命令,然后修改配置信息:

1
2
nmake clean
perl Configure VC-WIN32

接下来从步骤3 开始编译再来一遍。

使用 OpenSSL

测试代码

以计算MD5为例。
将安装后的C:\Program Files\OpenSSL\includeC:\Program Files\OpenSSL\lib添加到工程中:
233e11b4fe98.png

代码中要根据实际情况使用头文件.h、静态库.lib、动态库.dll,这里只用到了#include <openssl\md5.h>#pragma comment(lib, "libcrypto.lib")libcrypto-1_1-x64.dll
测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <openssl\md5.h>

#pragma comment(lib, "libcrypto.lib")

int main()
{
unsigned char szBuf[6] = {'1', '2', '3', '4', '5', '6'};
unsigned char md[MD5_DIGEST_LENGTH] = {0};
MD5(szBuf, sizeof(szBuf), md);

for (int i = 0; i < MD5_DIGEST_LENGTH; i++)
{
printf_s("%02x", md[i]);
}
printf_s("\n");
}

输出结果e10adc3949ba59abbe56e057f20f883e

可能遇到的问题

  • 找不到dll

44a882998935.png

检查对应版本的dll是否放到程序目录或者系统目录。

  • 编译报错找不到标识符或者未声明的标识符

报错详情:

1
2
3
4
5
6
7
8
已启动生成…
1>------ 已启动生成: 项目: test_openssl, 配置: Debug x64 ------
1>test_openssl.cpp
1>C:\Users\Admin\source\test_openssl\test_openssl.cpp(6,22): error C2065: “MD5_DIGEST_LENGTH”: 未声明的标识符
1>C:\Users\Admin\source\test_openssl\test_openssl.cpp(7,5): error C3861: “MD5”: 找不到标识符
1>C:\Users\Admin\source\test_openssl\test_openssl.cpp(9,25): error C2065: “MD5_DIGEST_LENGTH”: 未声明的标识符
1>已完成生成项目“test_openssl.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

检查代码是否引入头文件#include <openssl\md5.h>

  • 无法解析的外部符号xxx

报错详情:

1
2
3
4
5
6
7
已启动生成…
1>------ 已启动生成: 项目: test_openssl, 配置: Debug x64 ------
1>test_openssl.cpp
1>test_openssl.obj : error LNK2019: 无法解析的外部符号 MD5,函数 main 中引用了该符号
1>C:\Users\Admin\source\test_openssl\x64\Debug\test_openssl.exe : fatal error LNK1120: 1 个无法解析的外部命令
1>已完成生成项目“test_openssl.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

检查代码是否有#pragma comment(lib, "libcrypto.lib")

  • 无法打开包括文件xxx.h
    检查工程属性页的配置属性 -- VC++ 目录 -- 包含目录是否添加C:\Program Files\OpenSSL\include

  • 无法打开文件xxx.lib
    检查工程属性页的配置属性 -- VC++ 目录 -- 库目录是否添加C:\Program Files\OpenSSL\lib

  • 无法解析的外部符号_函数名@数字

1
2
3
4
5
6
7
已启动生成…
1>------ 已启动生成: 项目: test_openssl, 配置: Debug Win32 ------
1>test_openssl.cpp
1>test_openssl.obj : error LNK2019: 无法解析的外部符号 _MD5@12,函数 _main 中引用了该符号
1>C:\Users\Admin\source\test_openssl\Debug\test_openssl.exe : fatal error LNK1120: 1 个无法解析的外部命令
1>已完成生成项目“test_openssl.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

这个问题只出现在32位环境,和上面的无法解析的外部符号xxx报错信息很像,但是函数名前后多了_@数字字样,原因是代码工程使用了标准调用约定,
使用__stdcall,函数在obj 中显示为_funname@参数总字节数,用于函数本身平衡栈空间。
检查工程属性页的配置属性 -- C/C++ -- 高级 -- 调用约定,是否为C调用约定_cdecl,OpenSSL 默认使用C调用约定:
895ab22345bb.png

参考资料

  1. OpenSSL的 README.md
  2. OpenSSL源码目录中的INSTALL文件