Openssl-单向散列函数

  • Hash MD5 SHA1 SHA2 SHA3 国密SM3

  • openssl的HASH接口编程

  • 编程应用校验文件完整性

  • 掌握OpenSSL消息认证码(HMAC)编程

  • Hash List,模拟文件发送校验

  • Merkle Tree, 使用其写校验文件

  • 文件完整

  • 口令加密

  • 消息认证码

MD5

  • Step1: Append Padding Bits
    取余448 = N * 512 + 448
  • Step2: Apend Length
    在加上448的基础上再加上64
    =>512/8=64/4=16
  • Step3:初始化MD缓冲区
    word A: 01 23 45 67
    word B:89 ab cd ef
    word C:fe dc ba 98
    word D:76 54 32 10
  • Step4:以16字块处理消息

这里发现以前的MD5调用方法被弃用了,改用了EVP接口。
EVP 接口是 OpenSSL 提供的“高级加密接口”,用于统一和简化各种加密算法(如哈希、对称加密、非对称加密等)的调用方式。

主要作用:

  • 统一不同算法的使用方法(如 MD5、SHA256、AES 等)。
  • 提供更高的安全性和灵活性,便于算法切换和升级。
  • 支持硬件加速和新特性。

在哈希计算中的用法:

  • EVP_MD_CTX 表示一个消息摘要(哈希)上下文。
  • EVP_DigestInit_ex 初始化哈希算法(如 EVP_md5())。
  • EVP_DigestUpdate 输入数据。
  • EVP_DigestFinal_ex 得到哈希结果。

总结:
EVP 接口让你用统一的方式调用各种加密和哈希算法,是 OpenSSL 推荐的现代用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<iostream>
#include<openssl/evp.h>
using namespace std;

int main(int argc, char* argv[]) {

cout << "Test Hash !" << endl;
unsigned char data[] = "测试md5";
unsigned char out[EVP_MAX_MD_SIZE] = { 0 };
unsigned int out_len = 0;
int len = sizeof(data);

// 使用 EVP 接口替代 MD5_Init/Update/Final
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
EVP_DigestUpdate(ctx, data, len);
EVP_DigestFinal_ex(ctx, out, &out_len);
EVP_MD_CTX_free(ctx);

for (unsigned int i = 0; i < out_len; i++) {
cout << hex << (int)out[i];
}
cout << endl;

data[1] = 9;
EVP_MD_CTX* ctx2 = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx2, EVP_md5(), NULL);
EVP_DigestUpdate(ctx2, data, len);
EVP_DigestFinal_ex(ctx2, out, &out_len);
EVP_MD_CTX_free(ctx2);

for (unsigned int i = 0; i < out_len; i++) {
cout << hex << (int)out[i];
}
getchar();
return 0;
}

哈希列表-验证文件完整性

  • 哈希列表( Hash List )
  • 读取文件,分块生成Hash值
  • 合并所有Hash值再生成Hash值
  • Hash(Hash(f1)……Hash(f100))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include<iostream>
#include<openssl/evp.h>
#include<fstream>
using namespace std;
string GetFileListHash(string filepath) {
string hash;
ifstream ifs(filepath, ios::binary);

if (!ifs) {
cout << "文件未找到: " << filepath << endl;
return hash;
}
int block_size = 128;
unsigned char buf[1024] = { 0 };
unsigned char out[EVP_MAX_MD_SIZE] = { 0 };
unsigned int out_len = 0;

// 使用 EVP 计算文件 MD5
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx, EVP_md5(), NULL);

while (ifs) {
ifs.read((char*)buf, block_size);
int read_size = ifs.gcount();
if (read_size > 0) {
EVP_DigestUpdate(ctx, buf, read_size);
}
}

EVP_DigestFinal_ex(ctx, out, &out_len);
EVP_MD_CTX_free(ctx);

// 转16进制字符串
for (unsigned int i = 0; i < out_len; i++) {
char tmp[3] = { 0 };
sprintf_s(tmp, sizeof(tmp), "%02x", out[i]);
hash += tmp;
}

return hash;
}
int main(int argc, char* argv[]) {

cout << "Test Hash !" << endl;
unsigned char data[] = "测试md5";
unsigned char out[EVP_MAX_MD_SIZE] = { 0 };
unsigned int out_len = 0;
int len = sizeof(data);

// 使用 EVP 接口替代 MD5_Init/Update/Final
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
EVP_DigestUpdate(ctx, data, len);
EVP_DigestFinal_ex(ctx, out, &out_len);
EVP_MD_CTX_free(ctx);

for (unsigned int i = 0; i < out_len; i++) {
printf("%02x", out[i]);
}
cout << endl;

data[1] = 9;
EVP_MD_CTX* ctx2 = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx2, EVP_md5(), NULL);
EVP_DigestUpdate(ctx2, data, len);
EVP_DigestFinal_ex(ctx2, out, &out_len);
EVP_MD_CTX_free(ctx2);

for (unsigned int i = 0; i < out_len; i++) {
printf("%02x", out[i]);
}

auto hash = GetFileListHash("a.md");
cout << "hash " << hash << endl;

getchar();
return 0;
}

Openssl-单向散列函数
https://43.242.201.154/2025/07/25/Openssl-单向散列函数/
Author
Dong
Posted on
July 25, 2025
Licensed under