OpenSSL实现AES的ECB和CBC加解密,可一次性加解密任意长度的明文字符串或字节流(QT C++环境)

本篇博文讲述如何在Qt C++的环境中使用OpenSSL实现AES-ECB/CBC-Pkcs7加/解密,可以一次性加解密一个任意长度的明文字符串或者字节流,但不适合分段读取加解密的(例如,一个4GB的大型文件需要加解密,要分段读取,每次读取10MB,就加解密10MB,这种涉及全文件填充,而不是每个10MB片段填充具有较复杂的上下文处理,本文不探讨这种)

Qt中的QByteArray比较好用,所以我本篇文章不使用标准C++的unsigned char数组,而是用QByteArray代替,所以要依赖Qt的环境,如果你不用Qt就想办法把QByteArray改回unsigned char数组。

一、简介

AES:略(自行百度)

ECB:略(自行百度)

CBC:略(自行百度)

PKCS7:填充方式,AES支持多种填充方式:如NoPadding、PKCS5Padding、ISO10126Padding、PKCS7Padding、ZeroPadding。PKCS7兼容PKCS5,目前PKCS7比较通用。

二、实现方式

1.使用以下4个接口实现AES的ECB和CBC加解密,注意:PKCS7的填充需要自己另外实现,想要对大型文件分段读取整体加解密的话非常麻烦,要自己想办法处理上下文衔接问题,这4个接口在Openssl的v3.0版本以后被标记为废弃接口,但还可以使用:

int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
void AES_ecb_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key, const int enc);
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,size_t length, const AES_KEY *key, unsigned char *ivec, const int enc);

2.使用Openssl的EVP接口实现AES的ECB和CBC加解密,EVP接口内置各种填充不需要我们自己实现了,另外利用其自带的上下文结构特性,可以进行大型文件分段读取整体加解密,使用简易性和灵活性都很高,但是执行效率和资源消耗比非evp接口差一些。(推荐,v3.0前后的版本都可以兼容,但执行效率比上面4个接口稍差):

EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *c);
EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *c);

int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *c, int pad);

int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv);
int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);

int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv);
int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl);

三、实现Pkcs7填充/去除填充

描述:使用Openssl v3.0以前的旧接口实现,Pkcs7填充/去除填充需要自己实现。

创造一个Pkcs7填充/去填充的工具类WJPPadding,封装Pkcs7填充/去除填充的静态接口:

#ifndef WJPPADDING_H
#define WJPPADDING_H

#include <QByteArray>

/**
 * @brief 数据填充类(对齐类)
 * 算法数据填充模式,提供对数据进行PKCS7填充和去除填充的相关函数。
 */

class WJPPadding
{
public:
    WJPPadding();

    /**
     * @brief 根据原始数据长度,计算PKCS7填充后的数据长度
     * @param dataLen 原始数据长度
     * @param alignSize 对齐字节数
     * @return 返回填充后的数据长度
     */
    static int getPKCS7PaddedLength(int dataLen, int alignSize);

    /**
     * @brief 采用PKCS7Padding方式,将in数据进行alignSize字节对齐填充,填充后输出到out
     * 此函数用于加密前,对明文进行填充。
     * @param in 原始数据
     * @param out 填充后的数据
     * @param alignSize 对齐字节数(对于aes加解密,一般都是16 Byte)
     * @return 无返回
     */
    static void PKCS7Padding(const QByteArray &in, QByteArray &out, int alignSize);

    /**
     * @brief 采用PKCS7Padding方式,将in数据去除填充。
     * 此函数用于解密后,对解密结果进一步去除填充,以得到原始数据,直接在原数据中剔除
     * @param in 需要去除填充的数据
     * @return 无返回
     */
    static void PKCS7UnPadding(QByteArray &in);
};

#endif // WJPPADDING_H
#include "wjppadding.h"

WJPPadding::WJPPadding()
{

}

int WJPPadding::getPKCS7PaddedLength(int dataLen, int alignSize)
{
    // 计算填充的字节数(按alignSize字节对齐进行填充)
    int remainder = dataLen % alignSize;
    int paddingSize = (remainder == 0) ? alignSize : (alignSize - remainder);
    return (dataLen + paddingSize);
}

void WJPPadding::PKCS7Padding(const QByteArray &in, QByteArray &out, int alignSize)
{
    // 计算需要填充的字节数(按alignSize字节对齐进行填充)
    int remainder = in.size() % alignSize;
    int paddingSize = (remainder == 0) ? alignSize : (alignSize - remainder);
    char paddingChar = static_cast<char>(paddingSize);// 填充字节数转字符

    // 进行填充
    out.reserve(in.size()); // 预留拷贝空间
    out = in; //拷贝原始数据
    out.append(paddingSize, paddingChar);// 尾部填充上面计算得到的填充字符
}

