当前位置: 首页 > news >正文

OpenSSL3.5.2实现SM3数据摘要生成

OpenSSL库中使用EVP(Enhanced Verification Package增强验证包)进行SM3相关的摘要生成。

其逻辑为:可分为 初始化上下文→配置算法→分块处理数据→生成最终哈希→释放资源 五个步骤。

完整代码如下:

#include <QCoreApplication>
#include <iostream>
#include <openssl/opensslv.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/err.h>
using namespace std;// 计算 SM3 哈希值
int sm3_hash(const unsigned char *data, size_t data_len, unsigned char *hash, unsigned int *hash_len) {// 创建 EVP 上下文EVP_MD_CTX *ctx = EVP_MD_CTX_new();if (ctx == NULL) {fprintf(stderr, "无法创建 EVP 上下文\n");return 0;}// 初始化 SM3 哈希计算if (EVP_DigestInit_ex(ctx, EVP_sm3(), NULL) != 1) {fprintf(stderr, "SM3 初始化失败\n");ERR_print_errors_fp(stderr);EVP_MD_CTX_free(ctx);return 0;}// 更新哈希计算(可以分多次调用更新不同的数据块)if (EVP_DigestUpdate(ctx, data, data_len) != 1) {fprintf(stderr, "SM3 数据更新失败\n");ERR_print_errors_fp(stderr);EVP_MD_CTX_free(ctx);return 0;}// 完成哈希计算并获取结果if (EVP_DigestFinal_ex(ctx, hash, hash_len) != 1) {fprintf(stderr, "SM3 计算完成失败\n");ERR_print_errors_fp(stderr);EVP_MD_CTX_free(ctx);return 0;}// 释放上下文EVP_MD_CTX_free(ctx);return 1;
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 待计算哈希的数据const char *input = "Hello, SM3!";unsigned char hash[EVP_MAX_MD_SIZE];unsigned int hash_len;// 初始化 OpenSSL 错误信息ERR_load_crypto_strings();// 计算 SM3 哈希if (sm3_hash((const unsigned char *)input, strlen(input), hash, &hash_len)) {printf("input data: %s\n", input);printf("SM3 hash: ");for (unsigned int i = 0; i < hash_len; i++) {printf("%02x", hash[i]);}printf("\nhash length: %u byte\n", hash_len);}// 清理错误信息ERR_free_strings();return a.exec();
}

这里有一个需要注意的地方:

EVP_DigestUpdate 支持分多次调用,核心原因是为了处理大数据或流式数据:

  • 对于大文件(如几个 GB 的视频)或网络流(如实时接收的数据),无法一次性将所有数据加载到内存中(会导致内存溢出)。
  • 分多次调用时,每次只需传入一部分数据(例如每次 4KB),上下文 ctx 会保存中间状态,最终合并计算结果,与一次性处理的结果完全一致。
  • 这种设计既节省内存,又支持流式处理(边接收数据边计算哈希),是处理大数据场景的必需特性。

那么问题来了

为什么需要分块更新哈希计算?

分块调用 EVP_DigestUpdate 与 “哈希长度是否固定” 无关,核心原因是哈希算法的底层设计原理和实际工程需求:

1. 哈希算法的 “迭代式计算” 本质

SM3(以及几乎所有现代哈希算法,如 SHA256、MD5 等)的计算过程是迭代式的,而非 “一次性计算”。其底层逻辑是:

  • 将输入数据按固定大小(SM3 中是 512 位,即 64 字节)分成若干块(最后一块不足时会进行补位处理)。
  • 从初始状态开始,每处理一块数据,就用该块数据更新内部状态(一个 256 位的中间变量)。
  • 所有块处理完成后,将最终的内部状态转换为 32 字节的哈希值。

例如:

  • 输入 “abc” 时,数据量小于 512 位,补位后形成一个块,一次处理完成。
  • 输入 1GB 文件时,数据会被分成约 200 万个 512 位块,必须分块处理,每块更新一次内部状态,最终合并为同一个 32 字节哈希值。

EVP_DigestUpdate 的作用就是向算法传递这些分块数据,让算法逐步更新内部状态,它是哈希算法迭代特性的直接体现。

2. 工程上的 “内存限制” 需求

即使哈希算法支持一次性处理数据,实际工程中也必须分块:

  • 对于大文件(如 10GB 的视频),无法将全部数据一次性加载到内存(会导致内存溢出)。
  • 分块处理时,只需在内存中保留一个小块缓冲区(如 4KB),每次读取一块数据并传递给 EVP_DigestUpdate,内存占用始终保持在极低水平。

这种方式既符合哈希算法的迭代逻辑,又能高效处理任意大小的输入(从几字节到几十 GB)。

