本教程为openssl-1.1.1j.tar.gz为例子

第一步:下载OpenSSL源码


下载地址: https://www.openssl.org/source/
个人网盘: https://pan.pigeoooon.site/#/s/o8TJ kv6h007q.png

选择 openssl-x.x.x?.tar.gz 其中 x.x.x? 为目前最新版本号
解压到任意目录下,后续的目录均用 PATH 代替

第二步:下载安装ActivePerl


下载地址:https://www.activestate.com/activeperl/downloads/
个人网盘: https://pan.pigeoooon.site/#/s/m4sP
如果这个网址打不开的话,可以直接百度搜索ActivePerl下载。
运行安装程序,一直下一步操作安装完成。这里默认安装在C:\Perl64。

kv6h158k.png

打开 C:\Perl64\site\lib\ActivePerl\Config.pm 文件,将400行左右的代码注释掉,如下图。

kv6h27u6.png

第三步:编译64位版本


1、打开VS2019的 x86_x64 Cross Tools Command Prompt for VS 2019 软件

kv6h3i09.png

kv6h3szh.png

2、使用 cd /d PATH 到我们的解压目录

3、使用 perl Configure VC-WIN64A no-asm no-shared --prefix="C:\Program Files\openssl_bin" 进行生成编译脚本

4、如果编译不过,出现LNK2019等错误的时候,你可能还需要添加ws2_32.lib,crypt32.lib库。打开我们的 PATH 目录下 makefile 文件,将如下位置 EX_LIBS 后添加 Crypt32.lib Ws2_32.lib

kv6h5enb.png

5、使用 nmake 进行编译

6、使用 nmake test 进行测试,出现 Result: PASS 则为成功

7、使用 nmake install 将编译好的文件拷贝到我们设定的目录下 C:\Program Files\openssl_bin

8、使用 nmake clean 清除上次静态库的编译,以便重新编译

第四步:编译32位版本

1、打开VS2019的 x64_x86 Cross Tools Command Prompt for VS 2019 软件

kv6h6vs7.png

kv6h70i4.png

2、使用 cd /d PATH 到我们的解压目录

3、使用 perl Configure VC-WIN32 no-asm no-shared --prefix="C:\Program Files (x86)\openssl_bin" 进行生成编译脚本

4、如果编译不过,出现LNK2019等错误的时候,你可能还需要添加ws2_32.lib,crypt32.lib库。打开我们的 PATH 目录下 makefile 文件,将如下位置 EX_LIBS 后添加 Crypt32.lib Ws2_32.lib

5、使用 nmake 进行编译

6、使用 nmake test 进行测试,出现 Result: PASS 则为成功

7、使用 nmake install 将编译好的文件拷贝到我们设定的目录下 C:\Program Files (x86)\openssl_bin

8、使用 nmake clean 清除上次静态库的编译,以便重新编译

第五步:使用openssl库

1、给项目包含目录添加我们编译好的openssl库文件中的 include 目录

2、在使用openssl库函数时,添加如下头文件(按需添加)即可,一下使用RSA为例:

#include <openssl/rsa.h>
#include <openssl/engine.h>
#include <openssl/pem.h>
#include <openssl/err.h>

3、将我们编译好的静态文件 libcrypto.lib 和 libssl.lib 添加至我们的项目中

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

4、进行编译,若出现如下错误请添加 ws2_32.lib 依赖

