ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

实战篇-OpenSSL之AES加密算法-CBC模式

2021-01-06 17:01:23  阅读:897  来源: 互联网

标签:CBC 实战篇 加密 AES key QByteArray data 加密算法


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

实战篇-OpenSSL之AES加密算法-CBC模式

一、AES简介

密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。

这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS
PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。

AES属于对称加密算法,加解密使用同一个秘钥。

对称加密算法,一般有至少4种模式,即ECB、CBC、CFB、OFB等。

具体的加密原理,就不进行介绍了,本文主要从使用角度,进行说明。

以下命令行和编程实现,均基于OpenSSL开源库。在命令行中,我们可以使用命令实现对文件加解密,以验证我们的编程实现,是否正确。

二、CBC模式

加密块链模式 Cipher Block Chaining(CBC)。CBC 模式的加密首先也是将明文分成固定长度的块,然后将前面一个加密块输出的密文与下一个要加密的明文块进行异或操作,将计算结果再用密钥进行加密得到密 文。第一明文块加密的时候,因为前面没有加密的密文,所以需要一个初始化向量。跟 ECB 方式不一样,通过连接关系,使得密文跟明文不再是一一对应的关系,破解起来更困难,而且克服了只要简单调换密文块可能达到目的的攻击。

1、命令行操作

使用aes-128-cbc对hello.txt加密,密钥为12345678901234567890,初始化向量为12345678,密文为hello.en。

openssl enc -e -aes-128-cbc -in hello.txt -out hello.en -K 12345678901234567890 -iv 12345678

使用aes-128-cbc对hello.en解密,密钥为12345678901234567890,初始化向量为12345678,解密后的文件为hello.de。

openssl enc -d -aes-128-cbc -in hello.en -out hello.de -K 12345678901234567890 -iv 12345678

2、函数说明

AES CBC加密/解密:

void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
                     size_t length, const AES_KEY *key,
                     unsigned char *ivec, const int enc);
参数名称含义
in输入数据,长度任意
out输出数据,能够容纳下输入数据,且长度必须是16字节的倍数。
length输入数据的实际长度。
key使用AES_set_encrypt_key/AES_set_decrypt_key生成的Key
ivec可读写的一块内存。长度必须是16字节。
encAES_ENCRYPT 代表加密, AES_DECRYPT代表解密

AES_cbc_encrypt在加密的过程中会修改ivec的内容,因此ivec参数不能是一个常量,而且不能在传递给加密函数后再立马传递给解密函数,必须重新赋值之后再传递给解密函数。

3、编程实现

(1)特别注意

CBC加解密函数AES_cbc_encrypt,可以对任意长度的输入数据进行加密,若输入数据不为16字节整数倍时,该函数内部会使用ZeroPadding自动对输入数据,进行填充,再进行加密。

ZeroPadding的填充方式:数据长度不对齐时使用0填充,否则不填充。

加密过程倒是没有什么问题,但是,在将不对齐的明文生成的密文,进行解密时,解密后的数据中末尾出现多个0数据,若明文数据中末尾本身带有0数据,则无法区分该0数据到底是填充的,还是真实数据。

故,建议将输入数据进行PKCS7填充后,再进行加密,以便解密时,正确还原明文数据。

(2)实现CBC模式加解密

下面,函数已经封装完毕,如下:

/**
 * @brief AES::cbc_encrypt
 * CBC模式加解密,填充模式采用PKCS7Padding,
 * 支持对任意长度明文进行加解密。
 * @param in 输入数据
 * @param out 输出结果
 * @param key 密钥,长度必须是16/24/32字节,否则加密失败
 * @param ivec 初始向量,长度必须是16字节
 * @param enc true-加密,false-解密
 * @return 执行结果
 */
bool AES::cbc_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
    // 检查密钥合法性(只能是16、24、32字节)
    Q_ASSERT(key.size() == 16 || key.size() == 24 || key.size() == 32);
    Q_ASSERT(ivec.size() == 16); // 初始向量为16字节

    if (enc)
    {
        // 生成加密key
        AES_KEY aes_key;
        if (AES_set_encrypt_key((const unsigned char*)key.data(), key.size() * 8, &aes_key) != 0)
        {
            return false;
        }

        // 进行PKCS7Padding填充
        QByteArray inTemp = Padding::PKCS7Padding(in, AES_BLOCK_SIZE);

        // 执行CBC模式加密
        QByteArray ivecTemp = ivec; // ivec会被修改,故需要临时变量来暂存
        out.resize(inTemp.size()); // 调整输出buf大小
        AES_cbc_encrypt((const unsigned char*)inTemp.data(),
                        (unsigned char*)out.data(),
                        inTemp.size(),
                        &aes_key,
                        (unsigned char*)ivecTemp.data(),
                        AES_ENCRYPT);
        return true;
    }
    else
    {
        // 生成解密key
        AES_KEY aes_key;
        if (AES_set_decrypt_key((const unsigned char*)key.data(), key.size() * 8, &aes_key) != 0)
        {
            return false;
        }

        // 执行CBC模式解密
        QByteArray ivecTemp = ivec; // ivec会被修改,故需要临时变量来暂存
        out.resize(in.size()); // 调整输出buf大小
        AES_cbc_encrypt((const unsigned char*)in.data(),
                        (unsigned char*)out.data(),
                        in.size(),
                        &aes_key,
                        (unsigned char*)ivecTemp.data(),
                        AES_DECRYPT);

        // 解除PKCS7Padding填充
        out = Padding::PKCS7UnPadding(out);
        return true;
    }
}

加解密过程与ECB模式类似。

加密过程:

  • 生成加密key
  • 进行PKCS7Padding填充
  • 执行加密

解密过程:

  • 生成解密key
  • 执行解密
  • 去除PKCS7Padding填充

经测试,本函数支持对任意长度输入数据进行加解密。

(3)测试代码

void createTestData(QByteArray& data, int size)
{
    data.resize(size);
    for (int i = 0; i < size; i++)
    {
        data[i] = i % 128;
    }
}

void testAES(const QByteArray& data)
{
    QByteArray plainText = data;
    QByteArray encryptText;
    QByteArray decryptText;

    QByteArray key = QByteArray::fromHex("8cc72b05705d5c46f412af8cbed55aad");
    QByteArray ivec = QByteArray::fromHex("667b02a85c61c786def4521b060265e8");

    // AES cbc模式加密验证
    AES aes;
    aes.cbc_encrypt(plainText, encryptText, key, ivec, true);     // 加密
    aes.cbc_encrypt(encryptText, decryptText, key, ivec, false);  // 解密
    qDebug() << "AES cbc encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 产生1MB的测试数据
    QByteArray data;
    createTestData(data, 1*1024*1024);

    // 测试AES
    testAES(data);     // 测试,直接调用OpenSSL中AES算法函数

    return a.exec();
}

执行结果:

在这里插入图片描述

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



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

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

在这里插入图片描述

标签:CBC,实战篇,加密,AES,key,QByteArray,data,加密算法
来源: https://blog.csdn.net/u011832525/article/details/112282293

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有