分块计算的例子。

#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/err.h>// 计算文件的 SM3 哈希值(分块处理大文件)
int sm3_file_hash(const char *file_path, unsigned char *hash, unsigned int *hash_len) {// 打开文件(二进制模式,避免文本模式下的换行符转换)FILE *file = fopen(file_path, "rb");if (!file) {fprintf(stderr, "无法打开文件: %s\n", file_path);return 0;}// 创建 EVP 上下文EVP_MD_CTX *ctx = EVP_MD_CTX_new();if (!ctx) {fprintf(stderr, "无法创建 EVP 上下文\n");fclose(file);return 0;}// 初始化 SM3 算法if (EVP_DigestInit_ex(ctx, EVP_sm3(), NULL) != 1) {fprintf(stderr, "SM3 初始化失败\n");ERR_print_errors_fp(stderr);EVP_MD_CTX_free(ctx);fclose(file);return 0;}// 分块读取文件并更新哈希(每次读取 4KB 块)unsigned char buffer[4096];  // 缓冲区大小,可根据需求调整(如 8192、16384 等)size_t bytes_read;while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {// 每次读取一块数据,更新哈希计算if (EVP_DigestUpdate(ctx, buffer, bytes_read) != 1) {fprintf(stderr, "哈希更新失败(文件读取到 %zu 字节时)\n", bytes_read);ERR_print_errors_fp(stderr);EVP_MD_CTX_free(ctx);fclose(file);return 0;}}// 检查文件读取是否出错if (ferror(file)) {fprintf(stderr, "文件读取错误\n");EVP_MD_CTX_free(ctx);fclose(file);return 0;}// 完成哈希计算并获取结果if (EVP_DigestFinal_ex(ctx, hash, hash_len) != 1) {fprintf(stderr, "SM3 计算完成失败\n");ERR_print_errors_fp(stderr);EVP_MD_CTX_free(ctx);fclose(file);return 0;}// 释放资源EVP_MD_CTX_free(ctx);fclose(file);return 1;
}int main(int argc, char *argv[]) {const char *file_path = "D:/Neo4j/neo4j-community-3.5.5-windows.zip";unsigned char hash[EVP_MAX_MD_SIZE];unsigned int hash_len;// 初始化 OpenSSL 错误信息ERR_load_crypto_strings();// 计算文件的 SM3 哈希if (sm3_file_hash(file_path, hash, &hash_len)) {printf("文件: %s\n", file_path);printf("SM3 哈希值: ");for (unsigned int i = 0; i < hash_len; i++) {printf("%02x", hash[i]);}printf("\n哈希长度: %u 字节\n", hash_len);}// 清理错误信息ERR_free_strings();return 0;
}

运行截图如下:

http://www.dtcms.com/a/536883.html

相关文章:

  • 现代机器人学习入门:一份来自Hugging Face与牛津大学的综合教程开源SOTA资源库
  • 2D SLAM 主流算法推荐汇总和扫地机应用场景
  • 运维实战:SSL 证书故障避坑指南(精简版)
  • google网站管理员中心wordpress 字号 插件
  • 南通智能模板建站群晖wordpress安装
  • 网站建设时图片和文字北京网站定制报价
  • YOLOv5核心代码深度解析
  • SELinux 安全机制
  • 爱奇艺的网站是用什么做的网站tdk建设
  • 网站名是域名吗浙江华企 做网站怎么样
  • 基于python的化妆品推荐系统
  • 深圳网站的公司注册公司流程及费用查询
  • C++仿Muduo库Server服务器模块实现 基于Reactor模式的高性
  • 对IDC(数据中心)运维了解
  • Hyperopt 强大的分布式参数优化框架全解析
  • 网站都必须要备案吗建设一个视频网站首页
  • 前端页面连接后端fastapi实现模型本地部署和open ai接入
  • 中国空间站设计在轨飞行几年旅游网站建设ppt模板下载
  • HR4985微特步进电机驱动器:便捷与高效的完美融合
  • 广州外贸网站制作报名小程序怎么制作
  • 采用 Trie 树结合 RoaringBitmap 技术,构建高效的子串倒排索引
  • 网站建设分工明细表北京快三是官方的吗
  • JMeter:一个简单的测试计划怎么做?
  • VR仿真工业培训软件怎么用?燃气管道仿真实训案例解析
  • wordpress菜单分列顺义网站优化
  • 免费域名的网站九洲建设app
  • 效率工具(小黄鸟Reqable)批量提取小程序商城商品名称价格等信息
  • Shell脚本判断服务器SSH免密是否配置完成
  • MySQL查看服务器/客户端版本
  • express脚手架express-generator