void WJPPadding::PKCS7UnPadding(QByteArray &in)
{
    // 读取尾部最后一个元素,得到填充字符
    char paddingSize = in.at(in.size() - 1);

    // 填充字符转填充字节数,并在尾部剔除填充字节数所指的字节
    in.chop(static_cast<int>(paddingSize));
}

四、实现加解密接口

描述:以下封装的接口,没有evp前缀的使用Openssl v3.0以前的旧接口实现,有evp前缀的就是使用Openssl的EVP接口实现。

创造一个自己的AES加解密类WJPAES,封装加解密的静态接口:

#ifndef WJPAES_H
#define WJPAES_H

#include <QObject>

#include "openssl/types.h"

class WJPAES
{
public:
    WJPAES();

    /**
     * @brief ECB模式加解密,填充模式采用PKCS7,
     * 对任意长度明文进行加一次解密,根据机器内存情况,尽量不要太长。
     * @param in 输入数据
     * @param out 输出结果
     * @param key 密钥,长度必须是16/24/32字节,否则加密失败
     * @param enc true-加密,false-解密
     * @return 执行结果
     */
    static bool ecb_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const bool &enc);
    static bool evp_ecb_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const bool &enc);

    /**
     * @brief CBC模式加解密,填充模式采用PKCS7,
     * 对任意长度明文进行一次加解密,根据机器内存情况,尽量不要太长。
     * @param in 输入数据
     * @param out 输出结果
     * @param key 密钥,长度必须是16/24/32字节,否则加密失败
     * @param ivec 初始向量,长度必须是16字节
     * @param enc true-加密,false-解密
     * @return 执行结果
     */
    static bool cbc_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, const bool &enc);
    static bool evp_cbc_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, const bool &enc);


    /**
     * @brief 通用所有模式加解密,填充模式采用PKCS7,
     * 对任意长度明文进行一次加解密,根据机器内存情况,尽量不要太长。
     * @param in 输入数据
     * @param out 输出结果
     * @param key 密钥,长度必须是16/24/32字节,否则加密失败
     * @param ivec 初始向量,长度必须是16字节,如果是ECB可以传一个空的
     * @param enc true-加密,false-解密
     * @param cipher 加解密模式(ECB-128、ECB-192、ECB-256、CBC-128、CBC-192....)
     * @return 执行结果
     */
    static bool evp_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, const bool &enc, const EVP_CIPHER *cipher);
};

#endif // WJPAES_H

详细实现见源码:

https://download.csdn.net/download/wu10188/89276012

已测试过接口,开箱即用。

五、测试结果:

1.加密:

// 点击了加密按钮,触发加密流程
void AESTestWidget::on_btnEncrypt_clicked()
{
    //加密字符串
    QByteArray encryptText;
    QByteArray encryptTextBase64;
    QByteArray key(ui->leEncryptKey->text().toUtf8());
    QByteArray ivec(ui->leEncryptIV->text().toUtf8());

    // 加密(将明文加密为二进制数据)
    if(m_mode == "ECB")
    {
        //WJPAES::ecb_encrypt(ui->tePlaintext2Encrypt->toPlainText().toUtf8(), encryptText, key, true);
        WJPAES::evp_ecb_encrypt(ui->tePlaintext2Encrypt->toPlainText().toUtf8(), encryptText, key, true);
    }
    else if(m_mode == "CBC")
    {
        WJPAES::cbc_encrypt(ui->tePlaintext2Encrypt->toPlainText().toUtf8(), encryptText, key, ivec, true);
        //WJPAES::evp_cbc_encrypt(ui->tePlaintext2Encrypt->toPlainText().toUtf8(), encryptText, key, ivec, true);
    }

    // 对加密后的二进制数据进行base64编码
    encryptTextBase64 = encryptText.toBase64();
    ui->teCiphertextBase64->setText(QString::fromUtf8(encryptTextBase64));

    ui->teCiphertextHex->setText(QString::fromUtf8(encryptText.toHex()));

}

CBC加密Demo:

key:H321jDtfnet@1279

初始向量:H321jDtfnet@1279

明文:床前明月光,疑是地上霜。 举头望明月,低头思故乡。-liBai

加密后得到的密文(base64):UG7NZA2BjdZirj0R8scTpFHbzobdQnmNEQjDoZCi2tallU7wxLJPQ0q4SViZIb3gwt+BxpkOrp3nwZeSWWG95hF6atwh+ZLdjustYDQxA9A=

2.解密:

