使用 openssl AES RSA 对文件以及数据进行加解密

时间:2020-12-10 18:29:03

本文讲述以下内容,没有过多的详细讲述,只是基于 openssl 库的使用,以及将其

封装成一个实现各种方便使用的类,支持从内存中读取 RSA 密钥.。

说明:

一、简单说明

1. 通常 AES 与 RSA 结合使用,比如使用 AES 对文件

加密,再用 RSA 对 AES 密钥进行加密等,本文不讨论用法。

2. 这里编译进可执行文件里的密钥,只是少了密钥文件,并没有提高安全性,

因为密钥可直接从可执行文件中读取出来,实际使用中,可自行编写加密算法对

密钥进行加密,然后编译进可执行文件,然后运行过程中对密钥解密使用。(个人想法,欢迎讨论 ^_^)

3. 当前代码使用 openssl 1.01e ,CentOS6.5测试,

二、主要实现的功能:

对称加密:

1.  使用AES对信息串加密。

2.  使用AES对信息串解密。

3.  使用AES对文件加密。

4. 使用 AES 对文件解密。

非对称加密:

1. RSA 公钥(文件中)加密信息串

2. RSA 私钥(文件中)解密信息串

3. RSA 私钥(文件中)加密信息串

4. RSA 公钥(文件中)解密信息串

5. RSA 私钥(内存中)加密

6. RSA 公钥(内存中)解密


小二,上代码:

密钥编辑过程中可能会有错误,可自己生成,
密钥生成(命令行中)
openssl genrsa -out private.key 2048

openssl rsa -in private.key -pubout -out public.key
然后按下面代码格式写入头文件

privatekey.h

#ifndef PRIVATEKEY_H

#define PRIVATEKEY_H

/*!

@file
@brief 私钥文件

@note private.key 放在头文件按需包含 可避免把私钥编译进可执行文件的数据段

*/

namespace GoKey{

const static char prk[] =

"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEpAIBAAKCAQEAvBjZBE5H8LVuIzHT1kDk48TjgZUrHzbRN3tjkel+OaftEnOt\n"
"T26DsYGrTzKg98zkKNg0Igd0bta/VHP1VnnmPnw4CsKOAFmqWzXcYpr80z79iogY\n"
"kH9HIfH4Hz2px6YmXLVoGrIO/LF+ZbtNhQhIy5hUl6z+OA3CjVBNFx4o5T7oc3jl\n"
"049wG2zpKMmH3mxqe+SrFlh++tQp6G8o9Ka+PCUwaB0SK7Q+fONzrGWdRhmyI2UQ\n"
"PHeIaP6YrqpywS3U39hF5U5ewahyUYgNHe6CxvCRlUxzeMLfFwtXBbwbaRNxvsDb\n"
"zuDNma6NFpy2XPNETxEU0x6HD9uw2M3G52lnXQIDAQABAoIBAQCNlCJdJFLv8pAY\n"
"+5tYjHX8nykjU1Um5IrruPtES7zPxv8hwWI1BJkpldGNBi2090rRF5N9/aB9ATT4\n"
"a2PCGHTemX9RUgmyZ8tbZhohOkmM87/BcxkSQksXWNsLIM3XUc9UPtNCLL4tI6hh\n"
"CG+b75VjcaBSnkqnhGvpFAVCnlfn3Hnr5YMVIuEY06PUu9GstaFJ167AwGKNzWyB\n"
"PknoxEWs29+xmCo2TxLK18m7mRRcm141Mo1MBCrYunntqG2u3LK/K8uSPHyz4QTJ\n"
"FHTXKr8mN8ZmB5cLwte3WBurXv4R5n9QHrBVYxKsZxicP/+ovF13wxhs4f0tHxog\n"
"bACRmY49AoGBAPllmUbI4E/VVPjgE9+kk5Lf2j7tBNcBeek6FzAeEviGNJ9aoBw1\n"
"03xwwegwFdizrhXZNzjSquLB8rcuB0G992XuZ6MiMUtFxRL7Xy8jmFghmzLZ6zBY\n"
"HC/TpswDOsV6Ye3ils+5VJftEomPTwCXakZ/jfC/G59xjxB6beEFnRUzAoGBAMET\n"
"wvLV59UFMXPi4/z4WiW+DHGBJ9agwp65bLLcWOC/ga6JrqhnaQxFJt8kAsD01pND\n"
"FLSXzvJhyKKebesAsui2sL1X5oCK3Jtaswa38pYGY/8XzrzFYE+AwYqXla/qjJRK\n"
"2/9ucbdZw1ySFvRe2PUnUsXAVNQEOPYqlM5z7XEvAoGAMgmlaCNThhD6XHiw5BUk\n"
"GrrZEfkCGJVOPXIPRvMxwTVEB5jqON6/0N7auNg0+eaLDg0n3DnAWYCqTwBoKlOP\n"
"+J6NophwpXaZJMbUgSO2JDCR7/DzFB8JgLsJYzNerlJwpYbp1UGM7HJ/XqQnKKo4\n"
"g26CLwRY6XOiTZ8SvqweqSsCgYEApIVWRN4Iofe+3+SiKNjm1+pNaBqJ+9UYgW+3\n"
"M+GWykvlEPi5Hz6ewJncOZ/RyOHYRbuPVSNWcCrqb8dYGjuKN8oUaAMSos2wlaB/\n"
"lGYCC9x+MMv0md4F5zjrslDgd/oAuAvh0pIly/ptAeBqNujrLlrHqUHuBotijkBS\n"
"dGlMFnkCgYBmQUbAZj57oV5QcDxzBwnDPGX/4kNwux0pHt2Czl6Gcma7cz0Ff31v\n"
"hG2WuxIVVdma6HLVBhK8tfOE6fdufRhLsnqIQpx4E6FHlu2e4+qhRhmihW0zmHaJ\n"
"uxDqUSdwTOQktubPfvYctqkmdk5ajiYWShwTaPw7i/byZIWTd2uHKA==\n"
"-----END RSA PRIVATE KEY-----";

}

