首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 23-09-19 13:42
  • 4667
  • 9685
blog.csdn.net

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

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

  • 一、AES简介
  • 二、ECB模式
    • 1、命令行操作
    • 2、函数说明
    • 3、编程实现
      • (1)PKCS7填充方式
      • (2)实现ECB模式加解密
      • (3)测试代码

一、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开源库。在命令行中,我们可以使用命令实现对文件加解密,以验证我们的编程实现,是否正确。

二、ECB模式

电子密码本模式 Electronic Code Book(ECB)。这种模式是最早采用和最简单的模式,它将加密的数据分成若干组,每组的大小跟加密 密钥长度相同,然后每组都用相同的密钥进行加密。

其缺点是:电子编码薄模式用一个密钥加密消息的所有块,如果原消息中重复明文
块,则加密消息中的相应密文块也会重复,因此,电子编码薄模式适于加密小消息。

1、命令行操作

使用aes-128-ecb对hello.txt加密,128位密钥为8cc72b05705d5c46f412af8cbed55aad,密文为hello.en。

openssl enc -e -aes-128-ecb -in hello.txt -out hello.en -K 8cc72b05705d5c46f412af8cbed55aad
  • 1

使用aes-128-ecb对hello.en解密,128位密钥为8cc72b05705d5c46f412af8cbed55aad,解密后的文件为hello.de。

openssl enc -d -aes-128-ecb -in hello.en -out hello.de -K 8cc72b05705d5c46f412af8cbed55aad
  • 1

2、函数说明

生成加密/解密的Key:

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);
  • 1
  • 2
参数名称含义
userKey用户指定的密码。注意:只能是16/24/32字节。如果密码字符串长度不够,可以在字符串末尾追加一些特定的字符,或者重复密码字符串,直到满足最少的长度。
bits密码位数。即userKey的长度 * 8,只能是128/192/256位。
key向外输出参数
返回值0 - 成功; -1 - userkey,key为空;-2 - 密钥长度不是128/192/256

AES ECB加密/解密:

void AES_ecb_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key, const int enc);
  • 1
参数名称含义
in输入数据,长度固定为16字节
out输出数据,长度与输入数据一致,固定为16字节
key使用AES_set_encrypt_key/AES_set_decrypt_key生成的Key
encAES_ENCRYPT 代表加密, AES_DECRYPT代表解密

3、编程实现

由于ECB模式,每次只能处理一个块的数据,即16字节,所以如果需要处理任意长度的数据,那么需要在原始数据末尾,先进行填充,使得数据长度为16的整数倍,随后再分块进行加密。解密时,也需要分块解密,最后将解密后的数据,进行取消填充。

(1)PKCS7填充方式

AES支持多种填充方式:如NoPadding、PKCS5Padding、ISO10126Padding、PKCS7Padding、ZeroPadding。密文长度与填充方式关系,可参考《AES加密模式和填充模式》。

PKCS7填充方式:

假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小。

举个例子最直观,这里以块大小为16字节,进行PKCS7填充,如下:

数据1: {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}
填充后:{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06}
尾部填充了6个6,填充后数据长度为16。

数据2: {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}
填充后:{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}
尾部填充了16个16,填充后数据长度为32。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Padding类实现了上述的PKCS7填充功能,如下:

Padding.h

#ifndef PADDING_H
#define PADDING_H

#include 

/**
 * @brief The Padding class
 * 算法数据填充模式,提供对数据进行PKCS7填充和去除填充的相关函数。
 */
class Padding
{
public:
    static int getPKCS7PaddedLength(int dataLen, int alignSize);
    static QByteArray PKCS7Padding(const QByteArray &in, int alignSize);
    static QByteArray PKCS7UnPadding(const QByteArray &in);
};

#endif // PADDING_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Padding.cpp

#include "Padding.h"

