-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCryptoUtil.cpp
More file actions
168 lines (142 loc) · 5.7 KB
/
CryptoUtil.cpp
File metadata and controls
168 lines (142 loc) · 5.7 KB
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include "CryptoUtil.h"
#include "Protocol.h"
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <spdlog/spdlog.h>
#include <sstream>
#include <iomanip>
#include <cstring>
bool CryptoUtil::Encrypt(const std::vector<uint8_t> &plaintext, std::vector<uint8_t> &ciphertext) {
// 1. EVP_CIPHER_CTX 是 OpenSSL 的加密操作上下文,必须显式创建和释放
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if(!ctx) {
spdlog::error("[Crypto] EVP_CIPHER_CTX_new 失败,OpenSSL 可能未正确初始化");
return false;
}
// 2. 初始化 AES-256-CBC 加密
// 参数: ctx, 算法, ENGINE(nullptr=默认), 密钥, IV
if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr,
GetAESKey(), GetAESIV()) != 1)
{
spdlog::error("[Crypto] EVP_EncryptInit_ex 失败");
EVP_CIPHER_CTX_free(ctx);
return false;
}
// 3. 预分配输出缓冲区:AES-CBC 输出最多比输入多一个块 (16 字节)
// 公式: output_len_max = input_len + AES_BLOCK_SIZE
const int block_size = EVP_CIPHER_CTX_get_block_size(ctx);
ciphertext.resize(plaintext.size() + block_size);
int out_len1 = 0;
// 4. EVP_EncryptUpdate: 将 plaintext 分块加密,结果写入 ciphertext
// 注意: 不一定产生与输入等长的输出(CBC 需要等到块满才输出)
if (EVP_EncryptUpdate(ctx,
ciphertext.data(), &out_len1,
plaintext.empty() ? nullptr : plaintext.data(),
static_cast<int>(plaintext.size())) != 1)
{
spdlog::error("[Crypto] EVP_EncryptUpdate 失败");
EVP_CIPHER_CTX_free(ctx);
return false;
}
int out_len2 = 0;
// 5. EVP_EncryptFinal_ex: 处理最后一块并添加 PKCS#7 填充
// 即使明文长度是块大小的整数倍,也会额外添加一整块填充(全为 0x10)
// 这样解密方可以无歧义地识别填充边界
if (EVP_EncryptFinal_ex(ctx, ciphertext.data() + out_len1, &out_len2) != 1) {
spdlog::error("[Crypto] EVP_EncryptFinal_ex 失败,可能是填充错误");
EVP_CIPHER_CTX_free(ctx);
return false;
}
// 调整为实际输出大小
ciphertext.resize(out_len1 + out_len2);
// 释放上下文
EVP_CIPHER_CTX_free(ctx);
return true;
}
bool CryptoUtil::Decrypt(const std::vector<uint8_t> &ciphertext, std::vector<uint8_t> &plaintext) {
if (ciphertext.empty()) {
plaintext.clear();
return true;
}
// 1. 创建上下文
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
spdlog::error("[Crypto] EVP_CIPHER_CTX_new 失败 (Decrypt)");
return false;
}
// 2. 初始化 AES-256-CBC 解密,使用相同的密钥和 IV
if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr,
GetAESKey(), GetAESIV()) != 1)
{
spdlog::error("[Crypto] EVP_DecryptInit_ex 失败");
EVP_CIPHER_CTX_free(ctx);
return false;
}
// 3. 预分配解密输出缓冲区(解密后长度 <= 密文长度)
plaintext.resize(ciphertext.size());
int out_len1 = 0;
if (EVP_DecryptUpdate(ctx,
plaintext.data(), &out_len1,
ciphertext.data(),
static_cast<int>(ciphertext.size())) != 1)
{
spdlog::error("[Crypto] EVP_DecryptUpdate 失败");
EVP_CIPHER_CTX_free(ctx);
return false;
}
int out_len2 = 0;
// 4. EVP_DecryptFinal_ex: 去除 PKCS#7 填充,返回实际明文
// 若密文损坏(填充不合法),此函数返回 0 并报错
if (EVP_DecryptFinal_ex(ctx, plaintext.data() + out_len1, &out_len2) != 1) {
spdlog::error("[Crypto] EVP_DecryptFinal_ex 失败:密文损坏或填充错误");
EVP_CIPHER_CTX_free(ctx);
return false;
}
// 调整到去除填充后的实际明文长度
plaintext.resize(out_len1 + out_len2);
// 释放上下文
EVP_CIPHER_CTX_free(ctx);
return true;
}
bool CryptoUtil::EncryptString(const std::string& plaintext, std::vector<uint8_t>& ciphertext) {
std::vector<uint8_t> bytes(plaintext.begin(), plaintext.end());
return Encrypt(bytes, ciphertext);
}
bool CryptoUtil::DecryptToString(const std::vector<uint8_t>& ciphertext, std::string& plaintext) {
std::vector<uint8_t> bytes;
if (!Decrypt(ciphertext, bytes)) return false;
plaintext.assign(bytes.begin(), bytes.end());
return true;
}
// =========================================================
// SHA-256 密码哈希
//
// 加密策略: hash(username + ":" + password)
// - 不同用户即使使用相同密码,哈希值也不同
// - 防止攻击者使用彩虹表(预计算哈希表)批量破解
//
// 使用 OpenSSL EVP_DigestX 接口
// =========================================================
std::string CryptoUtil::HashPassword(const std::string& username, const std::string& password) {
// 1. 拼接加密输入
std::string salted = username + ":" + password;
// 2. 生成加密操作上下文
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned int hash_len = 0;
// 3. 初始哈希计算
EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr);
// 4. 追加哈希数据
EVP_DigestUpdate(ctx, salted.c_str(), salted.size());
// 5. 完成哈希计算
EVP_DigestFinal_ex(ctx, hash, &hash_len);
// 6. 释放上下文
EVP_MD_CTX_free(ctx);
// 将二进制哈希转换为十六进制字符串
std::ostringstream oss;
for (unsigned int i = 0; i < hash_len; ++i) {
oss << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<int>(hash[i]);
}
return oss.str();
}