1>libcrypto.lib(b_sock.obj) : error LNK2019: 无法解析的外部符号 __imp_ioctlsocket,函数 BIO_socket_ioctl 中引用了该符号
1>libcrypto.lib(b_sock.obj) : error LNK2019: 无法解析的外部符号 __imp_getsockname,函数 BIO_sock_info 中引用了该符号
1>libcrypto.lib(b_sock.obj) : error LNK2019: 无法解析的外部符号 __imp_getsockopt,函数 BIO_sock_error 中引用了该符号
1>libcrypto.lib(b_sock2.obj) : error LNK2001: 无法解析的外部符号 __imp_getsockopt
1>libcrypto.lib(b_sock.obj) : error LNK2019: 无法解析的外部符号 __imp_ntohs,函数 BIO_get_port 中引用了该符号
1>libcrypto.lib(b_addr.obj) : error LNK2001: 无法解析的外部符号 __imp_ntohs
1>libcrypto.lib(b_sock.obj) : error LNK2019: 无法解析的外部符号 __imp_gethostbyname,函数 BIO_gethostbyname 中引用了该符号
1>libcrypto.lib(b_sock.obj) : error LNK2019: 无法解析的外部符号 __imp_WSAStartup,函数 BIO_sock_init 中引用了该符号
1>libcrypto.lib(b_sock.obj) : error LNK2019: 无法解析的外部符号 __imp_WSACleanup,函数 bio_sock_cleanup_int 中引用了该符号
1>libcrypto.lib(b_sock.obj) : error LNK2019: 无法解析的外部符号 __imp_WSAGetLastError,函数 BIO_accept 中引用了该符号
1>libcrypto.lib(bss_sock.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAGetLastError
1>libcrypto.lib(b_sock2.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAGetLastError
1>libcrypto.lib(b_addr.obj) : error LNK2019: 无法解析的外部符号 __imp_getaddrinfo,函数 BIO_lookup 中引用了该符号
1>libcrypto.lib(b_addr.obj) : error LNK2019: 无法解析的外部符号 __imp_freeaddrinfo,函数 BIO_ADDRINFO_free 中引用了该符号
1>libcrypto.lib(b_addr.obj) : error LNK2019: 无法解析的外部符号 __imp_getnameinfo,函数 addr_strings 中引用了该符号
1>libcrypto.lib(bss_sock.obj) : error LNK2019: 无法解析的外部符号 __imp_recv,函数 sock_read 中引用了该符号
1>libcrypto.lib(bss_sock.obj) : error LNK2019: 无法解析的外部符号 __imp_send,函数 sock_write 中引用了该符号
1>libcrypto.lib(bss_sock.obj) : error LNK2019: 无法解析的外部符号 __imp_WSASetLastError,函数 sock_write 中引用了该符号
1>libcrypto.lib(b_sock2.obj) : error LNK2019: 无法解析的外部符号 __imp_accept,函数 BIO_accept_ex 中引用了该符号
1>libcrypto.lib(b_sock2.obj) : error LNK2019: 无法解析的外部符号 __imp_bind,函数 BIO_bind 中引用了该符号
1>libcrypto.lib(b_sock2.obj) : error LNK2019: 无法解析的外部符号 __imp_closesocket,函数 BIO_accept_ex 中引用了该符号
1>libcrypto.lib(b_sock2.obj) : error LNK2019: 无法解析的外部符号 __imp_connect,函数 BIO_connect 中引用了该符号
1>libcrypto.lib(b_sock2.obj) : error LNK2019: 无法解析的外部符号 __imp_listen,函数 BIO_listen 中引用了该符号
1>libcrypto.lib(b_sock2.obj) : error LNK2019: 无法解析的外部符号 __imp_setsockopt,函数 BIO_connect 中引用了该符号
1>libcrypto.lib(b_sock2.obj) : error LNK2019: 无法解析的外部符号 __imp_socket,函数 BIO_socket 中引用了该符号
#pragma comment(lib, "ws2_32.lib")

5、若出现如下错误请添加 Crypt32.lib 依赖

1>libcrypto.lib(e_capi.obj) : error LNK2019: 无法解析的外部符号 __imp_CertOpenStore,函数 capi_list_certs 中引用了该符号
1>libcrypto.lib(e_capi.obj) : error LNK2019: 无法解析的外部符号 __imp_CertCloseStore,函数 capi_find_key 中引用了该符号
1>libcrypto.lib(e_capi.obj) : error LNK2019: 无法解析的外部符号 __imp_CertEnumCertificatesInStore,函数 capi_find_cert 中引用了该符号
1>libcrypto.lib(e_capi.obj) : error LNK2019: 无法解析的外部符号 __imp_CertFindCertificateInStore,函数 capi_find_cert 中引用了该符号
1>libcrypto.lib(e_capi.obj) : error LNK2019: 无法解析的外部符号 __imp_CertDuplicateCertificateContext,函数 capi_load_ssl_client_cert 中引用了该符号
1>libcrypto.lib(e_capi.obj) : error LNK2019: 无法解析的外部符号 __imp_CertFreeCertificateContext,函数 capi_dsa_free 中引用了该符号
1>libcrypto.lib(e_capi.obj) : error LNK2019: 无法解析的外部符号 __imp_CertGetCertificateContextProperty,函数 capi_cert_get_fname 中引用了该符号

最后为RSA的完整示例代码

RSA头文件

#pragma once

#include <openssl/rsa.h>
#include <openssl/engine.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <string>

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "libssl.lib")
#pragma comment(lib, "libcrypto.lib")

/// <summary>
/// 生成私钥公钥
/// </summary>
/// <param name="nPrime">素数, 默认值65537是官方推荐的一个数字</param>
/// <param name="nKeySize">密钥的位数, 默认值2048, 可以是别的值, 但最好是4的倍数, 且至少为1024</param>
/// <param name="strSavePath">存储位置, 默认值当前路径</param>
void CreateRSAPrivateAndPublicKey(int nPrime = 65537, int nKeySize = 2048, std::string strSavePath = "");

/// <summary>
/// 私钥加密
/// </summary>
/// <param name="strPrivateKey">私钥</param>
/// <param name="strData">明文</param>
/// <returns>密文</returns>
std::string RSAPrivateEncrypt(const std::string& strPrivateKey, const std::string& strData);

/// <summary>
/// 私钥解密
/// </summary>
/// <param name="strPrivateKey">私钥</param>
/// <param name="strData">密文</param>
/// <returns>明文</returns>
std::string RSAPrivateDecrypt(const std::string& strPrivateKey, const std::string& strData);


/// <summary>
/// 公钥加密
/// </summary>
/// <param name="strPublicKey">公钥</param>
/// <param name="strData">明文</param>
/// <returns>密文</returns>
std::string RSAPublicEncrypt(const std::string& strPublicKey, const std::string& strData);

/// <summary>
/// 公钥解密
/// </summary>
/// <param name="strPublicKey">公钥</param>
/// <param name="strData">密文</param>
/// <returns>明文</returns>
std::string RSAPublicDecrypt(const std::string& strPublicKey, const std::string& strData);

RSA源文件

#include "RSA.h"
#include <assert.h>

// 生成新的公钥和私钥
void CreateRSAPrivateAndPublicKey(int nPrime, int nKeySize, std::string strSavePath)
{
 RSA* rsa = RSA_new();
 BIGNUM* e = BN_new();
 BN_set_word(e, nPrime);    // 65537是官方推荐的一个数字, 其实任意素数都是可以的
 RSA_generate_key_ex(rsa, nKeySize, e, 0);    // 2048表示密钥的位数, 可以是别的值, 但最好是4的倍数, 且至少为1024

 // 使用BIO打开密钥文件
 std::string privatePath = strSavePath + "privateKey.pem";
 BIO* _pri = BIO_new_file(privatePath.c_str(), "w");
 // 写入密钥
 PEM_write_bio_RSAPrivateKey(_pri, rsa, NULL, NULL, 0, NULL, NULL);
 // 使用BIO打开公钥文件
 std::string publicPath = strSavePath + "publicKey.pem";
 BIO* _pub = BIO_new_file(publicPath.c_str(), "w");
 // 写入公钥
 PEM_write_bio_RSAPublicKey(_pub, rsa);

 BIO_free(_pub);
 BIO_free(_pri);
 BN_free(e);
 RSA_free(rsa);
 CRYPTO_cleanup_all_ex_data();
}

// 私钥加密
std::string RSAPrivateEncrypt(const std::string& strPrivateKey, const std::string& strData)
{
 // 判断是否为空
 if (strPrivateKey.empty()  strData.empty())
 {
     assert(false);
     return "";
 }

 // 读取私钥
 RSA* pRSAPrivateKey = RSA_new();
 BIO* bio = BIO_new(BIO_s_mem());
 BIO_write(bio, strPrivateKey.c_str(), strPrivateKey.size());
 pRSAPrivateKey = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
 if (pRSAPrivateKey == nullptr)
 {
     assert(false);
     return "";
 }

 // 获取密钥长度
 int nLen = RSA_size(pRSAPrivateKey);

 // 返回结果
 std::string strRet;

 // 加密
 char* pEncode = new char[nLen + 1];
 int ret = RSA_private_encrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pEncode, pRSAPrivateKey, RSA_PKCS1_PADDING);
 if (ret >= 0)
 {
     strRet = std::string(pEncode, ret);
 }
 // 释放资源
 delete[] pEncode;
 RSA_free(pRSAPrivateKey);
 CRYPTO_cleanup_all_ex_data();
 return strRet;
}

