使用 openssl 进行 RSA 加解密(C++)
openssl
传输层安全性/安全套接层及其加密库
项目地址:https://gitcode.com/gh_mirrors/ope/openssl
免费下载资源
·
一. 生成密钥对
在 OPENSSL 中, RSA
是一个很重要的结构体。它的定义在 rsa_locl.h
中,面包含了在原理中提到的所有重要的变量 随机质数 p
, q
, 公钥指数 e
, 私钥指数 d
, 以及模数 n
struct rsa_st {
// ...
BIGNUM *n;
BIGNUM *e;
BIGNUM *d;
BIGNUM *p;
BIGNUM *q;
// ...
};
生成密钥函数:
int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
bits
密钥的规模(modulus
)。小于 1028 位的密钥是不安全的,小于 512 则会返回 0e
公开的指数。它应该是一个奇数(odd number), 一般是3, 17
或65537
cb
生成大随机数的回调函数。一般使用 NULL 即可, 默认为BN_GENCB_call()
生成公私钥并写入文件的代码示例:
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
#include <openssl/applink.c> //important!
void gen_rsa_key() {
RSA *rsa = RSA_new();
BIGNUM *e = BN_new();
BN_set_word(e, 17);
BN_GENCB* gencb = NULL;
EVP_PKEY* pkey = EVP_PKEY_new();
int rst = RSA_generate_key_ex(rsa, 3072, e, gencb);
rst = EVP_PKEY_set1_RSA(pkey, rsa);
FILE* f_pri = fopen("pri.key", "wb");
FILE* f_pub = fopen("pub.pem", "wb");
rst = PEM_write_RSAPublicKey(f_pub, rsa);
rst = PEM_write_RSAPrivateKey(f_pri, rsa, 0, 0, 0, 0, 0);
fclose(f_pri);
fclose(f_pub);
f_pri = NULL;
f_pub = NULL;
RSA_free(rsa);
BN_free(e);
EVP_PKEY_free(pkey);
BN_GENCB_free(gencb);
}
二. 公私钥加解密
公私钥加解密两个步骤, 一是载入密钥, 二是加解密。
载入密钥使用 API :
PEM_read_RSAPublicKey()
PEM_read_RSAPrivateKey()
PEM_read_bio_RSAPrivateKey()
PEM_read_bio_RSAPublicKey()
当载入失败时返回 NULL。
而加解密中两个重要的 API 是:
int RSA_public_encrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding);
int RSA_private_decrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding);
flen
是输入数据的长度,必须小于等于modulus = RSA_size(rsa)
个字节, 且和参数padding
相关。当 flen 不满足要求时,将会出现"data too small for key size"
或"data too large for key size"
错误。 flen 与 padding 的对应关系如下:RSA_PKCS1_PADDING
, flen <= modulus – 11RSA_SSLV23_PADDING
, flen <= modulus – 11RSA_NO_PADDING
, flen = modulusRSA_PKCS1_OAEP_PADDING
, flen <= modulus – 41 (2 倍的 SHA1 长度 + 1)
padding
是填充模式,当 flen 满足上述关系时,将会进行填充:RSA_PKCS1_PADDING
, 最常用的模式,使用 PKCS #1 v1.5 标准,前两字节填充0x00, 0x02
,接着的modulus - flen - 3
字节使用随机非 '\0'
值填充,接着填充一个字节的0x00
, 然后是from
数据RSA_SSLV23_PADDING
, 前两个字节填充0x00,0x02
, 接着的modulus - flen - 3 - 8
字节填充随机非 '\0'
值,然后填充 8 个0x03
, 接着填充一个字节的0x00
,然后是from
数据RSA_NO_PADDING
, 不需要填充RSA_PKCS1_OAEP_PADDING
, 前20字节使用 SHA1 填充, 接着填充至少 20 字节的0x00
, 最后填充0x01
,接着是from
数据
如图所示:
公私钥加解密示例:
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
#include <openssl/applink.c> //important!
const char* pri_key_path = "pri.key";
const char* pub_key_path = "pub.pem";
const char *plain_text = "The EVP interface supports the ability to perform authenticated encryption and decryption, as well as the option to attach unencrypted,"
" associated data to the message. Such Authenticated-Encryption with Associated-Data (AEAD) schemes provide confidentiality by encrypting the data, and also"
" provide authenticity assurances by creating a MAC tag over the encrypted data. The MAC tag will ensure the data is not accidentally altered or maliciously"
" tampered during transmission and storage.";
bool pub_encrypt(const char* pub_key_path, const unsigned char* in, int in_len, unsigned char*& out, int& out_len) {
bool ret = false;
RSA* rsa = RSA_new();
FILE* f = fopen(pub_key_path, "r");
if (!f) goto ERR;
rsa = PEM_read_RSAPublicKey(f, &rsa, 0, 0);
if (!rsa) goto ERR;
fclose(f);
f = NULL;
out_len = RSA_size(rsa);
if (in_len > out_len) goto ERR;
out = (unsigned char*)malloc(out_len);
int padding = RSA_PKCS1_PADDING;
int rst = RSA_public_encrypt(in_len, in, out, rsa, padding);
if (rst <= 0) {
free(out);
out = NULL;
printf("%s\n", ERR_reason_error_string(ERR_get_error()));
goto ERR;
}
ret = true;
ERR:
if(rsa)
RSA_free(rsa);
return ret;
}
bool pri_decrypt(const char* pri_key_path, const unsigned char* in, int in_len, unsigned char*& out, int& out_len) {
bool ret = false;
RSA* rsa = RSA_new();
FILE* f = fopen(pri_key_path, "r");
if (!f) goto ERR;
rsa = PEM_read_RSAPrivateKey(f, &rsa, 0, 0);
if (!rsa) goto ERR;
fclose(f);
f = NULL;
out_len = RSA_size(rsa);
if (in_len > out_len) goto ERR;
out = (unsigned char*)malloc(out_len);
int padding = RSA_PKCS1_PADDING;
int rst = RSA_private_decrypt(in_len, in, out, rsa, padding);
if (rst < 0) {
free(out);
out = NULL;
printf("%s", ERR_reason_error_string(ERR_get_error()));
goto ERR;
}
out_len = rst;
ret = true;
ERR:
if (rsa)
RSA_free(rsa);
return ret;
}
int main() {
//init_openssl();
unsigned char* cipher_text = NULL, *re_plain_text = NULL;
int enc_out_len = 0, dec_out_len;
bool rst = pub_encrypt(pub_key_path, reinterpret_cast<const unsigned char*>(plain_text), strlen(plain_text), cipher_text, enc_out_len);
assert(rst);
rst = pri_decrypt(pri_key_path, cipher_text, enc_out_len, re_plain_text, dec_out_len);
assert(rst);
assert(strlen(plain_text) == dec_out_len);
int cmp = memcmp(plain_text, re_plain_text, dec_out_len);
assert(cmp == 0);
free(cipher_text);
free(re_plain_text);
//clean_openssl();
return 0;
}
三. Tips
1. 在 Windows 下运行出现崩溃 "no OPENSSL_Applink"
多半是因为代码运行库不匹配导致的。在Windows 中,运行时库有这几种:
- Single Threaded
/ML
– MS VC++ often defaults to this for the release version of a new project. - Debug Single Threaded
/MLd
– MS VC++ often defaults to this for the debug version of a new project. - Multithreaded
/MT
- Debug Multithreaded
/MTd
- Multithreaded DLL
/MD
– OpenSSL defaults to this. - Debug Multithreaded DLL
/MDd
运行库不一致可能导致程序崩溃(通常是在进行IO操作时)。这种情况下可以重新编译 OPENSSL, 或者修改程序的运行库。更简单的办法,可以尝试添加一个文件引用:
#include <openssl/applink.c>
GitHub 加速计划 / ope / openssl
25.13 K
9.99 K
下载
传输层安全性/安全套接层及其加密库
最近提交(Master分支:1 个月前 )
fd39d1c8
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/25095)
2 个月前
ae87c488
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/25095)
2 个月前
更多推荐
已为社区贡献7条内容
所有评论(0)