一、基本用法
// 生成密钥对
RSACrypto rsa;
rsa.generateRSAKeyPair(RSACrypto::BITS_2K);
// 公钥加密
RSACrypto rsa1("public.pem", RSACrypto::PUBLICKEY);
QByteArray cipher = rsa1.publicEncrypt("测试数据");
// 私钥解密
RSACrypto rsa2("private.pem", RSACrypto::PRIVATEKEY);
QByteArray plain = rsa2.privateDecrypt(cipher);
// 数字签名
QByteArray signature = rsa2.sign(plain);
// 签名验证
bool isValid = rsa1.verify(signature, plain);
必要头文件
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/bio.h>
二、生成密钥对
2.1、EVP_PKEY_CTX_new_id
用于创建密钥上下文的函数,基于给定的公钥算法标识符创建一个新的密钥上下文对象
EVP_PKEY_CTX *EVP_PKEY_CTX_new_id(int id, ENGINE *e);
参数解释:
- id : 公钥算法的标识符,用于指定要使用的加密算法,可以使用
EVP_PKEY_XXX
常量(例如EVP_PKEY_RSA
、EVP_PKEY_EC
)指定所需的算法。 - e:可选参数,指定要使用的加密引擎。如果为 NULL,则使用默认的加密引擎
返回值:
- 成功:返回指向新创建的
EVP_PKEY_CTX
对象的指针。 - 失败:返回 NULL。
// 创建密钥上下文对象
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
assert(ctx != NULL);
2.2、EVP_PKEY_keygen_init
用于初始化密钥对生成操作的函数,用于初始化设置密钥生成操作的参数和上下文ctx
int EVP_PKEY_keygen_init(EVP_PKEY_CTX *ctx);
参数解释:
ctx
:指向要初始化的密钥对上下文的指针。
返回值:
- 1:成功初始化密钥对生成操作。
- 0 或者 负数:初始化失败。
// 初始化ctx
int ret = EVP_PKEY_keygen_init(ctx);
assert(ret == 1);
2.3、EVP_PKEY_CTX_set_rsa_keygen_bits
用于设置生成的 RSA 密钥对的位数
int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX *ctx, int bits);
参数解释:
ctx
:指向要设置 RSA 密钥对位数的密钥对上下文的指针。bits
:要生成的 RSA 密钥对的位数。
返回值:
- 1:成功设置 RSA 密钥生成位数。
- 0: 或者 负数:设置失败。
// 密钥长度,单位为比特
enum KeyLength
{
BITS_1K = 1024,
BITS_2K = 2048,
BITS_3K = 3072,
BITS_4K = 4096
};
// 指定密钥对长度,bits是KeyLength类型
ret = EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits);
assert(ret == 1);
2.4、EVP_PKEY_generate
根据提供的密钥对上下文对象,生成一个新的密钥对,生成的密钥对保存在ppkey中
int EVP_PKEY_generate(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey);
参数解释:
ctx
:指向已初始化的密钥对上下文的指针。ppkey
:指向接收生成的密钥对的指针,生成的密钥对保存在ppkey中。
返回值:
- 1:成功生成密钥对。
- 0 或者 负数:生成失败。
EVP_PKEY* m_privateKey = NULL;
// 生成密钥对
ret = EVP_PKEY_generate(ctx, &m_privateKey);
assert(ret == 1);
注意:ppkey可以使用私钥,这是因为私钥包含公钥,而公钥只包含公钥。同时,如果在函数中使用new创建一个EVP_PKEY*对象,没有回收会导致内存泄露。
2.5、EVP_PKEY_CTX_free
释放密钥对上下文对象
void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
参数解释:
ctx
:指向要释放内存的密钥对上下文对象的指针。
// 释放上下文
EVP_PKEY_CTX_free(ctx);
2.6、BIO_new_file
用于创建适用于文件 I/O 的 BIO
对象,它允许将文件与 BIO
抽象接口结合使用,以便进行读取或写入文件的操作。
BIO *BIO_new_file(const char *filename, const char *mode);
参数解释:
filename
:要进行读取或写入的文件的名称或路径。mode
:表示文件操作模式的字符串。常见的模式包括:"r"
:只读模式。"w"
:写入模式,如果文件不存在则创建,如果文件存在则截断。"a"
:追加模式,在文件末尾写入数据。"rb"
:以二进制格式打开文件进行只读操作。"wb"
:以二进制格式打开文件进行写入操作。"ab"
:以二进制格式打开文件进行追加操作。
返回值:
- 成功:指向新创建的文件
BIO
对象的指针。 - 失败:返回
NULL
。
// 将私钥写入文件(私钥里面包含公钥),pri是私钥文件名
BIO* bio = BIO_new_file(pri.data(), "wb");
ret = PEM_write_bio_PrivateKey(bio, m_privateKey, NULL, NULL, 0, NULL, NULL);
assert(ret == 1);
BIO_flush(bio);
BIO_free(bio);
// 将公钥写入文件,pub是公钥文件名
bio = BIO_new_file(pub.data(), "wb");
ret = PEM_write_bio_PUBKEY(bio, m_privateKey);
assert(ret == 1);
BIO_flush(bio);
BIO_free(bio);
2.7、BIO_flush
刷新 BIO
对象,将内存中的数据写入磁盘中,跟fflush函数类似
int BIO_flush(BIO *bio);
参数解释:
bio
:指向要刷新的BIO
对象的指针。
返回值:
- 成功:返回 1。
- 失败:返回 0。
2.8、BIO_free
释放 BIO
对象
void BIO_free(BIO *bio);
参数解释:
bio
:指向要释放的BIO
对象的指针。
完整示例
void RSACrypto::generateRSAKeyPair(KeyLength bits, const QByteArray& pub, const QByteArray& pri)
{
// 创建密钥上下文对象
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
assert(ctx != NULL);
// 初始化ctx
int ret = EVP_PKEY_keygen_init(ctx);
assert(ret == 1);
// 指定密钥对长度
ret = EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits);
assert(ret == 1);
// 生成密钥对
ret = EVP_PKEY_generate(ctx, &m_privateKey);
assert(ret == 1);
// 释放上下文
EVP_PKEY_CTX_free(ctx);
// 将私钥写入文件(私钥里面包含公钥)
BIO* bio = BIO_new_file(pri.data(), "wb");
ret = PEM_write_bio_PrivateKey(bio, m_privateKey, NULL, NULL, 0, NULL, NULL);
assert(ret == 1);
BIO_flush(bio);
BIO_free(bio);
// 将公钥写入文件
bio = BIO_new_file(pub.data(), "wb");
ret = PEM_write_bio_PUBKEY(bio, m_privateKey);
assert(ret == 1);
BIO_flush(bio);
BIO_free(bio);
}