// 点击了解密按钮,触发解密流程
void AESTestWidget::on_btnDecrypt_clicked()
{
    //解密base64字符串
    QByteArray decryptText;
    QByteArray key(ui->leDecryptKey->text().toUtf8());
    QByteArray ivec(ui->leDecryptIV->text().toUtf8());

    // 解密(先将base64编码的密文转为二进制字节数据再解密)
    if(m_mode == "ECB")
    {
        WJPAES::ecb_encrypt(QByteArray::fromBase64(ui->teCiphertext2Decrypt->toPlainText().toUtf8()),decryptText, key, false);
        //WJPAES::evp_ecb_encrypt(QByteArray::fromBase64(ui->teCiphertext2Decrypt->toPlainText().toUtf8()),decryptText, key, false);
    }
    else if(m_mode == "CBC")
    {
        //WJPAES::cbc_encrypt(QByteArray::fromBase64(ui->teCiphertext2Decrypt->toPlainText().toUtf8()),decryptText, key, ivec, false);
        WJPAES::evp_cbc_encrypt(QByteArray::fromBase64(ui->teCiphertext2Decrypt->toPlainText().toUtf8()),decryptText, key, ivec, false);
    }

    qDebug() << "解密结果:" << decryptText;
    ui->tePlaintextByDecrypt->setText(QString::fromUtf8(decryptText));
}

CBC解密Demo:

key:H321jDtfnet@1279

初始向量:H321jDtfnet@1279

密文(base64):UG7NZA2BjdZirj0R8scTpFHbzobdQnmNEQjDoZCi2tallU7wxLJPQ0q4SViZIb3gwt+BxpkOrp3nwZeSWWG95hF6atwh+ZLdjustYDQxA9A=

解密后得到的明文:床前明月光,疑是地上霜。 举头望明月,低头思故乡。-liBai

Demo下载地址:

https://download.csdn.net/download/wu10188/89276012

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/607500.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux-笔记 i2c-tools

1、i2c-tools介绍 1、在日常linux开发中&#xff0c;有时候需要确认i2c硬件是否正常连接&#xff0c;设备是否正常工作&#xff0c;设备的地址是多少等等&#xff0c;这里我们就需要使用一个用于测试I2C总线的工具——i2c-tools&#xff0c;i2c-tools原理是通过操作/dev 路径 …

JavaScript之数据类型(2)——复杂类型(object)

object的介绍&#xff1a; 我对于object的理解是和C/C中的结构体一样&#xff0c;是一个自定义的数据类型&#xff0c;我们可以通过多个简单的数据类型来定义一个便于我们使用的新的数据类型。 在网上某佬对于其解释如下&#xff1a; Object类型&#xff0c;我们也称为一个对象…

Redis学习5——Redis应用之签到

Redis位图bitMap 位图由一系列二进制位组成&#xff0c;每个位可以被设置为1或0&#xff0c;当我们在处理需要高效存储和操作大量二进制位数据的适合&#xff0c;位图是一个非常有用的工具。 位图操作命令有&#xff1a; SETBIT&#xff1a;设置位图中指定位置的位的值。可以…

3.ERC4626

ERC4626是一个vault&#xff0c;在DAI中&#xff0c;使用ETH换取DAI。其流程为先充值ETH到maker vault。 Vault 资产的管理、分红用户充值某项资产获取某个凭证该凭证作为分红、推出的依据Yield Farming/借贷/质押等 以太坊改进提案EIP:ethereum improvemwnt proposal 最初E…

Mysql8.0.30一次表锁问题的解决

起因 给material_config_field_data表的字段建立全文索引的时&#xff0c;发现该表卡死&#xff0c;然后无法对该表进行任何操作。 查找问题 执行sql #这个命令会显示InnoDB存储引擎的详细状态信息&#xff0c;包括锁等待和锁争用的信息 SHOW ENGINE INNODB STATUS结果 复制S…

​Inf-DiT:Upsampling Any-Resolution Image、Vidu、MVDiff、Trio-ViT

本文首发于公众号&#xff1a;机器感知 ​Inf-DiT&#xff1a;Upsampling Any-Resolution Image、Vidu、MVDiff、Trio-ViT Inf-DiT: Upsampling Any-Resolution Image with Memory-Efficient Diffusion Transformer Diffusion models have shown remarkable performance in im…

树莓派4-使用systemctl设置开机自启oled播放服务ip地址与logo

一、目标&#xff1a; 开机自启oled显示服务ip与端口&#xff0c;并播放logo 二、过程&#xff1a; 1、出现luma库不存在问题&#xff0c;修改.service文件&#xff0c;增加用户与用户组。在本地测试过程中可以使用python script.py执行python脚本&#xff0c;所以将.servic…

java递归-(迷宫问题)

前面 这里我们来玩个有趣的事情&#xff0c;链接是0221_韩顺平Java_老鼠出迷宫1_哔哩哔哩_bilibili 我们要找的是小老鼠按路径走到右下点 要点 我们这里方法调用时对于引用类型&#xff1a;如java中引用数据类型有哪些&#xff1f;_java引用数据类型-CSDN博客 会共享引用类型…