#endif // PRIVATEKEY_H


 

publickey.h

/*!
@file
@brief 公钥文件
*/

#ifndef PUBLICKEY_H
#define PUBLICKEY_H
namespace GoKey{
/** public.key*/
const static char pbk[] =
"-----BEGIN PUBLIC KEY-----\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvBjZBE5H8LVuIzHT1kDk\n"
"48TjgZUrHzbRN3tjkel+OaftEnOtT26DsYGrTzKg98zkKNg0Igd0bta/VHP1Vnnm\n"
"Pnw4CsKOAFmqWzXcYpr80z79iogYkH9HIfH4Hz2px6YmXLVoGrIO/LF+ZbtNhQhI\n"
"y5hUl6z+OA3CjVBNFx4o5T7oc3jl049wG2zpKMmH3mxqe+SrFlh++tQp6G8o9Ka+\n"
"PCUwaB0SK7Q+fONzrGWdRhmyI2UQPHeIaP6YrqpywS3U39hF5U5ewahyUYgNHe6C\n"
"xvCRlUxzeMLfFwtXBbwbaRNxvsDbzuDNma6NFpy2XPNETxEU0x6HD9uw2M3G52ln\n"
"XQIDAQAB\n"
"-----END PUBLIC KEY-----";
}
#endif // PUBLICKEY_H

copensslcrypto.h

#ifndef COPENSSLCRYPTO_H
#define COPENSSLCRYPTO_H

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/aes.h>
#include <openssl/crypto.h>
#include <openssl/rsa.h>
#include<openssl/pem.h>
#include<openssl/err.h>
#include <iostream>
#include <assert.h>

