4.1使用公钥加密
4.1.1、EVP_PKEY_CTX_new
用于创建与给定密钥对象(EVP_PKEY
)相关联的密钥上下文(EVP_PKEY_CTX
)。
EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
参数解释:
pkey
:与上下文关联的密钥对象。这可以是一个公钥、私钥或对称密钥对象,具体取决于使用场景。e
:可选参数,与上下文关联的引擎(Engine)。如果不需要使用特定引擎,可以传入NULL
。
返回值
EVP_PKEY_CTX
类型的指针,即新创建的密钥上下文对象
// 创建加密数据的上下文对象
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(m_publicKey, NULL);
assert(ctx != NULL);
需要注意的是,使用完密钥上下文后应该调用 EVP_PKEY_CTX_free
函数来释放相应的资源,以避免内存泄漏。
4.1.2、EVP_PKEY_encrypt_init
用于初始化使用非对称密钥进行加密操作
int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *);
参数解释:
- ctx:要进行加密操作的密钥上下文
返回值
- 1:成功
- 0:失败
int ret = EVP_PKEY_encrypt_init(ctx);
assert(ret == 1);
4.1.3、EVP_PKEY_CTX_set_rsa_padding
用于设置 RSA 加密或解密操作的填充方式
int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad);
参数解释:
ctx
:RSA 加密或解密操作的上下文(EVP_PKEY_CTX
)。pad
:要设置的加密填充(padding)方式,可以是以下值之一:RSA_PKCS1_PADDING
:PKCS#1 填充方式,是最常见的 RSA 填充方式。RSA_PKCS1_OAEP_PADDING
:PKCS#1 OAEP 填充方式,带有随机性质的填充方式,安全性更高。RSA_NO_PADDING
:不进行填充操作,仅加密或解密数据。
设置签名时,不能使用RSA_PKCS1_OAPE_PADDING这种填充方式,因为OAEP 是一种概率性加密填充,每次加密同一明文会产生不同的密文,但签名需要确定性,这样才能保证相同数据的签名结果可以被验证
返回值
- 1:成功
- 0:失败
ret = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING);
assert(ret == 1);
4.1.4、EVP_PKEY_encrypt
使用非对称密钥进行加密操作
int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx,
unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen);
参数解释:
ctx
:指向 EVP_PKEY_CTX 对象的指针,用于进行加密操作的上下文。out
:指向输出缓冲区的指针,用于存储加密后的数据。outlen
:指向保存输出数据长度的变量的指针,同时也作为输入来指定输出缓冲区的大小。in
:指向输入缓冲区的指针,包含需要加密的数据。inlen
:输入数据的长度。
返回值
- 1:成功
- 0:失败
size_t outLen = 0;
ret = EVP_PKEY_encrypt(
ctx, NULL, &outLen, reinterpret_cast<const unsigned char*>(data.data()), data.size());
assert(ret == 1);
unsigned char* out = new unsigned char[outLen];
EVP_PKEY_encrypt(
ctx, out, &outLen, reinterpret_cast<const unsigned char*>(data.data()), data.size());
assert(ret == 1);
使用公钥加密数据,由于不知道加密数据的大小,所以第一次调用EVP_PKEY_encrypt的目的是获取outLen,这是因为outLen记录着加密后数据的长度,通过这个长度就能创建出合适的内存大小
4.1.5、EVP_PKEY_CTX_free
释放密钥对上下文对象
void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
参数解释:
ctx
:指向要释放内存的密钥对上下文对象的指针。
// 释放资源
delete[] out;
EVP_PKEY_CTX_free(ctx);
完整示例
QByteArray RSACrypto::publicEncrypt(const QByteArray& data)
{
// 创建加密数据的上下文对象
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(m_publicKey, NULL);
assert(ctx != NULL);
// 设置加密和填充模式
int ret = EVP_PKEY_encrypt_init(ctx);
assert(ret == 1);
ret = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
assert(ret == 1);
// 使用公钥加密数据,由于不知道加密数据的大小,所以第一次调用EVP_PKEY_encrypt的目的是获取outLen,
// 这是因为outLen记录着加密后数据的长度,通过这个长度就能创建出合适的内存大小
size_t outLen = 0;
ret = EVP_PKEY_encrypt(
ctx, NULL, &outLen, reinterpret_cast<const unsigned char*>(data.data()), data.size());
assert(ret == 1);
unsigned char* out = new unsigned char[outLen];
EVP_PKEY_encrypt(
ctx, out, &outLen, reinterpret_cast<const unsigned char*>(data.data()), data.size());
assert(ret == 1);
QByteArray encryptData(reinterpret_cast<char*>(out), outLen);
// 释放资源
delete[] out;
EVP_PKEY_CTX_free(ctx);
return encryptData;
}
4.2 使用私钥解密
4.2.1、EVP_PKEY_CTX_new
用于创建与给定密钥对象(EVP_PKEY
)相关联的密钥上下文(EVP_PKEY_CTX
)。
EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
参数解释:
pkey
:与上下文关联的密钥对象。这可以是一个公钥、私钥或对称密钥对象,具体取决于使用场景。e
:可选参数,与上下文关联的引擎(Engine)。如果不需要使用特定引擎,可以传入NULL
。
返回值
EVP_PKEY_CTX
类型的指针,即新创建的密钥上下文对象
// 创建加密数据的上下文对象
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(m_privateKey, NULL);
assert(ctx != NULL);
需要注意的是,使用完密钥上下文后应该调用 EVP_PKEY_CTX_free
函数来释放相应的资源,以避免内存泄漏。
4.2.2、EVP_PKEY_decrypt_init
用于初始化使用非对称密钥进行解密操作
int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *);
参数解释:
- ctx:要进行加密操作的密钥上下文
返回值
- 1:成功
- 0:失败
int ret = EVP_PKEY_decrypt_init(ctx);
assert(ret == 1);
4.2.3、EVP_PKEY_CTX_set_rsa_padding
用于设置 RSA 加密或解密操作的填充方式
int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad);
参数解释:
ctx
:RSA 加密或解密操作的上下文(EVP_PKEY_CTX
)。pad
:要设置的加密填充(padding)方式,可以是以下值之一:RSA_PKCS1_PADDING
:PKCS#1 填充方式,是最常见的 RSA 填充方式。RSA_PKCS1_OAEP_PADDING
:PKCS#1 OAEP 填充方式,带有随机性质的填充方式,安全性更高。RSA_NO_PADDING
:不进行填充操作,仅加密或解密数据。
设置签名时,不能使用RSA_PKCS1_OAPE_PADDING这种填充方式,因为OAEP 是一种概率性加密填充,每次加密同一明文会产生不同的密文,但签名需要确定性,这样才能保证相同数据的签名结果可以被验证
返回值
- 1:成功
- 0:失败
ret = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING);
assert(ret == 1);
4.2.4、EVP_PKEY_decrypt
使用非对称密钥进行加密操作
int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx,
unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen);
参数解释:
ctx
:指向 EVP_PKEY_CTX 对象的指针,用于进行解密操作的上下文。out
:指向输出缓冲区的指针,用于存储解密后的数据。outlen
:指向保存输出数据长度的变量的指针,同时也作为输入来指定输出缓冲区的大小。in
:指向输入缓冲区的指针,包含需要解密的数据。inlen
:输入数据的长度。
返回值
- 1:成功
- 0:失败
size_t outLen = 0;
ret = EVP_PKEY_decrypt(
ctx, NULL, &outLen, reinterpret_cast<const unsigned char*>(data.data()), data.size());
assert(ret == 1);
unsigned char* out = new unsigned char[outLen];
EVP_PKEY_decrypt(
ctx, out, &outLen, reinterpret_cast<const unsigned char*>(data.data()), data.size());
assert(ret == 1);
使用私钥解密数据,由于不知道解密数据的大小,所以第一次调用EVP_PKEY_decrypt的目的是获取outLen,这是因为outLen记录着解密后数据的长度,通过这个长度就能创建出合适的内存大小
4.2.5、EVP_PKEY_CTX_free
释放密钥对上下文对象
void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
参数解释:
ctx
:指向要释放内存的密钥对上下文对象的指针。
// 释放资源
delete[] out;
EVP_PKEY_CTX_free(ctx);
完整示例
QByteArray RSACrypto::privateDecrypt(const QByteArray& data)
{
// 创建解密数据的上下文对象
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(m_privateKey, NULL);
assert(ctx != NULL);
// 设置加密和填充模式
int ret = EVP_PKEY_decrypt_init(ctx);
assert(ret == 1);
ret = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
assert(ret == 1);
// 使用私钥解密
size_t outLen = 0;
ret = EVP_PKEY_decrypt(
ctx, NULL, &outLen, reinterpret_cast<const unsigned char*>(data.data()), data.size());
assert(ret == 1);
unsigned char* out = new unsigned char[outLen];
ret = EVP_PKEY_decrypt(
ctx, out, &outLen, reinterpret_cast<const unsigned char*>(data.data()), data.size());
assert(ret == 1);
QByteArray decryptData(reinterpret_cast<char*>(out), outLen);
// 释放资源
delete[] out;
EVP_PKEY_CTX_free(ctx);
return decryptData;
}