/**
 * @brief Padding::getPKCS7PaddedLength
 * 根据原始数据长度,计算进行PKCS7填充后的数据长度
 * @param dataLen 原始数据长度
 * @param alignSize 对齐字节数
 * @return 返回填充后的数据长度
 */
int Padding::getPKCS7PaddedLength(int dataLen, int alignSize)
{
    // 计算填充的字节数(按alignSize字节对齐进行填充)
    int remainder = dataLen % alignSize;
    int paddingSize = (remainder == 0) ? alignSize : (alignSize - remainder);
    return (dataLen + paddingSize);
}

/**
 * @brief Padding::PKCS7Padding
 * 采用PKCS7Padding方式,将in数据进行alignSize字节对齐填充。
 * 此函数用于加密前,对明文进行填充。
 * @param in 数据
 * @param alignSize 对齐字节数
 * @return 返回填充后的数据
 */
QByteArray Padding::PKCS7Padding(const QByteArray &in, int alignSize)
{
    // 计算需要填充字节数(按alignSize字节对齐进行填充)
    int remainder = in.size() % alignSize;
    int paddingSize = (remainder == 0) ? alignSize : (alignSize - remainder);

    // 进行填充
    QByteArray temp(in);
    temp.append(paddingSize, paddingSize);
    return temp;
}

/**
 * @brief Padding::PKCS7UnPadding
 * 采用PKCS7Padding方式,将in数据去除填充。
 * 此函数用于解密后,对解密结果进一步去除填充,以得到原始数据。
 * @param in 数据
 * @return 返回去除填充后的数据
 */
QByteArray Padding::PKCS7UnPadding(const QByteArray &in)
{
    char paddingSize = in.at(in.size() - 1);
    return in.left(in.size() - paddingSize);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

代码比较简单,注释也说的很明白啦。

(2)实现ECB模式加解密

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

#include "AES.h"
#include 
#include 
#include "Padding.h"

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

    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);

        // 执行ECB模式加密
        out.resize(inTemp.size()); // 调整输出buf大小
        for (int i = 0; i < inTemp.size() / AES_BLOCK_SIZE; i++)
        {
            AES_ecb_encrypt((const unsigned char*)inTemp.data() + AES_BLOCK_SIZE * i,
                            (unsigned char*)out.data() + AES_BLOCK_SIZE * i,
                            &aes_key,
                            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;
        }

        // 执行ECB模式解密
        out.resize(in.size()); // 调整输出buf大小
        for (int i = 0; i < in.size() / AES_BLOCK_SIZE; i++)
        {
            AES_ecb_encrypt((const unsigned char*)in.data() + AES_BLOCK_SIZE * i,
                            (unsigned char*)out.data() + AES_BLOCK_SIZE * i,
                            &aes_key,
                            AES_DECRYPT);
        }

        // 解除PKCS7Padding填充
        out = Padding::PKCS7UnPadding(out);
        return true;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

加密过程:先通过AES_set_encrypt_key函数生成加密key,然后将输入数据进行PKCS7填充,保证得到16字节整数倍明文,ECB模式密文长度等于明文长度,故将输出缓冲out调整为明文大小,以保存密文数据,最后,将明文按16字节,分块进行加密。

解密过程:先通过AES_set_decrypt_key函数生成解密key,由于输入数据,即密文本身就是16字节整数倍,故直接按16字节进行分块解密,并将解密后的数据,进行去除填充,得到真正的明文。

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

(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");

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

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

    // 产生1MB+3B的测试数据,为了使该测试数据长度,不为8或16的整数倍
    QByteArray data;
    createTestData(data, 1*1024*1024+3);

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

    return a.exec();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

执行结果:

在这里插入图片描述

本文涉及工程代码地址:http://iyenn.com/index/link?url=https://gitee.com/bailiyang/cdemo/tree/master/Qt/49OpenSSL/OpenSSL



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

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

在这里插入图片描述

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览50756 人正在系统学习中
注:本文转载自blog.csdn.net的百里杨的文章"https://blog.csdn.net/zyhse/article/details/112278849"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top