/*!
@class
@note COpensslCrypto 实现简单封装openssl 的rsa 加密 aes 加密.
RSA的API中当使用参数RSA_PKCS1_PADDING时,明文长度不能大于密文长度-11;
当使用参数RSA_NO_PADDING时,明文长度需要正好是128/256
@note AES加密,块大小必须为128位(16字节),如果不是,则要补齐,
密钥长度可以选择128位、192位、256位,所以密码长度必须是16,24,32字节
@todo 加入 aes 文件加解密操作
*/
class COpensslCrypto
{
enum AES_BIT
{
AES_BIT128 = 128,
AES_BIT192 = 192,
AES_BIT256 = 256
};
public:
COpensslCrypto();
/** @brief rsa 公钥加密*/
static std::string RSAPubEncode( const std::string& strPemFileName, const std::string& strData );
/** @brief rsa 私钥解密*/
static std::string RSAPriDecode( const std::string& strPemFileName, const std::string& strData );
/** @brief rsa 私钥加密*/
static std::string RSAPriEncode( const std::string& strPemFileName, const std::string& strData );
/** @brief rsa 公钥解密*/
static std::string RSAPubDecode( const std::string& strPemFileName, const std::string& strData );
/** @brief rsa 私钥加密,如果bp为空,则使用默认bp读取rsa密钥*/
static std::string RSAPriEncodeBIO(const std::string& strData,const char *prk, BIO *bp = NULL);
/** @brief rsa bio 公钥解密,如果bp为空,则使用默认bp读取rsa密钥*/
static std::string RSAPubDecodeBIO(const std::string& strData,const char *pbk, BIO *bp = NULL);

/** @brief aes加密*/
static std::string AESEncode( const std::string& password, const std::string& data ,AES_BIT bitCount = AES_BIT256);
/** @brief aes解密*/
static std::string AESDecode( const std::string& strPassword, const std::string& strData ,AES_BIT bitCount = AES_BIT256);
/** @brief aes文件加密*/
static bool AESEncodeFile(const std::string &infile, const std::string &outfile, const std::string &password);
/** @brief aes文件解密*/
static bool AESDecodeFile(const std::string &infile, const std::string &outfile, const std::string &password);

};

#endif // COPENSSLCRYPTO_H

copensslcrypto.cpp

#include "copensslcrypto.h"
#include "openssl/err.h"

inline size_t getFileLength(FILE *fp);

COpensslCrypto::COpensslCrypto()
{
}

/**
* @param strPemFileName RSA公钥文件名.
* @param strData 你要加密的数据.
* @return 返回加密后的数据,出错返回空串
* @brief 使用strPemFileName 公钥加密strData返回.
* @see RSAPriDecode().
*/
std::string COpensslCrypto::RSAPubEncode( const std::string& strPemFileName, const std::string& strData )
{
if (strPemFileName.empty() || strData.empty()){
return "";
}
FILE* hPubKeyFile = NULL;
if(!(hPubKeyFile = fopen(strPemFileName.c_str(), "rb"))){
perror("open");
return "";
}
std::string strRet;
RSA* pRSAPublicKey = RSA_new();
if(PEM_read_RSA_PUBKEY(hPubKeyFile, &pRSAPublicKey, 0, 0) == NULL){
RSA_free(pRSAPublicKey);
return "";
}

int nLen = RSA_size(pRSAPublicKey);
char* pEncode = new char[nLen + 1];
int ret = RSA_public_encrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pEncode, pRSAPublicKey, RSA_PKCS1_PADDING);
if (ret >= 0){
strRet = std::string(pEncode, ret);
}
delete[] pEncode;
RSA_free(pRSAPublicKey);
fclose(hPubKeyFile);
CRYPTO_cleanup_all_ex_data();
return strRet;
}

/**
* @param strPemFileName RSA私钥文件名.
* @param strData 你要解密的数据.
* @return 返回解密后的数据,出错返回空串
* @brief 使用strPemFileName 私钥解密strData返回.
* @see RSAPubEncode().
*/
std::string COpensslCrypto::RSAPriDecode( const std::string& strPemFileName, const std::string& strData )
{
if (strPemFileName.empty() || strData.empty()){
return "";
}
FILE* hPriKeyFile = NULL;
if(!(hPriKeyFile = fopen(strPemFileName.c_str(),"rb"))){
return "";
}
std::string strRet;
RSA* pRSAPriKey = RSA_new();
if(PEM_read_RSAPrivateKey(hPriKeyFile, &pRSAPriKey, 0, 0) == NULL){
RSA_free(pRSAPriKey);
return "";
}
int nLen = RSA_size(pRSAPriKey);
char* pDecode = new char[nLen+1];

int ret = RSA_private_decrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pDecode, pRSAPriKey, RSA_PKCS1_PADDING);
if(ret >= 0){
strRet = std::string((char*)pDecode, ret);
}
delete [] pDecode;
RSA_free(pRSAPriKey);
fclose(hPriKeyFile);
CRYPTO_cleanup_all_ex_data();
return strRet;
}