// 私钥解密
std::string RSAPrivateDecrypt(const std::string& strPrivateKey, const std::string& strData)
{
 // 判断是否为空
 if (strPrivateKey.empty()  strData.empty())
 {
     assert(false);
     return "";
 }

 // 读取私钥
 RSA* pRSAPrivateKey = RSA_new();
 BIO* bio = BIO_new(BIO_s_mem());
 BIO_write(bio, strPrivateKey.c_str(), strPrivateKey.size());
 pRSAPrivateKey = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
 if (pRSAPrivateKey == nullptr)
 {
     assert(false);
     return "";
 }

 // 获取密钥长度
 int nLen = RSA_size(pRSAPrivateKey);

 // 返回结果
 std::string strRet;

 // 解密
 char* pEncode = new char[nLen + 1];
 int ret = RSA_private_decrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pEncode, pRSAPrivateKey, RSA_PKCS1_PADDING);
 if (ret >= 0)
 {
     strRet = std::string(pEncode, ret);
 }
 // 释放资源
 delete[] pEncode;
 RSA_free(pRSAPrivateKey);
 CRYPTO_cleanup_all_ex_data();
 return strRet;
}

// 公钥加密
std::string RSAPublicEncrypt(const std::string& strPublicKey, const std::string& strData)
{
 // 判断是否为空
 if (strPublicKey.empty()  strData.empty())
 {
     assert(false);
     return "";
 }

 // 读取公钥
 RSA* pRSAPublicKey = RSA_new();
 BIO* bio = BIO_new(BIO_s_mem());
 BIO_write(bio, strPublicKey.c_str(), strPublicKey.size());
 pRSAPublicKey = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL);
 if (pRSAPublicKey == nullptr)
 {
     assert(false);
     return "";
 }

 // 获取密钥长度
 int nLen = RSA_size(pRSAPublicKey);

 // 返回结果
 std::string strRet;

 // 加密
 char* pEncode = new char[nLen + 1];
 int ret = RSA_public_encrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pEncode, pRSAPublicKey, RSA_PKCS1_PADDING);
 if (ret >= 0)
 {
     strRet = std::string(pEncode, ret);
 }
 // 释放资源
 delete[] pEncode;
 RSA_free(pRSAPublicKey);
 CRYPTO_cleanup_all_ex_data();
 return strRet;
}

