使用OpenSSL推荐的EVP高级接口计算摘要
OpenSSL 源代码中低级接口被标记为废弃 deprecated,为了与 OpenSSL 保持同步,记录一下 OpenSSL 高级接口计算摘要的方法。低级接口的使用请参考前面的文章——OpenSSL-3.0.3编程—计算数据摘要
直接使用openssl API 计算摘要一般包含如下三个步骤,与低级接口类似,多了一步分配空间
- 创建摘要上下文,分配空间
- 初始化指定摘要算法的上下文
- 更新数据。可以添加多次,比如"hello, world" 可以分两次更新,可以参考如下代码
- 计算摘要值。对第3步更新的所有的数据计算摘要,通过传出参数返回摘要值
- 使用 EVP_MD_CTX_free 释放资源(重要)
- 将第4步计算的二进制摘要值转换为十六进制或Base64输出,本步骤可省略,看具体需要采用合适的方式
样例代码
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/md5.h>
#include <string.h>
#include <iostream>
#include "CommonFun.h"
int main(int argc, char* argv[]) {
// 1.创建摘要上下文,分配空间
EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
if (mdctx == nullptr) {
handleErrors();
}
// 2.用指定摘要算法初始化上下文指针
int retInit = EVP_DigestInit_ex(mdctx, EVP_sha384(), nullptr);
if (retInit != 1) {
handleErrors();
}
const char* data = "123456";
// 3.传入待计算摘要的原始数据,update。可以调用多次 update
int retUpdate = EVP_DigestUpdate(mdctx, data, strlen(data));
if (retUpdate != 1) {
handleErrors();
}
unsigned char* md = new unsigned char[MD5_DIGEST_LENGTH];
unsigned int digestLength = MD5_DIGEST_LENGTH;
// 4.计算哈希值
int retFinal = EVP_DigestFinal_ex(mdctx, md, &digestLength);
if (retFinal != 1) {
handleErrors();
}
// 5.将二进制哈希值转换为十六进制输出
char* mdHex = new char[digestLength * 2 + 1];
for (int i = 0; i < digestLength; ++i) {
sprintf(&mdHex[i * 2], "%02x", md[i]);
}
std::cout << "摘要值: " << mdHex << std::endl;
delete[] mdHex;
EVP_MD_CTX_free(mdctx);
return 0;
}
推荐阅读 OpenSSL 官方 wiki
这个 OpenSSL wiki 网站包含一些简单的 sdk api 使用,它的更新好像不是很及时,不过初学者可以借鉴
摘要实现类简要说明
函数 EVP_DigestInit_ex 的第二个参数可以指定摘要算法,如 EVP_sha1、EVP_sha224、EVP_sha256、EVP_sha3_256、EVP_sm3……另外,EVP_sha256 与 EVP_sha3_256 有啥区别还没明白,以后明白了再补充
特别说明: OpenSSL3 里面包含国密算法,如 sm2、sm3、sm4,若要使用sm3计算摘要,可以在函数 EVP_DigestInit_ex 第二个参数传入 EVP_sm3()
几个函数的使用步骤
- 步骤1: 使用 EVP_MD_CTX_new 函数给摘要上下文指针分配空间
- 步骤2: 使用 EVP_DigestInit_ex 函数指定摘要算法,并初始化上下文指针
- 步骤3: 使用 EVP_DigestUpdate 函数更新数据,可以调用多次
- 步骤4: 使用 EVP_DigestFinal_ex 计算摘要
- 步骤5: 补充一个比较重要的步骤,使用 EVP_MD_CTX_free 释放资源
- 步骤6: (可省略)将二进制摘要值转换为十六进制输出
handleErrors 函数
#include <iostream>
#include <openssl/err.h>
void handleErrors(void) {
ERR_print_errors_fp(stderr);
abort();
}