/**
* @param strPemFileName RSA私钥文件名.
* @param strData 你要加密的数据.
* @return 返回加密后的数据,出错返回空串
* @brief 使用strPemFileName 私钥加密strData返回.
* @see RSAPriDecode().
*/
std::string COpensslCrypto::RSAPriEncode( const std::string& strPemFileName, const std::string& strData )
{
if (strPemFileName.empty() || strData.empty()){
return "";
}
FILE* hPriKeyFile = NULL;
if(!(hPriKeyFile = fopen(strPemFileName.c_str(), "rb"))){
perror("open");
return "";
}
std::string strRet;
RSA* pRSAPrivateKey = RSA_new();
if(PEM_read_RSAPrivateKey(hPriKeyFile, &pRSAPrivateKey, 0, 0) == NULL){
RSA_free(pRSAPrivateKey);
return "";
}

int nLen = RSA_size(pRSAPrivateKey);
char* pEncode = new char[nLen + 1];
int ret = RSA_private_encrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pEncode, pRSAPrivateKey, RSA_PKCS1_PADDING);
if (ret >= 0){
strRet = std::string(pEncode, ret);
}
delete[] pEncode;
RSA_free(pRSAPrivateKey);
fclose(hPriKeyFile);
CRYPTO_cleanup_all_ex_data();
return strRet;
}

/**
* @param strPemFileName RSA公钥文件名.
* @param strData 你要解密的数据.
* @return 返回解密后的数据,出错返回空串
* @brief 使用strPemFileName 公钥解密strData返回.
* @see RSAPriEncode().
*/
std::string COpensslCrypto::RSAPubDecode( const std::string& strPemFileName, const std::string& strData )
{
if (strPemFileName.empty() || strData.empty()){
return "";
}
FILE* hPubKeyFile = NULL;
if(!(hPubKeyFile = fopen(strPemFileName.c_str(),"rb"))){
return "";
}
std::string strRet;
RSA* pRSAPubKey = RSA_new();
if(PEM_read_RSA_PUBKEY(hPubKeyFile, &pRSAPubKey, 0, 0) == NULL){
RSA_free(pRSAPubKey);
return "";
}
int nLen = RSA_size(pRSAPubKey);
char* pDecode = new char[nLen+1];

int ret = RSA_public_decrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pDecode, pRSAPubKey, RSA_PKCS1_PADDING);
if(ret >= 0){
strRet = std::string((char*)pDecode, ret);
}
delete [] pDecode;
RSA_free(pRSAPubKey);
fclose(hPubKeyFile);
CRYPTO_cleanup_all_ex_data();
return strRet;
}


/**
* @param strData 要加密的数据.
* @param prk 密钥串空间地址.
* @param bp 已经加载好私钥的bp指针,可以为空.
* @return 返回加密后的数据,出错返回空串
* @brief 如果bp为空,则使用默认bp读取rsa私钥,如果
* bp 不为空,prk参数将不使用.
* @see RSAPubDecodeBIO().
*/
std::string COpensslCrypto::RSAPriEncodeBIO(const std::string &strData, const char *prk, BIO *bp)
{
if (strData.empty()){
return "";
}

bool bpflag = false;
if(bp == NULL){
bp = BIO_new_mem_buf(const_cast<char *>(prk),-1);
if(bp == NULL){
return "";
}
bpflag = true;
}

std::string strRet;
RSA* pRSAPrivateKey = RSA_new();
if(PEM_read_bio_RSAPrivateKey(bp, &pRSAPrivateKey, 0, 0) == NULL){
RSA_free(pRSAPrivateKey);
if(bpflag){
BIO_free_all(bp);
}
return "";
}

int nLen = RSA_size(pRSAPrivateKey);
char* pEncode = new char[nLen + 1];
int ret = RSA_private_encrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pEncode, pRSAPrivateKey, RSA_PKCS1_PADDING);
if (ret >= 0){
strRet = std::string(pEncode, ret);
}
delete[] pEncode;
RSA_free(pRSAPrivateKey);
if(bpflag){
BIO_free_all(bp);
}
CRYPTO_cleanup_all_ex_data();
return strRet;
}

