使用 c++ 实现对文件签名和验证
使用 c++ 实现对文件签名和验证
之前在实现 SOTA 升级之前使用 Python 实现了签名认证的功能,今天采用 c++ 重构之前的代码,方便集成到 SOTA 代码里。
代码
ec_sig.cpp
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 <iostream>
#include <fstream>
#include <vector>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/ec.h>
#include <openssl/err.h>
// 错误处理函数
void handle_errors(const char* context = nullptr) {
char buf[1024];
ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
std::cerr << "Error [" << (context ? context : "general") << "]: " << buf << std::endl;
exit(1);
}
EVP_PKEY* load_ec_private_key(const char* key_path) {
BIO* bio = BIO_new_file(key_path, "r");
if (!bio) handle_errors("open private key");
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr);
BIO_free(bio);
if (!pkey || EVP_PKEY_id(pkey) != EVP_PKEY_EC) {
std::cerr << "Invalid ECDSA private key format" << std::endl;
exit(1);
}
return pkey;
}
EVP_PKEY* load_ec_public_key(const char* key_path) {
BIO* bio = BIO_new_file(key_path, "r");
if (!bio) handle_errors("open public key");
EVP_PKEY* pkey = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr);
BIO_free(bio);
if (!pkey || EVP_PKEY_id(pkey) != EVP_PKEY_EC) {
std::cerr << "Invalid ECDSA public key format" << std::endl;
exit(1);
}
return pkey;
}
bool ecdsa_sign_file(const char* file_path,
const char* key_path,
const char* sig_path) {
EVP_PKEY* pkey = load_ec_private_key(key_path);
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
if (!ctx) handle_errors("create sign context");
if (EVP_DigestSignInit(ctx, nullptr, EVP_sha256(), nullptr, pkey) != 1)
handle_errors("sign init");
std::ifstream file(file_path, std::ios::binary);
if (!file) {
std::cerr << "Cannot open input file: " << file_path << std::endl;
return false;
}
// 读取整个文件到内存(适用于小文件)
file.seekg(0, std::ios::end);
size_t file_size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<unsigned char> data(file_size);
if (!file.read(reinterpret_cast<char*>(data.data()), file_size)) {
std::cerr << "Read file failed: " << file_path << std::endl;
return false;
}
size_t sig_len;
if (EVP_DigestSign(ctx, nullptr, &sig_len, data.data(), data.size()) != 1)
handle_errors("get signature length");
std::vector<unsigned char> signature(sig_len);
if (EVP_DigestSign(ctx, signature.data(), &sig_len, data.data(), data.size()) != 1)
handle_errors("finalize signature");
std::ofstream sig_file(sig_path, std::ios::binary);
if (!sig_file) {
std::cerr << "Cannot create signature file: " << sig_path << std::endl;
return false;
}
sig_file.write(reinterpret_cast<char*>(signature.data()), sig_len);
std::cout << "Signature generated (" << sig_len << " bytes)" << std::endl;
EVP_MD_CTX_free(ctx);
EVP_PKEY_free(pkey);
return true;
}
bool ecdsa_verify_signature(const char* file_path,
const char* key_path,
const char* sig_path) {
EVP_PKEY* pkey = load_ec_public_key(key_path);
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
if (!ctx) handle_errors("create verify context");
if (EVP_DigestVerifyInit(ctx, nullptr, EVP_sha256(), nullptr, pkey) != 1)
handle_errors("verify init");
std::ifstream file(file_path, std::ios::binary);
if (!file) {
std::cerr << "Cannot open input file: " << file_path << std::endl;
return false;
}
file.seekg(0, std::ios::end);
size_t file_size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<unsigned char> data(file_size);
if (!file.read(reinterpret_cast<char*>(data.data()), file_size)) {
std::cerr << "Read file failed: " << file_path << std::endl;
return false;
}
std::ifstream sig_file(sig_path, std::ios::binary);
if (!sig_file) {
std::cerr << "Cannot open signature file: " << sig_path << std::endl;
return false;
}
sig_file.seekg(0, std::ios::end);
size_t sig_len = sig_file.tellg();
sig_file.seekg(0, std::ios::beg);
std::vector<unsigned char> signature(sig_len);
if (!sig_file.read(reinterpret_cast<char*>(signature.data()), sig_len)) {
std::cerr << "Read signature failed" << std::endl;
return false;
}
int result = EVP_DigestVerify(ctx, signature.data(), sig_len,
data.data(), data.size());
EVP_MD_CTX_free(ctx);
EVP_PKEY_free(pkey);
return result == 1;
}
int main() {
const char* private_key = "ec_private.pem";
const char* public_key = "ec_public.pem";
const char* data_file = "document.bin";
const char* sig_file = "signature.der"; // 改为 .der 扩展名
// 生成签名
if (ecdsa_sign_file(data_file, private_key, sig_file)) {
std::cout << "\n--- 签名成功 ---" << std::endl;
} else {
std::cerr << "\n!!! 签名失败 !!!" << std::endl;
return 1;
}
// 验证签名
if (ecdsa_verify_signature(data_file, public_key, sig_file)) {
std::cout << "\n=== 验证成功 ===" << std::endl;
} else {
std::cerr << "\n!!! 验证失败 !!!" << std::endl;
return 1;
}
return 0;
}
验证步骤
- 生成新密钥对
1 2
openssl ecparam -name prime256v1 -genkey -noout -out ec_private.pem openssl ec -in ec_private.pem -pubout -out ec_public.pem
- 创建测试文件
1
echo "Important Data $(date)" > document.bin
- 编译并运行
1 2
g++ -std=c++11 -Wall ec_sig.cpp -o ec_sig -lssl -lcrypto ./ec_sig
- 手动验证签名
1 2 3 4 5 6
# 生成签名 openssl dgst -sha256 -sign ec_private.pem -out openssl_sig.der document.bin # 比较两个签名文件 diff signature.der openssl_sig.der # 使用OpenSSL验证 openssl dgst -sha256 -verify ec_public.pem -signature signature.der document.bin
- 输出
1 2 3
Signature generated (72 bytes) --- 签名成功 --- === 验证成功 ===
本文由作者按照 CC BY 4.0 进行授权