Closed quinnwencn closed 3 months ago
RSA签名算法的逻辑是,先对消息计算哈希值,再对哈希进行私钥加密(签名),生成数字签名,如图所示: 但是RSA有一个致命问题:选择密文攻击。因此,可以考虑在签名前对哈希值进行填充,已达到防止选择密文攻击的目的。目前常见的填充算法有:RSA_PKCS1_PADDING_v1.5、RSA_PKCS1_OAEP_PADDING,RSASSA-PKCS1-v1.5其实就是RSA_PKCS1_PADDING_v1.5填充算法。
RSA_PKCS1_PADDING_v1.5
RSA_PKCS1_OAEP_PADDING
RSASSA-PKCS1-v1.5
采用RSASSA-PSS的签名算法,实际上就是在签名前使用RSA-PSS填充了哈希,然后再签名,过程如图所示: 其中EM的生成逻辑如下:
// // Created by Robert on 24-3-18. //
std::string SHA256_HASH(const std::string& input) { uint8_t hash[SHA256_DIGEST_LENGTH]; SHA256_CTX sha256; SHA256_Init(&sha256); SHA256_Update(&sha256, input.c_str(), input.length()); SHA256_Final(hash, &sha256); std::string result {""}; char buf[2]; for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { sprintf(buf, "%02x", hash[i]); result += buf; }
return result;
}
std::string Base64Encode(const std::string& input) { BIO* bio = BIO_new(BIO_s_mem()); if (bio == nullptr) { std::cerr << "Failed to create bio for base64 encoding" << std::endl; return ""; }
BIO* base64Bio = BIO_push(BIO_new(BIO_f_base64()), bio); if (base64Bio == nullptr) { std::cerr << "BIO_push failed." << std::endl; BIO_free_all(bio); return ""; } // 禁用 Base64 编码中的换行符 BIO_set_flags(base64Bio, BIO_FLAGS_BASE64_NO_NL); // 将数据写入 BIO 进行编码 BIO_write(base64Bio, input.c_str(), static_cast<int>(input.length())); BIO_flush(base64Bio); // 获取编码后的数据 BUF_MEM* bioBuffer; BIO_get_mem_ptr(base64Bio, &bioBuffer); // 将编码数据拷贝到字符串中 std::string encodedData(bioBuffer->data, bioBuffer->length); // 释放 BIO 和其他资源 BIO_free_all(base64Bio); return encodedData;
std::string Base64Decode(const std::string& base64) { // 创建一个 BIO 对象,用于进行 Base64 解码 BIO* bio = BIO_new_mem_buf(base64.c_str(), static_cast(base64.length())); if (bio == nullptr) { std::cerr << "Failed to create BIO for base64 decoding" << std::endl; return ""; }
// 创建一个解码 BIO BIO* base64Bio = BIO_new(BIO_f_base64()); if (base64Bio == nullptr) { std::cerr << "Failed to create BIO for base64 decoding" << std::endl; BIO_free_all(bio); return ""; } // 禁用 Base64 解码中的换行符 BIO_set_flags(base64Bio, BIO_FLAGS_BASE64_NO_NL); // 将解码 BIO 与输入 BIO 关联 BIO_push(base64Bio, bio); // 计算解码后的数据长度 int length = BIO_pending(base64Bio); // 创建一个缓冲区来存储解码后的数据 std::string decodedData(length, '\0'); // 进行 Base64 解码 length = BIO_read(base64Bio, &decodedData[0], length); // 释放 BIO 和其他资源 BIO_free_all(base64Bio); // 调整解码后的数据长度 decodedData.resize(length); return decodedData;
bool RSAPSSVerify(const std::string& publicKey, const std::string& message, const std::string& signature) { BIO* publicKeyBio = BIO_new_mem_buf(publicKey.c_str(), publicKey.length()); if (publicKeyBio == nullptr) { std::cerr << "Failed to create bio from publickey string" << std::endl; return false; }
RSA* rsaPublicKey = PEM_read_bio_RSA_PUBKEY(publicKeyBio, nullptr, nullptr, nullptr); if (rsaPublicKey == nullptr) { std::cerr << "Failed to load public key from bio" << ERR_error_string(ERR_get_error(), nullptr) << std::endl; return false; } RSA_set_method(rsaPublicKey, RSA_PKCS1_OpenSSL()); auto size = RSA_size(rsaPublicKey); std::vector<uint8_t> decrypt (size, 0); int status = RSA_public_decrypt(signature.size(), reinterpret_cast<const unsigned char*>(signature.c_str()), decrypt.data(), rsaPublicKey, RSA_NO_PADDING); if (status == -1) { std::cerr << "Decript failed." << std::endl; return false; } std::string digest = SHA256_HASH(message); status = RSA_verify_PKCS1_PSS(rsaPublicKey, reinterpret_cast<const unsigned char* >(digest.c_str()), EVP_sha256(), decrypt.data(), -2); return status == 1;
bool RSAPSSSign(const std::string& privateKey, const std::string& message, std::string& signature) { BIO privateKeyBio = BIO_new_mem_buf(privateKey.c_str(), privateKey.length()); RSA rsaPrivateKey = PEM_read_bio_RSAPrivateKey(privateKeyBio, nullptr, nullptr, nullptr);
auto size = RSA_size(rsaPrivateKey); std::vector<uint8_t> EM (size, 0); std::string hash = SHA256_HASH(message); int status = RSA_padding_add_PKCS1_PSS(rsaPrivateKey, EM.data(), reinterpret_cast<const unsigned char *>(hash.c_str()), EVP_sha256(), -1 /* maximum salt length*/); if (status == -1) { std::cerr << "Add padding failed." << std::endl; RSA_free(rsaPrivateKey); BIO_free(privateKeyBio); return false; } signature.resize(size); status = RSA_private_encrypt(RSA_size(rsaPrivateKey), EM.data(), reinterpret_cast<unsigned char*>(signature.data()), rsaPrivateKey, RSA_NO_PADDING); if (status == -1) { std::cerr << "RSA_private_encrypt failed with error " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; RSA_free(rsaPrivateKey); BIO_free(privateKeyBio); return false; } RSA_free(rsaPrivateKey); BIO_free(privateKeyBio); return true;
bool GenerateRSAKeyPairs(uint16_t size, std::string& publicKey, std::string& privateKey) { RSA rsa = RSA_new(); BIGNUM e = BN_new(); if (!BN_set_word(e, RSA_F4)) { BN_free(e); RSA_free(rsa); return false; }
if (RSA_generate_key_ex(rsa, size, e, nullptr) != 1) { BN_free(e); RSA_free(rsa); return false; } BN_free(e); BIO* bio = BIO_new(BIO_s_mem()); if (!PEM_write_bio_RSAPrivateKey(bio, rsa, nullptr, nullptr, 0, nullptr, nullptr)) { BIO_free(bio); RSA_free(rsa); return false; } char* pri_data {nullptr}; auto pri_size = BIO_get_mem_data(bio, &pri_data); if (pri_size < 0) { BIO_free(bio); RSA_free(rsa); return false; } privateKey.assign(pri_data, pri_size); BIO_free(bio); bio = BIO_new(BIO_s_mem()); if (!PEM_write_bio_RSA_PUBKEY(bio, rsa)) { BIO_free(bio); RSA_free(rsa); return false; } char* pub_data {nullptr}; auto pub_size = BIO_get_mem_data(bio, &pub_data); publicKey.assign(pub_data, pub_size); BIO_free(bio); RSA_free(rsa); return true;
RSA签名
RSA签名算法的逻辑是,先对消息计算哈希值,再对哈希进行私钥加密(签名),生成数字签名,如图所示:
但是RSA有一个致命问题:选择密文攻击。因此,可以考虑在签名前对哈希值进行填充,已达到防止选择密文攻击的目的。目前常见的填充算法有:
RSA_PKCS1_PADDING_v1.5
、RSA_PKCS1_OAEP_PADDING
,RSASSA-PKCS1-v1.5
其实就是RSA_PKCS1_PADDING_v1.5
填充算法。RSASSA-PSS
采用RSASSA-PSS的签名算法,实际上就是在签名前使用RSA-PSS填充了哈希,然后再签名,过程如图所示:
其中EM的生成逻辑如下:
![image](https://github.com/robertwenhk/blog/assets/143626366/9d0a43e7-6eee-410c-9ac1-72fd30010fdd)
C++签名和验签示例:
ifndef RSASSA_PSS_H
define RSASSA_PSS_H
include
include
include
include <openssl/rsa.h>
include <openssl/pem.h>
include <openssl/err.h>
include <openssl/sha.h>
std::string SHA256_HASH(const std::string& input) { uint8_t hash[SHA256_DIGEST_LENGTH]; SHA256_CTX sha256; SHA256_Init(&sha256); SHA256_Update(&sha256, input.c_str(), input.length()); SHA256_Final(hash, &sha256); std::string result {""}; char buf[2]; for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { sprintf(buf, "%02x", hash[i]); result += buf; }
}
std::string Base64Encode(const std::string& input) { BIO* bio = BIO_new(BIO_s_mem()); if (bio == nullptr) { std::cerr << "Failed to create bio for base64 encoding" << std::endl; return ""; }
}
std::string Base64Decode(const std::string& base64) { // 创建一个 BIO 对象,用于进行 Base64 解码 BIO* bio = BIO_new_mem_buf(base64.c_str(), static_cast(base64.length()));
if (bio == nullptr) {
std::cerr << "Failed to create BIO for base64 decoding" << std::endl;
return "";
}
}
bool RSAPSSVerify(const std::string& publicKey, const std::string& message, const std::string& signature) { BIO* publicKeyBio = BIO_new_mem_buf(publicKey.c_str(), publicKey.length()); if (publicKeyBio == nullptr) { std::cerr << "Failed to create bio from publickey string" << std::endl; return false; }
}
bool RSAPSSSign(const std::string& privateKey, const std::string& message, std::string& signature) { BIO privateKeyBio = BIO_new_mem_buf(privateKey.c_str(), privateKey.length()); RSA rsaPrivateKey = PEM_read_bio_RSAPrivateKey(privateKeyBio, nullptr, nullptr, nullptr);
}
bool GenerateRSAKeyPairs(uint16_t size, std::string& publicKey, std::string& privateKey) { RSA rsa = RSA_new(); BIGNUM e = BN_new(); if (!BN_set_word(e, RSA_F4)) { BN_free(e); RSA_free(rsa); return false; }
}
endif //RSASSA_PSS_H