/**
* @param strData 要解密的数据.
* @param pbk 密钥串空间地址.
* @param bp 已经加载好公钥的bp指针,可以为空.
* @return 返回解密后的数据,出错返回空串
* @brief 如果bp为空,则使用默认bp读取rsa公钥,如果
* bp 不为空,pbk参数将不使用.
* @see RSAPubEncodeBIO().
*/
std::string COpensslCrypto::RSAPubDecodeBIO(const std::string &strData, const char *pbk, BIO *bp)
{
if (strData.empty()){
return "";
}
bool bpflag = false;
if(bp == NULL){
bp = BIO_new_mem_buf(const_cast<char *>(pbk),-1);
if(bp == NULL){
return "";
}
bpflag = true;
}

std::string strRet;
RSA* pRSAPubKey = RSA_new();
if(PEM_read_bio_RSA_PUBKEY(bp, &pRSAPubKey, 0, 0) == NULL){
if(bpflag){
BIO_free_all(bp);
}
RSA_free(pRSAPubKey);
return "";
}
int nLen = RSA_size(pRSAPubKey);
char* pDecode = new char[nLen+1];

int ret = RSA_public_decrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pDecode, pRSAPubKey, RSA_PKCS1_PADDING);
if(ret >= 0){
strRet = std::string((char*)pDecode, ret);
}

delete [] pDecode;
RSA_free(pRSAPubKey);
CRYPTO_cleanup_all_ex_data();
if(bpflag){
BIO_free_all(bp);
}
return strRet;
}

/**
* @param password AES明文密码.
* @param data 要加密的数据.
* @param bitCount AES密钥长度,默认是 AES_BIT256.
* @return 返回加密后的数据,出错返回空串
* @brief
* @see AESDecode().
*/
std::string COpensslCrypto::AESEncode(const std::string &password, const std::string &data, AES_BIT bitCount)
{
AES_KEY aes_key;
if(AES_set_encrypt_key((const unsigned char*)password.c_str(), bitCount, &aes_key) < 0){
return "";
}
std::string strRet;
std::string data_bak = data;
unsigned int data_length = data_bak.length();
int padding = 0;
if (data_bak.length() % AES_BLOCK_SIZE > 0){
padding = AES_BLOCK_SIZE - data_bak.length() % AES_BLOCK_SIZE;
}
data_length += padding;
while (padding > 0){
data_bak += '\0';
padding--;
}
for(unsigned int i = 0; i < data_length/AES_BLOCK_SIZE; i++){
std::string str16 = data_bak.substr(i*AES_BLOCK_SIZE, AES_BLOCK_SIZE);
unsigned char out[AES_BLOCK_SIZE];
::memset(out, 0, AES_BLOCK_SIZE);
AES_encrypt((const unsigned char*)str16.c_str(), out, &aes_key);
strRet += std::string((const char*)out, AES_BLOCK_SIZE);
}
return strRet;
}

/**
* @param strPassword AES明文密码.
* @param strData 要加密的数据.
* @param bitCount AES密钥长度,默认是 AES_BIT256.
* @return 返回解密后的数据,出错返回空串
* @brief
* @see AESEncode().
*/
std::string COpensslCrypto::AESDecode(const std::string &strPassword, const std::string &strData, AES_BIT bitCount)
{
AES_KEY aes_key;
if(AES_set_decrypt_key((const unsigned char*)strPassword.c_str(), bitCount, &aes_key) < 0){
return "";
}
std::string strRet;
for(unsigned int i = 0; i < strData.length()/AES_BLOCK_SIZE; i++){
std::string str16 = strData.substr(i*AES_BLOCK_SIZE, AES_BLOCK_SIZE);
unsigned char out[AES_BLOCK_SIZE];
::memset(out, 0, AES_BLOCK_SIZE);
AES_decrypt((const unsigned char*)str16.c_str(), out, &aes_key);
strRet += std::string((const char*)out, AES_BLOCK_SIZE);
}
return strRet;
}


