Files
blackbird/libcrypt/inc/us/MyRSA.hpp
T
qing 3d03f7a1ce actpred: 代码审计(16项发现全部修复)+ 文档更新
审计报告: docs/reviews/actpred_CODE_AUDIT_20260808.md
- P0(2项): episode索引错位(dataset.py)、推理归一化静默失败(predict.py)
- P1(6项): 空训练集校验、图像自然排序、空样本防御、形状校验、
  MobileNet下载错误提示、stats路径校验
- P2(8项): README示例修正、公共属性、glob优化、版本约束、
  单元测试(17项)、GPU安装说明、BooleanOptionalAction、ONNX验证

新增文件: actpred/test_actpred.py (17项单元测试, 全部通过)
更新文档: README.md配置表扩充、INDEX.md、审计报告修复总结
2026-08-08 16:05:01 +00:00

568 lines
23 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include "Crypto_Basic.hpp"
#include <iostream>
#include <fstream>
#include <vector>
#include <memory>
#include <stdexcept>
#include <sstream>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/core_names.h>
#include <openssl/rsa.h>
// 错误处理宏(打印完整错误栈,便于调试)
#define HANDLE_SSL_ERROR() { \
ERR_print_errors_fp(stderr); \
}
// 自定义删除器用于管理OpenSSL资源
struct EVP_PKEY_deleter {
void operator()(EVP_PKEY* pkey) const { EVP_PKEY_free(pkey); }
};
using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, EVP_PKEY_deleter>;
struct EVP_PKEY_CTX_deleter {
void operator()(EVP_PKEY_CTX* ctx) const { EVP_PKEY_CTX_free(ctx); }
};
using EVP_PKEY_CTX_ptr = std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_deleter>;
struct BIO_deleter {
void operator()(BIO* bio) const { BIO_free_all(bio); }
};
using BIO_ptr = std::unique_ptr<BIO, BIO_deleter>;
struct EVP_MD_CTX_deleter {
void operator()(EVP_MD_CTX* ctx) const { EVP_MD_CTX_free(ctx); }
};
using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, EVP_MD_CTX_deleter>;
namespace qing {
// 签名填充模式枚举
enum class RSASignaturePadding {
PKCS1_v1_5, // 传统 PKCS#1 v1.5 填充(兼容旧系统)
PSS // 概率签名方案(更安全,推荐)
};
class MyRSA : public qing::Crypto_Basic {
public:
class Generator {
public:
Generator() {
key = generate_rsa_key();
if (!key) throw Crypto_Basic::CryptoExp("RSA 密钥对生成失败");
}
// 保存私钥为 PKCS#8 格式(AES-256-CBC 加密),支持密码保护
void save_pri_key(const std::string& filename, const char* password = nullptr) {
if (!save_private_key(key.get(), filename, password)) {
throw Crypto_Basic::CryptoExp("保存私钥失败。");
}
}
// 保存公钥(现代 SubjectPublicKeyInfo 格式)
void save_pub_key(const std::string& filename) {
if (!save_public_key(key.get(), filename)) {
throw Crypto_Basic::CryptoExp("保存公钥失败。");
}
}
// 获取密钥(直接使用)
EVP_PKEY* get_private_key() const { return key.get(); }
// 导出为 PEM 字符串(PKCS#8 格式)
std::string get_private_key_pem(const char* password = nullptr) {
return key_to_pem(key.get(), true, password);
}
std::string get_public_key_pem() {
return key_to_pem(key.get(), false);
}
private:
EVP_PKEY_ptr key;
static EVP_PKEY_ptr generate_rsa_key(size_t key_bits = 2048) {
EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr));
if (!ctx) {
HANDLE_SSL_ERROR();
return nullptr;
}
if (EVP_PKEY_keygen_init(ctx.get()) <= 0) {
HANDLE_SSL_ERROR();
return nullptr;
}
OSSL_PARAM params[] = {
OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_RSA_BITS, &key_bits),
OSSL_PARAM_END
};
if (EVP_PKEY_CTX_set_params(ctx.get(), params) <= 0) {
HANDLE_SSL_ERROR();
return nullptr;
}
EVP_PKEY* pkey = nullptr;
if (EVP_PKEY_generate(ctx.get(), &pkey) <= 0) {
HANDLE_SSL_ERROR();
return nullptr;
}
return EVP_PKEY_ptr(pkey);
}
// 保存私钥为 PKCS#8 加密格式
static bool save_private_key(EVP_PKEY* pkey, const std::string& filename,
const char* password = nullptr) {
BIO_ptr bio(BIO_new_file(filename.c_str(), "wb"));
if (!bio) {
std::cerr << "Failed to open file: " << filename << std::endl;
return false;
}
const EVP_CIPHER* cipher = password ? EVP_aes_256_cbc() : nullptr;
int ret = PEM_write_bio_PKCS8PrivateKey(bio.get(), pkey, cipher,
(char*)password,
password ? static_cast<int>(strlen(password)) : 0,
nullptr, nullptr);
if (ret != 1) {
HANDLE_SSL_ERROR();
return false;
}
return true;
}
static bool save_public_key(EVP_PKEY* pkey, const std::string& filename) {
BIO_ptr bio(BIO_new_file(filename.c_str(), "wb"));
if (!bio) {
std::cerr << "Failed to open file: " << filename << std::endl;
return false;
}
int ret = PEM_write_bio_PUBKEY(bio.get(), pkey);
if (ret != 1) {
HANDLE_SSL_ERROR();
return false;
}
return true;
}
static std::string key_to_pem(EVP_PKEY* pkey, bool is_private,
const char* password = nullptr) {
BIO_ptr bio(BIO_new(BIO_s_mem()));
if (!bio) {
HANDLE_SSL_ERROR();
throw Crypto_Basic::CryptoExp("导出 PEM 时无法创建 BIO");
}
int ret;
if (is_private) {
const EVP_CIPHER* cipher = password ? EVP_aes_256_cbc() : nullptr;
ret = PEM_write_bio_PKCS8PrivateKey(bio.get(), pkey, cipher,
(char*)password,
password ? static_cast<int>(strlen(password)) : 0,
nullptr, nullptr);
} else {
ret = PEM_write_bio_PUBKEY(bio.get(), pkey);
}
if (ret != 1) {
HANDLE_SSL_ERROR();
throw Crypto_Basic::CryptoExp("导出 PEM 失败");
}
char* data;
long len = BIO_get_mem_data(bio.get(), &data);
return std::string(data, len);
}
};
// 私钥类:支持 PKCS#1 和 PKCS#8 格式(含加密)
class Private_Key : Crypto_Basic {
public:
// 从文件加载(支持密码)
Private_Key(const std::string& fullpath, bool from_file = true,
const char* password = nullptr) {
if (from_file) {
if (!(pri_key = load_private_key_from_file(fullpath, password)))
throw Crypto_Basic::CryptoExp("读取私钥失败");
} else {
if (!(pri_key = load_private_key_from_string(fullpath, password)))
throw Crypto_Basic::CryptoExp("从字符串加载私钥失败");
}
}
static Private_Key from_pem(const std::string& pem_string,
const char* password = nullptr) {
return Private_Key(pem_string, false, password);
}
// 解密(OAEP)
std::string Decrypt(const std::string& cipher_hex) {
std::vector<unsigned char> ciphertext = hex_to_bytes(cipher_hex);
std::vector<unsigned char> decrypted = rsa_decrypt(pri_key.get(), ciphertext);
if (decrypted.empty()) throw Crypto_Basic::CryptoExp("解密失败");
return std::string(decrypted.begin(), decrypted.end());
}
// 签名(支持 PSS 和 PKCS#1 v1.5)
std::string Sign(const std::string& message,
RSASignaturePadding padding = RSASignaturePadding::PSS) {
std::vector<unsigned char> msg(message.begin(), message.end());
std::vector<unsigned char> signature = rsa_sign(pri_key.get(), msg, padding);
if (signature.empty()) throw Crypto_Basic::CryptoExp("签名失败");
return bytes_to_hex(signature);
}
std::vector<unsigned char> SignBytes(const std::string& message,
RSASignaturePadding padding = RSASignaturePadding::PSS) {
std::vector<unsigned char> msg(message.begin(), message.end());
return rsa_sign(pri_key.get(), msg, padding);
}
std::string Sign(const std::vector<unsigned char>& message,
RSASignaturePadding padding = RSASignaturePadding::PSS) {
std::vector<unsigned char> signature = rsa_sign(pri_key.get(), message, padding);
if (signature.empty()) throw Crypto_Basic::CryptoExp("签名失败");
return bytes_to_hex(signature);
}
std::vector<unsigned char> SignBytes(const std::vector<unsigned char>& message,
RSASignaturePadding padding = RSASignaturePadding::PSS) {
return rsa_sign(pri_key.get(), message, padding);
}
EVP_PKEY* get() const { return pri_key.get(); }
private:
EVP_PKEY_ptr pri_key;
static EVP_PKEY_ptr load_private_key_from_file(const std::string& filename,
const char* password = nullptr) {
BIO_ptr bio(BIO_new_file(filename.c_str(), "rb"));
if (!bio) {
std::cerr << "Failed to open file: " << filename << std::endl;
return nullptr;
}
pem_password_cb* cb = password ? &password_callback : nullptr;
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio.get(), nullptr, cb, (void*)password);
if (!pkey) {
HANDLE_SSL_ERROR();
return nullptr;
}
return EVP_PKEY_ptr(pkey);
}
static EVP_PKEY_ptr load_private_key_from_string(const std::string& pem_string,
const char* password = nullptr) {
BIO_ptr bio(BIO_new_mem_buf(pem_string.c_str(), -1));
if (!bio) {
std::cerr << "Failed to create BIO from string" << std::endl;
return nullptr;
}
pem_password_cb* cb = password ? &password_callback : nullptr;
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio.get(), nullptr, cb, (void*)password);
if (!pkey) {
HANDLE_SSL_ERROR();
return nullptr;
}
return EVP_PKEY_ptr(pkey);
}
// 密码回调(共享,消除重复 lambda)
static int password_callback(char* buf, int size, int rwflag, void* u) {
if (!u) return 0;
const char* pass = static_cast<const char*>(u);
int len = static_cast<int>(strlen(pass));
if (len > size) len = size;
memcpy(buf, pass, len);
return len;
}
// RSA 解密(OAEP)
static std::vector<unsigned char> rsa_decrypt(EVP_PKEY* private_key,
const std::vector<unsigned char>& ciphertext) {
EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new(private_key, nullptr));
if (!ctx) {
HANDLE_SSL_ERROR();
return {};
}
if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
size_t outlen = 0;
if (EVP_PKEY_decrypt(ctx.get(), nullptr, &outlen,
ciphertext.data(), ciphertext.size()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
std::vector<unsigned char> plaintext(outlen);
if (EVP_PKEY_decrypt(ctx.get(), plaintext.data(), &outlen,
ciphertext.data(), ciphertext.size()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
plaintext.resize(outlen);
return plaintext;
}
// RSA 签名(修正了 PSS 参数顺序)
static std::vector<unsigned char> rsa_sign(EVP_PKEY* private_key,
const std::vector<unsigned char>& message,
RSASignaturePadding padding) {
EVP_MD_CTX_ptr ctx(EVP_MD_CTX_new());
if (!ctx) {
HANDLE_SSL_ERROR();
return {};
}
const EVP_MD* md = EVP_sha256();
if (EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr, private_key) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
EVP_PKEY_CTX* pkey_ctx = EVP_MD_CTX_get_pkey_ctx(ctx.get());
if (!pkey_ctx) {
HANDLE_SSL_ERROR();
return {};
}
int padding_mode;
if (padding == RSASignaturePadding::PKCS1_v1_5) {
padding_mode = RSA_PKCS1_PADDING;
} else {
padding_mode = RSA_PKCS1_PSS_PADDING;
}
// 先设置填充模式(关键)
if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_mode) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
// 如果是 PSS,再设置盐长度
if (padding == RSASignaturePadding::PSS) {
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
}
size_t siglen = 0;
if (EVP_DigestSign(ctx.get(), nullptr, &siglen,
message.data(), message.size()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
std::vector<unsigned char> signature(siglen);
if (EVP_DigestSign(ctx.get(), signature.data(), &siglen,
message.data(), message.size()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
signature.resize(siglen);
return signature;
}
};
// 公钥类
class Public_Key : public Crypto_Basic {
public:
Public_Key(const std::string& filename, bool from_file = true) {
if (from_file) {
if (!(pub_key = load_public_key_from_file(filename)))
throw Crypto_Basic::CryptoExp("读取公钥失败");
} else {
if (!(pub_key = load_public_key_from_string(filename)))
throw Crypto_Basic::CryptoExp("从字符串加载公钥失败");
}
}
static Public_Key from_pem(const std::string& pem_string) {
return Public_Key(pem_string, false);
}
// 加密(OAEP)
std::string Encrypt(const std::string& original_text) {
std::vector<unsigned char> plaintext(original_text.begin(), original_text.end());
std::vector<unsigned char> ciphertext = rsa_encrypt(pub_key.get(), plaintext);
if (ciphertext.empty()) throw Crypto_Basic::CryptoExp("加密失败");
return bytes_to_hex(ciphertext);
}
// 验证签名(支持 PSS 和 PKCS#1 v1.5)
bool Verify(const std::string& message, const std::string& signature_hex,
RSASignaturePadding padding = RSASignaturePadding::PSS) {
std::vector<unsigned char> msg(message.begin(), message.end());
std::vector<unsigned char> signature = hex_to_bytes(signature_hex);
return rsa_verify(pub_key.get(), msg, signature, padding);
}
bool Verify(const std::string& message, const std::vector<unsigned char>& signature,
RSASignaturePadding padding = RSASignaturePadding::PSS) {
std::vector<unsigned char> msg(message.begin(), message.end());
return rsa_verify(pub_key.get(), msg, signature, padding);
}
bool Verify(const std::vector<unsigned char>& message, const std::string& signature_hex,
RSASignaturePadding padding = RSASignaturePadding::PSS) {
std::vector<unsigned char> signature = hex_to_bytes(signature_hex);
return rsa_verify(pub_key.get(), message, signature, padding);
}
bool Verify(const std::vector<unsigned char>& message,
const std::vector<unsigned char>& signature,
RSASignaturePadding padding = RSASignaturePadding::PSS) {
return rsa_verify(pub_key.get(), message, signature, padding);
}
EVP_PKEY* get() const { return pub_key.get(); }
private:
EVP_PKEY_ptr pub_key;
static EVP_PKEY_ptr load_public_key_from_file(const std::string& filename) {
BIO_ptr bio(BIO_new_file(filename.c_str(), "rb"));
if (!bio) {
std::cerr << "Failed to open file: " << filename << std::endl;
return nullptr;
}
EVP_PKEY* pkey = PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr);
if (!pkey) {
HANDLE_SSL_ERROR();
return nullptr;
}
return EVP_PKEY_ptr(pkey);
}
static EVP_PKEY_ptr load_public_key_from_string(const std::string& pem_string) {
BIO_ptr bio(BIO_new_mem_buf(pem_string.c_str(), -1));
if (!bio) {
std::cerr << "Failed to create BIO from string" << std::endl;
return nullptr;
}
EVP_PKEY* pkey = PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr);
if (!pkey) {
HANDLE_SSL_ERROR();
return nullptr;
}
return EVP_PKEY_ptr(pkey);
}
static std::vector<unsigned char> rsa_encrypt(EVP_PKEY* public_key,
const std::vector<unsigned char>& plaintext) {
EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new(public_key, nullptr));
if (!ctx) {
HANDLE_SSL_ERROR();
return {};
}
if (EVP_PKEY_encrypt_init(ctx.get()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
size_t outlen = 0;
if (EVP_PKEY_encrypt(ctx.get(), nullptr, &outlen,
plaintext.data(), plaintext.size()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
std::vector<unsigned char> ciphertext(outlen);
if (EVP_PKEY_encrypt(ctx.get(), ciphertext.data(), &outlen,
plaintext.data(), plaintext.size()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
ciphertext.resize(outlen);
return ciphertext;
}
// RSA 验证(修正了 PSS 参数顺序)
static bool rsa_verify(EVP_PKEY* public_key,
const std::vector<unsigned char>& message,
const std::vector<unsigned char>& signature,
RSASignaturePadding padding) {
EVP_MD_CTX_ptr ctx(EVP_MD_CTX_new());
if (!ctx) {
HANDLE_SSL_ERROR();
return false;
}
const EVP_MD* md = EVP_sha256();
if (EVP_DigestVerifyInit(ctx.get(), nullptr, md, nullptr, public_key) <= 0) {
HANDLE_SSL_ERROR();
return false;
}
EVP_PKEY_CTX* pkey_ctx = EVP_MD_CTX_get_pkey_ctx(ctx.get());
if (!pkey_ctx) {
HANDLE_SSL_ERROR();
return false;
}
int padding_mode;
if (padding == RSASignaturePadding::PKCS1_v1_5) {
padding_mode = RSA_PKCS1_PADDING;
} else {
padding_mode = RSA_PKCS1_PSS_PADDING;
}
// 先设置填充模式(关键)
if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_mode) <= 0) {
HANDLE_SSL_ERROR();
return false;
}
// 如果是 PSS,再设置盐长度
if (padding == RSASignaturePadding::PSS) {
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) <= 0) {
HANDLE_SSL_ERROR();
return false;
}
}
int result = EVP_DigestVerify(ctx.get(), signature.data(), signature.size(),
message.data(), message.size());
if (result == 1) {
return true;
} else if (result == 0) {
return false;
} else {
HANDLE_SSL_ERROR();
return false;
}
}
};
};
} // namespace qing