斯坦福大学的在线密码学课程

密码学是保护计算机系统信息不可或缺的工具。在本课程中&#xff0c;您将了解密码系统的内部工作原理&#xff0c;以及如何在实际应用中正确使用它们。课程首先将详细讨论当强大的对手窃听和篡改流量时&#xff0c;拥有共享密钥的双方如何进行安全通信。我们将研究许多已部署的…

部署Gerapy

1.Gerapy 是什么&#xff1f; Gerapy 是一款基于 Python 3 的分布式爬虫管理框架&#xff0c;它旨在简化和优化分布式爬虫的部署、管理和监控过程。 2.作用与功能&#xff1f; 2.1分布式管理&#xff1a; Gerapy 允许用户在多台机器上部署和管理Scrapy爬虫&#xff0c;实现爬虫…

【计算机毕设】小型企业办公自动化系统+vue - 免费源码(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 本项目旨在设计并实现一个小型企业办公自动化系统&#xff0c;利用Vue作为前端框架&#xff0c;为企业员工提供便捷的办公管理工具&#xff0c;提升…

基于51单片机的八路抢答器—加随机抽选功能

基于51单片机的八路抢答器 &#xff08;仿真&#xff0b;程序原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.主持人按键控制开始抢答&#xff1b; 2.开始抢答按下&#xff0c;数码管20秒倒计时&#xff1b; 3.8个按键代表八位选手&#xff0c;谁…

python面向函数

组织好的&#xff0c;可重复利用的&#xff0c;用来实现单一&#xff0c;或相关联功能的代码段&#xff0c;避免重复造轮子&#xff0c;增加程序复用性。 定义方法为def 函数名 (参数) 参数可动态传参&#xff0c;即使用*args代表元组形式**kwargs代表字典形式&#xff0c;代替…

tsconfig 备忘清单

前言 ❝ Nealyang/blog0 使用 ts 已多年&#xff0c;但是貌似对于 tsconfig 总是记忆不清&#xff0c;每次都是 cv 历史项目&#xff0c;所以写了这篇备忘录&#xff0c;希望能帮助到大家。 本文总结整理自 Matt Pocock 的一篇文章3&#xff0c;加以个人理解&#xff0c;并做了…

【爬虫基础1.1课】——requests模块

目录索引 requests模块的作用&#xff1a;实例引入&#xff1a; 特殊情况&#xff1a;锦囊1&#xff1a;锦囊2: 这一个栏目&#xff0c;我会给出我从零开始学习爬虫的全过程。感兴趣的小伙伴可以关注一波&#xff0c;用于复习和新学都是不错的选择。 那么废话不多说&#xff0c…

【Matlab-动画-附源码】3分钟教你用Matlab做一个Lorenz动画

lorenz-x-y-z Lorenz三个维度数据 在科研工作中&#xff0c;经常需要将数据可视化以便更好地理解和传达研究成果。 但大家主要放静态图片&#xff0c;而视频或动画通常比静态图片更具吸引力和表现力。AE, Manim太难学&#xff0c;Matlab就可以用来制作动画。 在这篇博客中&…

Linux-信号执行

1. 信号什么时候被处理 当进程从内核态返回到用户态的时候&#xff0c;进行信号的检测和处理 什么内核态&#xff0c;什么又是用户态呢&#xff1f; 当进程在CPU上运行时&#xff0c;内核态&#xff1a;允许进程访问操作系统的代码和数据&#xff0c;用户态&#xff1a;进程只…

视频降噪算法 hqdn3d 原理分析

视频降噪 视频降噪是一种处理技术&#xff0c;旨在减少视频中的噪声&#xff0c;提高画面质量。噪声可能来自多种源头&#xff0c;包括摄像机的传感器、压缩算法、传输过程中的干扰等。降噪处理对于视频监控、视频会议、电影后期制作以及任何需要高画质输出的应用场景都非常重…

【面经】网络

了解TCP/IP协议,了解常用的网络协议&#xff1a;study-area 一、TCP/IP协议 TCP/IP协议是一组网络通信协议&#xff0c;旨在实现不同计算机之间的信息传输。 1、TCP/IP四层模型&#xff1a; 网络接口层、网络层、传输层和应用层。 网络接口层&#xff1a;定义了数据的格式和…

揭秘抖音快速涨10000粉的方法:巨量千川投流让你轻松快速增粉

抖音已经成为了当今社交平台的热门之一&#xff0c;而如何快速涨粉已经成为了很多人关注的焦点。本文将揭秘一种高效的方式——巨量千川投流&#xff0c;通过官方真实流量和真实粉丝&#xff0c;每天快速涨关注&#xff0c;实现快速增粉1000~10万。 巨量千川投流是一种专业的抖…
最新文章