/**
* @param infile 要加密的文件.
* @param outfile 输出文件.
* @param password AES明文密钥.
* @return 成功返回 true;
* @brief 重复调用AESEncode()进行文件加密.
* @see AESEncode().
* @see AESDecodeFile().
*/
bool COpensslCrypto::AESEncodeFile(const std::string &infile, const std::string &outfile, const std::string &password)
{
std::string pwd = password;
pwd.resize(32,'a');

FILE *fpInfile = fopen(infile.c_str(),"r");
FILE *fpOutFile = fopen(outfile.c_str(),"w+");
if(!fpInfile || !fpOutFile){
if(fpInfile)
fclose(fpInfile);
if(fpOutFile)
fclose(fpOutFile);
return false;
}
char buff[AES_BLOCK_SIZE];
memset(buff,0,sizeof(buff));
std::string encode_data;
size_t readSize = 0;
while((readSize = fread(buff,1,AES_BLOCK_SIZE,fpInfile)) > (size_t)0 && readSize != (size_t)EOF){
encode_data = AESEncode(pwd,std::string(buff,readSize));
fwrite(encode_data.data(),encode_data.length(),1,fpOutFile);
memset(buff,0,sizeof(buff));
}
fclose(fpInfile);
fclose(fpOutFile);
return true;
}

/**
* @param infile 要解密的文件.
* @param outfile 输出文件.
* @param password AES明文密钥.
* @return 成功返回 true;
* @brief 重复调用AESDecode()进行文件加密.
* @see AESDecode().
* @see AESEncodeFile().
* @bug (已解决,但引入新bug)如果输入文件最后几个字节(少于AES_BLOCK_SIZE)有 '\0'可能会附着在解密
* 后的文件里,特别是tar.gz 后面倒数几个字节有 \0 020 216 002 (acii)
* @bug (新 bug) 如果待加密文件最后有几个'\0',可能解密出来就没有了,这个概率比较少
*/
bool COpensslCrypto::AESDecodeFile(const std::string &infile, const std::string &outfile, const std::string &password)
{
std::string pwd = password;
pwd.resize(32,'a');

FILE *fpInfile = fopen(infile.c_str(),"r");
FILE *fpOutFile = fopen(outfile.c_str(),"w+");
if(!fpInfile || !fpOutFile){
if(fpInfile)
fclose(fpInfile);
if(fpOutFile)
fclose(fpOutFile);
return false;
}

char buff[AES_BLOCK_SIZE + 1];
memset(buff,0,sizeof(buff));
std::string encode_data;
size_t readSize = 0;
size_t inFileOffset = 0;
size_t inFileLength = getFileLength(fpInfile);
while((readSize = fread(buff,1,AES_BLOCK_SIZE,fpInfile)) > 0 && readSize != (size_t)EOF){
encode_data = AESDecode(pwd,std::string(buff,readSize));
if(inFileLength - inFileOffset <= AES_BLOCK_SIZE){
break;
}
else{
fwrite(encode_data.data(),encode_data.size(),1,fpOutFile);
}
memset(buff,0,sizeof(buff));
inFileOffset += readSize;
}
size_t len = encode_data.find_last_not_of('\0',encode_data.length());
fwrite(encode_data.data(),1,len + 1,fpOutFile);
fclose(fpInfile);
fclose(fpOutFile);
return true;
}

inline size_t getFileLength(FILE *fp)
{
long backup = ftell(fp);
size_t size = 0;
fseek(fp,0,SEEK_SET);
fseek(fp,0,SEEK_END);
size = ftell(fp);
fseek(fp,backup,SEEK_SET);
return size;
}


一个简单的使用 rsa 私钥加密信息串例子,其它类似

#include "privateKey.h"
#include "copensslcrypto.h"
#include <iostream>
int main(void)
{
std::string str = "hello world!";
std::string priEncode;// 存放加密数据
priEncode = COpensslCrypto::RSAPriEncodeBIO(str,GoKey::prk);
return 0;
}