本文属于《OpenSSL加密算法库使用系列教程》之一,欢迎查看其它文章。

实战篇-OpenSSL之调用EVP框架实现AES多种加密模式

  • 一、OpenSSL EVP简介
  • 二、EVP中对称加密与解密流程
  • 三、通过EVP实现AES多种加密模式
  • 四、测试代码

一、OpenSSL EVP简介

OpenSSL EVP(high-level cryptographic functions)提供了丰富的密码学中的各种函数。Openssl 中实现了各种对称算法、摘要算法以及签名/验签算法。EVP 函数将这些具体的算法进行了封装。

通过这样的统一的封装,使得只需要在初始化参数的时候做很少的改变,就可以使用相同的代码但采用不同的加密算法进行数据的加密和解密。

一句话,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()
{// 初始化CTXctx = EVP_CIPHER_CTX_new();EVP_CIPHER_CTX_init(ctx);
}EvpAES::~EvpAES()
{// 释放CTXEVP_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和ivint 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和ivint 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实现AES多种加密模式

使用EVP的好处就是,不用考虑诸如对齐填充、秘钥、处理长度等等细节,这些细节每个加密模式,可能都不一样。EVP把这些加密算法全部统一了,以统一的方式去操作。

与直接调用具体的加密函数相比,EVP的步骤更多了一些,所以具体使用哪种,根据自己实际情况来即可。

我们在前面的encrypt函数基础上,封装更多的加密模式方法出来,以供外部使用。

这里实现了ECB、CBC、CFB1、CFB8、CFB128、OFB128、CTR、GCM、XTS、OCB共10种模式。如下:

#include "EvpAES.h"
#include <openssl/evp.h>
#include <openssl/aes.h>#define KEY_SIZE_16B            16
#define KEY_SIZE_24B            24
#define KEY_SIZE_32B            32bool 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_CIPHERconst 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_CIPHERconst 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_CIPHERconst 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_CIPHERconst 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_CIPHERconst 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_CIPHERconst 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_CIPHERconst 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_CIPHERconst 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_CIPHERconst 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_CIPHERconst 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);// 测试AEStestEvpAES(data);  // 测试,通过EVP框架调用AES算法return a.exec();
}

执行结果:

本文涉及工程代码地址:https://gitee.com/bailiyang/cdemo/tree/master/Qt/49OpenSSL/OpenSSL


若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

实战篇-OpenSSL之调用EVP框架实现AES多种加密模式相关推荐

  1. OpenSSL之调用EVP框架实现AES多种加密模式

    OpenSSL之调用EVP框架实现AES多种加密模式 一.OpenSSL EVP简介 二.EVP中对称加密与解密流程 三.通过EVP实现AES多种加密模式 四.测试代码 原文链接: https://b ...

  2. 实战篇-OpenSSL之AES加密算法-ECB模式

    本文属于<OpenSSL加密算法库使用系列教程>之一,欢迎查看其它文章. 实战篇-OpenSSL之AES加密算法-ECB模式 一.AES简介 二.ECB模式 1.命令行操作 2.函数说明 ...

  3. 实战篇-OpenSSL之AES加密算法-CFB8模式

    本文属于<OpenSSL加密算法库使用系列教程>之一,欢迎查看其它文章. 实战篇-OpenSSL之AES加密算法-CFB8模式 一.AES简介 二.CFB8模式 1.命令行操作 2.函数说 ...

  4. 实战篇-OpenSSL之AES加密算法-CFB128模式

    本文属于<OpenSSL加密算法库使用系列教程>之一,欢迎查看其它文章. 实战篇-OpenSSL之AES加密算法-CFB128模式 一.AES简介 二.CFB128模式 1.命令行操作 2 ...

  5. 实战篇-OpenSSL之AES加密算法-CFB1模式

    本文属于<OpenSSL加密算法库使用系列教程>之一,欢迎查看其它文章. 实战篇-OpenSSL之AES加密算法-CFB1模式 一.AES简介 二.CFB1模式 1.命令行操作 2.函数说 ...

  6. 实战篇-OpenSSL之TripleDES加密算法-ECB模式

    本文属于<OpenSSL加密算法库使用系列教程>之一,欢迎查看其它文章. 实战篇-OpenSSL之TripleDES加密算法-ECB模式 一.TripleDES简介 二.ECB模式 1.命 ...

  7. 实战篇-OpenSSL之TripleDES加密算法-CFB64模式

    本文属于<OpenSSL加密算法库使用系列教程>之一,欢迎查看其它文章. 实战篇-OpenSSL之TripleDES加密算法-CFB64模式 一.TripleDES简介 二.CFB64模式 ...

  8. caffe框架下目标检测——faster-rcnn实战篇操作

    原有模型 1.下载fasrer-rcnn源代码并安装 git clone --recursive https://github.com/rbgirshick/py-faster-rcnn.git 1) ...

  9. python自动化框架2019_《一头扎进》系列之Python+Selenium自动化测试框架实战篇6 - 价值好几K的框架,呦!这个框架还真牛叉哦!!!...

    1. 简介 本文开始介绍如何通过unittest来管理和执行测试用例,这一篇主要是介绍unittest下addTest()方法来加载测试用例到测试套件中去.用addTest()方法来加载我们测试用例到 ...

最新文章

  1. YOLOv3 训练的各种config文件以及weights文件。
  2. 找工作 50道编程题Java实现(32-50)
  3. WebApi的安全性及其解决方案
  4. 退出python命令行-退出python命令
  5. lambda sort
  6. PHP运行神器--用HHVM减少你一半的服务器
  7. mysql dba工作笔记pdf_社区专家在线:Oracle数据库、MySQL、Db2 等数据库日常运维故障与性能调优在线答疑...
  8. hive 导入mysql数据库_求助 Hive 导入MYsql 数据库 报错啊
  9. pta-3、输入输出-格式化输出字符串 (10 分)
  10. Fast-SCNN 多分支结构共享低级特征的语义分割网络 (一)
  11. 利用navicat 进行 mysql建表语句转oracle建表语句
  12. 计算机硬件现状及发展,计算机硬件的发展历史以及计算机的现状和发展趋势
  13. 部分常用GIS网站论坛推荐
  14. java基于ssm的房屋租赁管理系统
  15. 值得分享的炒白银技巧有哪些?
  16. (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  17. python 聚类 客户细分_【火炉炼AI】机器学习027-项目案例:用聚类算法建立客户细分模型...
  18. aardio - 范例搜索工具
  19. 量化投资中常用python代码分析(一)
  20. 大数据和人工智能AI的联系和区别

热门文章

  1. JS面向对象编程(OOP)
  2. 美化窗体——VB窗体的背景图充满整个窗体
  3. matlab画图函数汇总(三)
  4. ATC DP X - Tower
  5. linux服务器登录卡在last login的解决
  6. 软考中级-软件设计师-视频学习时长记录
  7. 第十三章第一节(Triangle类)(Triangle class)
  8. 脚本批量提取文件路径
  9. 到了2020年,年薪80w的阿里P7专家,顶尖的技术人才只因做到了这几点
  10. 标题标签对seo的重要性!