// 公钥解密
std::string RSAPublicDecrypt(const std::string& strPublicKey, const std::string& strData)
{
 // 判断是否为空
 if (strPublicKey.empty()  strData.empty())
 {
     assert(false);
     return "";
 }

 // 读取公钥
 RSA* pRSAPublicKey = RSA_new();
 BIO* bio = BIO_new(BIO_s_mem());
 BIO_write(bio, strPublicKey.c_str(), strPublicKey.size());
 pRSAPublicKey = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL);
 if (pRSAPublicKey == nullptr)
 {
     assert(false);
     return "";
 }

 // 获取密钥长度
 int nLen = RSA_size(pRSAPublicKey);

 // 返回结果
 std::string strRet;

 // 解密
 char* pEncode = new char[nLen + 1];
 int ret = RSA_public_decrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pEncode, pRSAPublicKey, RSA_PKCS1_PADDING);
 if (ret >= 0)
 {
     strRet = std::string(pEncode, ret);
 }
 // 释放资源
 delete[] pEncode;
 RSA_free(pRSAPublicKey);
 CRYPTO_cleanup_all_ex_data();
 return strRet;
}

RSA使用实例

#include <iostream>
#include <string>
#include "RSA.h"

using namespace std;

string privateKey = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA9Bhk806dD+exe+rnBEInMhJ9zaE0Ok/3iscSiT9uI/mKdtj2\nQfQ4QH9yRHbIm+yFaPo+xOd2jVjJcJtDiHFk/4IjuE65OWaI7wryMa5/SP8GijNm\nDpPHNbNVfGrL+qL7+H6tZZ9ggFxgE2bqw97MBAW63DtAzeFeQyleHtxUpwRONazU\nYOhz2CZAqOUSoxwztx8CWtZSCNOV4cs67e7Q/uyuZPIxlS2z2MgI65/ez4vqZkj6\nbJHHb5c7gd4MMbnZ54xWI+S3mtcrOF8SV5ITTtt+tgGygm1kNeasKJRRqKqib2IE\nuRo1taKKr++Q9hSBk95saqcKkovyqinr6v1yCQIDAQABAoIBAQChfwJLd9eyjjh/\nJAt0ZdBI8LMLOXy0l/PPfaZl5/GXG6Lgvusu98W/5pJTecOAZhxeODMPU5S8L+IW\n/qLPwzZvVksLxgoGUDCI91UGSc0tHo3VIeyD+IH0pZIJnx8V1H+hCS7v7WKLipKG\nQ7FPpjiU8LWQwCNAE7up89Sx1lSrqoLMf5oyKbZ42Jld0oFBmljp/EW6Mo7tvQlv\nI5tAYccNHKvVwqYk1VMEU9LBqHDLpJXeRMxlxQTB2cfEr/CaAkCJE2mf5REFgsuB\nJ36AwRfKLcwFDPCv9r850l+AYh5NRlkp3FwIi+s5obpMGUfnCdcQMBmcerwwkTyf\ntoUl892BAoGBAPpkQcIGBiegPncUih9RXvEbNx/lmuqncWTd6DcjMuX2Of2ll32V\n1g4Nc8SzfHl3TZPe0qTN61NqH8ZIubNObz+5C8Fa9k+fSeQIbE5iYx2U+9ZLLmFg\nSq7QcAxprWIm5Bu7r6XWzPJtrIjoJ0JF0T3oxITKnaJiCIDhPoSTpPFrAoGBAPmQ\nCMnj1ccB+K7/ODFbx05MDWONpkjFonrQnBrRXThoRklLRA5DAYsxT5iLS7fJ2mKa\nk13eyJZdtJ1JD6DPuGcai/7ZeGn/l2E8fpNapCPyhQeG1CIbqwa6MGGqfPQjqTQ7\nmWWoEH/lEvwH5yx9hlh8ZKnFUyIHoBV8qvMrjiNbAoGBAKBa4q4SU5C/FCII+mgS\nIZ6Bkm0QC4Vp1LoHT3c4SJlzdjIWAY3BDsQTI2f+lqHnoLwpgHdhFOtn1I+U9bB6\nc864gGnFCmd1mMm8Bziv09AXIK1dmodsNof8HzYj25E3XPDR4yxvAvPi/xLysmnD\n8rwWPPzaEdfztoRrPDGFqKWfAoGBAIHh0HEiPlRAVmjdMyWdGnFJa35wbiZZlWJN\nx7C9XcLJoirrHRQ/E0KZ+07s0A9q4lmHEUM9ey+mvSVOrO+Iq/QdANc131FrUCGv\nFkEiX2LGCS4NocHOnIf3xs5NqJJ3LMyeaAtcGJo3YlYA1vN0sMLEmq8wnz+KsGn+\nZAoClQsZAoGAcsOZnAUfRpdu5H1femhlU83MzpdDNsIcgEp6m0on0i4q/e4n2VgJ\nBXi1/K1tbnU2cU6A4FmLlJ1i9Lf7JAdycylRm5BG9ynXeb3uligxh0kN5SvIiADY\n7MLCB6DyqyXaD7uf903lWJOOlBQpL+aypUAQ5jLyr9zaVDiDwuzPAYU=\n-----END RSA PRIVATE KEY-----";
string publicKey = "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA9Bhk806dD+exe+rnBEInMhJ9zaE0Ok/3iscSiT9uI/mKdtj2QfQ4\nQH9yRHbIm+yFaPo+xOd2jVjJcJtDiHFk/4IjuE65OWaI7wryMa5/SP8GijNmDpPH\nNbNVfGrL+qL7+H6tZZ9ggFxgE2bqw97MBAW63DtAzeFeQyleHtxUpwRONazUYOhz\n2CZAqOUSoxwztx8CWtZSCNOV4cs67e7Q/uyuZPIxlS2z2MgI65/ez4vqZkj6bJHH\nb5c7gd4MMbnZ54xWI+S3mtcrOF8SV5ITTtt+tgGygm1kNeasKJRRqKqib2IEuRo1\ntaKKr++Q9hSBk95saqcKkovyqinr6v1yCQIDAQAB\n-----END RSA PUBLIC KEY-----";

int main()
{
 std::string strTest = "测试文本";
 std::string strRet;
 // 测试私钥加密公钥解密
 //strRet = RSAPrivateEncrypt(privateKey, strTest);
 //cout << strRet << endl;
 //strRet = RSAPublicDecrypt(publicKey, strRet);
 //cout << strRet << endl;
 // 测试公钥加密私钥解密
 strRet = RSAPublicEncrypt(publicKey, strTest);
 cout << strRet << endl;
 strRet = RSAPrivateDecrypt(privateKey, strRet);
 cout << strRet << endl;
 return 0;
}

写在最后

上面的我想必你一定学会怎么配置openssl和使用他了,那么再说一个更简单的使用办法就是使用vcpkg可以非常简单的编译并使用openssl库,哈哈哈哈哈哈哈哈哈哈。