原文链接:
https://blog.csdn.net/zyhse/article/details/112294229
Openssl EVP(high-level cryptographic functions[1])提供了丰富的密码学中的各种函数。Openssl 中实现了各种对称算法、摘要算法以及签名/验签算法。EVP 函数将这些具体的算法进行了封装。
EVP系列的函数的声明包含在”evp.h”里面,这是一系列封装了openssl>加密库里面所有算法的函数。通过这样的统一的封装,使得只需要在初始化参数的时候做很少的改变,就可以使用相同的代码但采用不同的加密算法进行数据的加密和解密。
EVP系列函数主要封装了加密、摘要、编码三大类型的算法,使用算法前需要调用OpenSSL_add_all_algorithms函数。
其中以加密算法与摘要算法为基本,公开密钥算法是对数据加密采用了对称加密算法,对密钥采用非对称加密(公钥加密,私钥解密)。数字签名是非对称算法(私钥签名,公钥认证)。
EVP 主要封装了如下功能函数:
OpenSSL EVP(high-level cryptographic functions)提供了丰富的密码学中的各种函数。Openssl 中实现了各种对称算法、摘要算法以及签名/验签算法。EVP 函数将这些具体的算法进行了封装。
通过这样的统一的封装,使得只需要在初始化参数的时候做很少的改变,就可以使用相同的代码但采用不同的加密算法进行数据的加密和解密。
一句话,EVP是封装的高层接口,通过它加解密,可以不用关心更多细节问题,使用更简单。
EVP中用于对称加密的函数,主要有下面这些。
一般加密流程,执行步骤如下所示:
EVP_CIPHER_CTX_init,初始化对称计算上下文
EVP_EncryptInit_ex,加密初始化函数,本函数调用具体算法的init 回调函数,将外送密钥key 转换
为内部密钥形式,将初始化向量iv 拷贝到ctx 结构中。
EVP_EncryptUpdate,加密函数,用于多次计算,它调用了具体算法的 do_cipher 回调函数。
EVP_EncryptFinal_ex,获取加密结果,函数可能涉及填充,它调用了具体算法的 do_cipher 回调函数。
一般解密流程,执行步骤如下所示:
EVP_CIPHER_CTX_init,初始化对称计算上下文
EVP_DecryptInit_ex,解密初始化函数。
EVP_DecryptUpdate,解密函数,用于多次计算,它调用了具体算法的 do_cipher 回调函数。
EVP_DecryptFinal_ex,获取解密结果,函数可能涉及去填充,它调用了具体算法的 do_cipher 回调函数。
根据上述的步骤,封装一个基本的,EVP加解密函数,如下:
EvpAES::EvpAES()
{
// 初始化CTX
ctx = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(ctx);
}
EvpAES::~EvpAES()
{
// 释放CTX
EVP_CIPHER_CTX_cleanup(ctx);
EVP_CIPHER_CTX_free(ctx);
}
bool EvpAES::encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, const EVP_CIPHER *ciper, bool enc)
{
if (enc)
{
// 指定加密算法及key和iv
int ret = EVP_EncryptInit_ex(ctx, ciper, NULL, (const unsigned char*)key.data(), (const unsigned char*)ivec.data());
if(ret != 1)
{
return false;
}
// 进行加密操作
int mlen = 0;
out.resize(in.size() + AES_BLOCK_SIZE);
ret = EVP_EncryptUpdate(ctx, (unsigned char*)out.data(), &mlen, (const unsigned char*)in.data(), in.size());
if(ret != 1)
{
return false;
}
// 结束加密操作
int flen = 0;
ret = EVP_EncryptFinal_ex(ctx, (unsigned char *)out.data() + mlen, &flen);
if(ret != 1)
{
return false;
}
out.resize(mlen + flen);
return true;
}
else
{
// 指定解密算法及key和iv
int ret = EVP_DecryptInit_ex(ctx, ciper, NULL, (const unsigned char*)key.data(), (const unsigned char*)ivec.data());
if(ret != 1)
{
return false;
}
// 进行解密操作
int mlen = 0;
out.resize(in.size());
ret = EVP_DecryptUpdate(ctx, (unsigned char*)out.data(), &mlen, (const unsigned char*)in.data(), in.size());
if(ret != 1)
{
return false;
}
// 结束解密操作
int flen = 0;
ret = EVP_DecryptFinal_ex(ctx, (unsigned char *)out.data() + mlen, &flen);
if(ret != 1)
{
return false;
}
out.resize(mlen + flen);
return true;
}
}
使用EVP的好处就是,不用考虑诸如对齐填充、秘钥、处理长度等等细节,这些细节每个加密模式,可能都不一样。EVP把这些加密算法全部统一了,以统一的方式去操作。
与直接调用具体的加密函数相比,EVP的步骤更多了一些,所以具体使用哪种,根据自己实际情况来即可。
我们在前面的encrypt函数基础上,封装更多的加密模式方法出来,以供外部使用。
这里实现了ECB、CBC、CFB1、CFB8、CFB128、OFB128、CTR、GCM、XTS、OCB共10种模式。如下:
#include "EvpAES.h"
#include
#include
#define KEY_SIZE_16B 16
#define KEY_SIZE_24B 24
#define KEY_SIZE_32B 32
bool EvpAES::ecb_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, bool enc)
{
// 检查密钥合法性(只能是16、24、32字节)
Q_ASSERT(key.size() == KEY_SIZE_16B || key.size() == KEY_SIZE_24B || key.size() == KEY_SIZE_32B);
// 根据key大小创建EVP_CIPHER
const EVP_CIPHER * cipher = nullptr;
if (key.size() == KEY_SIZE_16B)
{
cipher = EVP_aes_128_ecb();
}
else if (key.size() == KEY_SIZE_24B)
{
cipher = EVP_aes_192_ecb();
}
else
{
cipher = EVP_aes_256_ecb();
}
// 执行加解密
return encrypt(in, out, key, QByteArray(), cipher, enc);
}
bool EvpAES::cbc_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
// 检查密钥合法性(只能是16、24、32字节)
Q_ASSERT(key.size() == KEY_SIZE_16B || key.size() == KEY_SIZE_24B || key.size() == KEY_SIZE_32B);
Q_ASSERT(ivec.size() == KEY_SIZE_16B); // 初始向量为16字节
// 根据key大小创建EVP_CIPHER
const EVP_CIPHER * cipher = nullptr;
if (key.size() == KEY_SIZE_16B)
{
cipher = EVP_aes_128_cbc();
}
else if (key.size() == KEY_SIZE_24B)
{
cipher = EVP_aes_192_cbc();
}
else
{
cipher = EVP_aes_256_cbc();
}
// 执行加解密
return encrypt(in, out, key, ivec, cipher, enc);
}
bool EvpAES::cfb1_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
// 检查密钥合法性(只能是16、24、32字节)
Q_ASSERT(key.size() == KEY_SIZE_16B || key.size() == KEY_SIZE_24B || key.size() == KEY_SIZE_32B);
Q_ASSERT(ivec.size() == KEY_SIZE_16B); // 初始向量为16字节
// 根据key大小创建EVP_CIPHER
const EVP_CIPHER * cipher = nullptr;
if (key.size() == KEY_SIZE_16B)
{
cipher = EVP_aes_128_cfb1();
}
else if (key.size() == KEY_SIZE_24B)
{
cipher = EVP_aes_192_cfb1();
}
else
{
cipher = EVP_aes_256_cfb1();
}
// 执行加解密
return encrypt(in, out, key, ivec, cipher, enc);
}
bool EvpAES::cfb8_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
// 检查密钥合法性(只能是16、24、32字节)
Q_ASSERT(key.size() == KEY_SIZE_16B || key.size() == KEY_SIZE_24B || key.size() == KEY_SIZE_32B);
Q_ASSERT(ivec.size() == KEY_SIZE_16B); // 初始向量为16字节
// 根据key大小创建EVP_CIPHER
const EVP_CIPHER * cipher = nullptr;
if (key.size() == KEY_SIZE_16B)
{
cipher = EVP_aes_128_cfb8();
}
else if (key.size() == KEY_SIZE_24B)
{
cipher = EVP_aes_192_cfb8();
}
else
{
cipher = EVP_aes_256_cfb8();
}
// 执行加解密
return encrypt(in, out, key, ivec, cipher, enc);
}
bool EvpAES::cfb128_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
// 检查密钥合法性(只能是16、24、32字节)
Q_ASSERT(key.size() == KEY_SIZE_16B || key.size() == KEY_SIZE_24B || key.size() == KEY_SIZE_32B);
Q_ASSERT(ivec.size() == KEY_SIZE_16B); // 初始向量为16字节
// 根据key大小创建EVP_CIPHER
const EVP_CIPHER * cipher = nullptr;
if (key.size() == KEY_SIZE_16B)
{
cipher = EVP_aes_128_cfb128();
}
else if (key.size() == KEY_SIZE_24B)
{
cipher = EVP_aes_192_cfb128();
}
else
{
cipher = EVP_aes_256_cfb128();
}
// 执行加解密
return encrypt(in, out, key, ivec, cipher, enc);
}
bool EvpAES::ofb128_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
// 检查密钥合法性(只能是16、24、32字节)
Q_ASSERT(key.size() == KEY_SIZE_16B || key.size() == KEY_SIZE_24B || key.size() == KEY_SIZE_32B);
Q_ASSERT(ivec.size() == KEY_SIZE_16B); // 初始向量为16字节
// 根据key大小创建EVP_CIPHER
const EVP_CIPHER * cipher = nullptr;
if (key.size() == KEY_SIZE_16B)
{
cipher = EVP_aes_128_ofb();
}
else if (key.size() == KEY_SIZE_24B)
{
cipher = EVP_aes_192_ofb();
}
else
{
cipher = EVP_aes_256_ofb();
}
// 执行加解密
return encrypt(in, out, key, ivec, cipher, enc);
}
bool EvpAES::ctr_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
// 检查密钥合法性(只能是16、24、32字节)
Q_ASSERT(key.size() == KEY_SIZE_16B || key.size() == KEY_SIZE_24B || key.size() == KEY_SIZE_32B);
Q_ASSERT(ivec.size() == KEY_SIZE_16B); // 初始向量为16字节
// 根据key大小创建EVP_CIPHER
const EVP_CIPHER * cipher = nullptr;
if (key.size() == KEY_SIZE_16B)
{
cipher = EVP_aes_128_ctr();
}
else if (key.size() == KEY_SIZE_24B)
{
cipher = EVP_aes_192_ctr();
}
else
{
cipher = EVP_aes_256_ctr();
}
// 执行加解密
return encrypt(in, out, key, ivec, cipher, enc);
}
bool EvpAES::gcm_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
// 检查密钥合法性(只能是16、24、32字节)
Q_ASSERT(key.size() == KEY_SIZE_16B || key.size() == KEY_SIZE_24B || key.size() == KEY_SIZE_32B);
Q_ASSERT(ivec.size() == KEY_SIZE_16B); // 初始向量为16字节
// 根据key大小创建EVP_CIPHER
const EVP_CIPHER * cipher = nullptr;
if (key.size() == KEY_SIZE_16B)
{
cipher = EVP_aes_128_gcm();
}
else if (key.size() == KEY_SIZE_24B)
{
cipher = EVP_aes_192_gcm();
}
else
{
cipher = EVP_aes_256_gcm();
}
// 执行加解密
return encrypt(in, out, key, ivec, cipher, enc);
}
bool EvpAES::xts_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
// 检查密钥合法性(只能是16、32字节)
Q_ASSERT(key.size() == KEY_SIZE_16B || key.size() == KEY_SIZE_32B);
Q_ASSERT(ivec.size() == KEY_SIZE_16B); // 初始向量为16字节
// 根据key大小创建EVP_CIPHER
const EVP_CIPHER * cipher = nullptr;
if (key.size() == KEY_SIZE_16B)
{
cipher = EVP_aes_128_xts();
}
else
{
cipher = EVP_aes_256_xts();
}
// 执行加解密
return encrypt(in, out, key, ivec, cipher, enc);
}
bool EvpAES::ocb_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
// 检查密钥合法性(只能是16、24、32字节)
Q_ASSERT(key.size() == KEY_SIZE_16B || key.size() == KEY_SIZE_24B || key.size() == KEY_SIZE_32B);
Q_ASSERT(ivec.size() == KEY_SIZE_16B); // 初始向量为16字节
// 根据key大小创建EVP_CIPHER
const EVP_CIPHER * cipher = nullptr;
if (key.size() == KEY_SIZE_16B)
{
cipher = EVP_aes_128_ocb();
}
else if (key.size() == KEY_SIZE_24B)
{
cipher = EVP_aes_192_ocb();
}
else
{
cipher = EVP_aes_256_ocb();
}
// 执行加解密
return encrypt(in, out, key, ivec, cipher, enc);
}
代码虽多,但结构类似,比较简单,直接看。
经测试,本类中加解密函数,支持对任意长度输入数据进行加解密。
扩展:
应该DES、TripleDES等对称加密算法,也可以按照上述的方法,从encrypt函数,进行再次封装,以向外提供该算法方法。毕竟都是属于对称加密,道理上应该是可以的。这个就不去试了,感兴趣的小伙伴,自行尝试吧。
void createTestData(QByteArray& data, int size)
{
data.resize(size);
for (int i = 0; i < size; i++)
{
data[i] = i % 128;
}
}
void testEvpAES(const QByteArray& data)
{
QByteArray plainText = data;
QByteArray encryptText;
QByteArray decryptText;
QByteArray key = QByteArray::fromHex("8cc72b05705d5c46f412af8cbed55aad");
QByteArray ivec = QByteArray::fromHex("667b02a85c61c786def4521b060265e8");
// EvpAES ecb模式加密验证
EvpAES aes;
aes.ecb_encrypt(plainText, encryptText, key, true);
aes.ecb_encrypt(encryptText, decryptText, key, false);
qDebug() << "EvpAES ecb encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
encryptText.clear();
decryptText.clear();
// EvpAES cbc模式加密验证
aes.cbc_encrypt(plainText, encryptText, key, ivec, true);
aes.cbc_encrypt(encryptText, decryptText, key, ivec, false);
qDebug() << "EvpAES cbc encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
encryptText.clear();
decryptText.clear();
// EvpAES cfb1模式加密验证
aes.cfb1_encrypt(plainText, encryptText, key, ivec, true);
aes.cfb1_encrypt(encryptText, decryptText, key, ivec, false);
qDebug() << "EvpAES cfb1 encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
encryptText.clear();
decryptText.clear();
// EvpAES cfb8模式加密验证
aes.cfb8_encrypt(plainText, encryptText, key, ivec, true);
aes.cfb8_encrypt(encryptText, decryptText, key, ivec, false);
qDebug() << "EvpAES cfb8 encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
encryptText.clear();
decryptText.clear();
// EvpAES cfb128模式加密验证
aes.cfb128_encrypt(plainText, encryptText, key, ivec, true);
aes.cfb128_encrypt(encryptText, decryptText, key, ivec, false);
qDebug() << "EvpAES cfb128 encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
encryptText.clear();
decryptText.clear();
// EvpAES ofb128模式加密验证
aes.ofb128_encrypt(plainText, encryptText, key, ivec, true);
aes.ofb128_encrypt(encryptText, decryptText, key, ivec, false);
qDebug() << "EvpAES ofb128 encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
encryptText.clear();
decryptText.clear();
// EvpAES ctr模式加密验证
aes.ctr_encrypt(plainText, encryptText, key, ivec, true);
aes.ctr_encrypt(encryptText, decryptText, key, ivec, false);
qDebug() << "EvpAES ctr encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
encryptText.clear();
decryptText.clear();
// EvpAES gcm模式加密验证
aes.gcm_encrypt(plainText, encryptText, key, ivec, true);
aes.gcm_encrypt(encryptText, decryptText, key, ivec, false);
qDebug() << "EvpAES gcm encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
encryptText.clear();
decryptText.clear();
// EvpAES xts模式加密验证
aes.xts_encrypt(plainText, encryptText, key, ivec, true);
aes.xts_encrypt(encryptText, decryptText, key, ivec, false);
qDebug() << "EvpAES xts encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
encryptText.clear();
decryptText.clear();
// EvpAES ocb模式加密验证
aes.ocb_encrypt(plainText, encryptText, key, ivec, true);
aes.ocb_encrypt(encryptText, decryptText, key, ivec, false);
qDebug() << "EvpAES ocb encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
encryptText.clear();
decryptText.clear();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 产生1MB的测试数据
QByteArray data;
createTestData(data, 1*1024*1024);
// 测试AES
testEvpAES(data); // 测试,通过EVP框架调用AES算法
return a.exec();
}
OpenSSL中文手册之EVP库详解
https://blog.csdn.net/liao20081228/article/details/76285896
Windows平台OpenSSL编译方法
https://blog.csdn.net/oldmtn/article/details/47104661?utm_source=blogxgwz1&utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-1.essearch_pc_relevant&spm=1001.2101.3001.4242.2
Windows 下编译 OpenSSL
https://blog.csdn.net/liang19890820/article/details/51658574
nmake 不能用的解决方法
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
关于windows系统编译openssl遇到的ms\do_ms不是内部或外部命令的解决方法
https://blog.csdn.net/qq_33204646/article/details/70624170
这之类的命令,我在编译时输入这些的命令都是提示找不到这些命令,按照网上的说法也重新去安装了一些编译器,结果是统统无效。折腾了快一个下午,无可奈何跑到外网去找,才发现原来从openssl 1.1.0版本以后就不再有ms\do_*.bat这样的文件,而是直接使用nmake命令就行了,原文贴上来:
on Windows (only pick one of the targets for configuration):
$ perl Configure { VC-WIN32 | VC-WIN64A | VC-WIN64I | VC-CE }
$ nmake
$ nmake test
$ nmake install
Note that in order to perform the install step above you need to have
appropriate permissions to write to the installation directory.
If any of these steps fails, see section Installation in Detail below.
This will build and install OpenSSL in the default location, which is:
Unix: normal installation directories under /usr/local
OpenVMS: SYS$COMMON:[OPENSSL]
Windows: C:\Program Files\OpenSSL or C:\Program Files (x86)\OpenSSL
The installation directory should be appropriately protected to ensure
unprivileged users cannot make changes to OpenSSL binaries or files, or install
engines. If you already have a pre-installed version of OpenSSL as part of
your Operating System it is recommended that you do not overwrite the system
version and instead install to somewhere else.
最后执行nmake install
后来翻了一下openssl的install文档,才发现里面都有写,好了,不说了,去哭一会儿。