高级加密标准AES的工作模式(ECB、CBC、CFB、OFB)

高级加密标准AES的工作模式(ECB、CBC、CFB、OFB)_aes加解密cfb模式 12一个简单的使用AES加密一个数据块并解密的例子如下第8行设置128位密钥,第9行使用AES_encrypt对128位的数据块进行加密,第13、14行使用与加密时相同的密钥解密。输出1。 阅读详情

最近在重构之前写的HTTP代理,这个代理是由代理客户端和代理服务端组成的,二者之前使用SSL保证通信内容不会受到中间人(MITM)攻击。而新的实现打算移除SSL,因为SSL握手的开销过大,尤其是客户端与服务端之间隔了个太平洋,另一方面本月中旬的时候Google安全团队证明了SSLv3已经是不安全的了,需要升级到TLS,但TLS同样有握手的开销。在新的实现中客户端和服务端之间的通信将使用AES加密,每个连接使用独立的随机生成的密钥和初始化向量。客户端在向服务端发起连接后使用非对称加密算法RSA将密钥和初始化向量加密后发送给服务端,服务端在收到密钥和初始化向量后就全部使用AES加密通信,这保证了通信内容不会被窃听(但可能被篡改)。
本文作为备忘记录了学习分组加密模式的一些体会和理解。AES作为一种分组加密算法为了适应不同的安全性要求和传输需求允许在多种不同的加密模式下工作,本文只涉及到ECB、CBC、CFB和OFB四种加密模式,以OpenSSL开源库和C++语言作为描述。

高级加密标准AES

高级加密标准(Advanced Encryption Standard: AES)是美国国家标准与技术研究院(NIST)在2001年建立了电子数据的加密规范。它是一种分组加密标准,每个加密块大小为128位,允许的密钥长度为128、192和256位。
下面列出了AES在OpenSSL中最主要的函数

1
2
3
4
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_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key);
void AES_decrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key);

这些函数声明在<openssl/aes.h>文件中,其中AES_set_encryp_key和AES_set_decrypt_key用来为加密器和解密器设置密钥,AES_encrypt用来加密单块数据(128位),AES_decrypt用来解密单块数据。OpenSSL中对ECB、CBC、CFB和OFB等加密模式都是对这两个函数的封装。
为了描述方便引入两个C++函数from_hex_string和to_hex_string前者用来将16进制字符串转为对应的二进制向量,后者用来将二进制向量序列化成16进制的字符串。

1
2
std::vector<unsigned char> from_hex_string(const std::string& hex);
std::string to_hex_string(const std::vector<unsigned char>& vec);

一个简单的使用AES加密一个数据块并解密的例子如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
auto key = from_hex_string("2B7E151628AED2A6ABF7158809CF4F3C");
auto plan_vec = from_hex_string("6BC1BEE22E409F96E93D7E117393172A");
std::vector<unsigned char> cipher_vec(16);
std::vector<unsigned char> decrypt_vec(16);

// aes encrypt
AES_KEY aes_enc_ctx;
AES_set_encrypt_key(key.data(), 128, &aes_enc_ctx);
AES_encrypt(plan_vec.data(), cipher_vec.data(), &aes_enc_ctx);

// aes decrypt
AES_KEY aes_dec_ctx;
AES_set_decrypt_key(key.data(), 128, &aes_dec_ctx);
AES_decrypt(cipher_vec.data(), decrypt_vec.data(), &aes_dec_ctx);

std::cout << "plan   : " << to_hex_string(plan_vec) << std::endl;
std::cout << "cipher : " << to_hex_string(cipher_vec) << std::endl;
std::cout << "decrypt: " << to_hex_string(decrypt_vec) << std::endl;

第8行设置128位密钥2B7E151628AED2A6ABF7158809CF4F3C,第9行使用AES_encrypt对128位的数据块6BC1BEE22E409F96E93D7E117393172A进行加密,第13、14行使用与加密时相同的密钥解密。
输出

1
plan   : 6BC1BEE22E409F96E93D7E117393172A
cipher : 3AD77BB40D7A3660A89ECAF32466EF97
decrypt: 6BC1BEE22E409F96E93D7E117393172A

ECB模式(电子密码本模式:Electronic codebook)

ECB是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。

ECB加密流程(图片来自维基百科)ECB加密流程(图片来自维基百科)

ECB解密流程(图片来自维基百科)ECB解密流程(图片来自维基百科)

OpenSSL中针对ECB模式,有个名为AES_ecb_encrypt的函数,它的实现是这样的(其实就是AES_encrypt和AES_decrypt套了个马甲)。

1
2
3
4
5
6
7
8
void AES_ecb_encrypt(const unsigned char *in, unsigned char *out,const AES_KEY *key, const int enc) {
    assert(in && out && key);
    assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc));
    if (AES_ENCRYPT == enc)
        AES_encrypt(in, out, key);
    else
        AES_decrypt(in, out, key);
}

ECB模式由于每块数据的加密是独立的因此加密和解密都可以并行计算,ECB模式最大的缺点是相同的明文块会被加密成相同的密文块,这种方法在某些环境下不能提供严格的数据保密性。

CBC模式(密码分组链接:Cipher-block chaining)

CBC模式对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密。第一个明文块与一个叫初始化向量的数据块异或。

CBC加密流程(图片来自维基百科)CBC加密流程(图片来自维基百科)

CBC解密流程(图片来自维基百科)CBC解密流程(图片来自维基百科)

OpenSSL中用于AES-CBC加密的函数是AES_cbc_encrypt

1
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:密文输出缓冲区
  • length:明文数据长度(字节)
  • key:当enc为AES_ENCRYPT时此参数要用AES_set_encrypt_key初始化,当enc为AES_DECRYPT时要用AES_set_decrypt_key初始化
  • enc:加密AES_ENCRYPT 解密AES_DECRYPT

AES_cbc_encrypt允许length不是16(128位)的整数倍,不足的部分会用0填充,输出总是16的整数倍。完成加密或解密后会更新初始化向量IV。举例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
auto key = from_hex_string("2B7E151628AED2A6ABF7158809CF4F3C");
auto i_vec = from_hex_string("000102030405060708090A0B0C0D0E0F");
auto plan_vec = from_hex_string("6B");

AES_KEY aes_enc_ctx;
AES_set_encrypt_key(key.data(), 128, &aes_enc_ctx);
std::vector<unsigned char> cipher_vec(16);
AES_cbc_encrypt(plan_vec.data(), cipher_vec.data(), 1, &aes_enc_ctx, i_vec.data(), AES_ENCRYPT);

std::cout << "plan   : " << to_hex_string(plan_vec) << std::endl;
std::cout << "cipher : " << to_hex_string(cipher_vec) << std::endl;
std::cout << "ivec   : " << to_hex_string(i_vec) << std::endl;

AES_KEY aes_dec_ctx;
AES_set_decrypt_key(key.data(), 128, &aes_dec_ctx);
std::vector<unsigned char> decrypt_vec(16);
i_vec = from_hex_string("000102030405060708090A0B0C0D0E0F");
AES_cbc_encrypt(cipher_vec.data(), decrypt_vec.data(), 16, &aes_dec_ctx, i_vec.data(), AES_DECRYPT);

std::cout << "decrypt: " << to_hex_string(decrypt_vec) << std::endl;

输出

1
plan   : 6B
cipher : F05F94CA1B1459C236C2C35A4BCA72ED
ivec   : F05F94CA1B1459C236C2C35A4BCA72ED
decrypt: 6B000000000000000000000000000000

CBC模式相比ECB有更高的保密性,但由于对每个数据块的加密依赖与前一个数据块的加密所以加密无法并行。与ECB一样在加密前需要对数据进行填充,不是很适合对流数据进行加密。

CFB模式(密文反馈:Cipher feedback)

与ECB和CBC模式只能够加密块数据不同,CFB能够将块密文(Block Cipher)转换为流密文(Stream Cipher)。

CFB加密流程(图片来自维基百科)CFB加密流程(图片来自维基百科)

CFB解密流程(图片来自维基百科)CFB解密流程(图片来自维基百科)

注意:CFB、OFB和CTR模式中解密也都是用的加密器而非解密器。
CFB的加密工作分为两部分:

  1. 将一前段加密得到的密文再加密;
  2. 将第1步加密得到的数据与当前段的明文异或。

由于加密流程和解密流程中被块加密器加密的数据是前一段密文,因此即使明文数据的长度不是加密块大小的整数倍也是不需要填充的,这保证了数据长度在加密前后是相同的。
这种模式称为128位的CFB模式(又称CFB128)在OpenSSL中用来进行这种加解密的函数为AES_cfb128_encrypt

1
void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out, size_t length, const AES_KEY *key, unsigned char *ivec, int *num, const int enc)

这个函数中大部分的参数都和AES_cbc_encrypt相同,除了

  • key: 无论是加密还是解密这个key都要用AES_set_encrypt_key初始化
  • num: 用于返回自上次对前一个密文块加密后到函数返回为止已处理的明文数据长度(字节)见下文分析
  • enc: 加密AES_ENCRYPT 解密AES_DECRYPT

刚开始用这个函数的时候一直疑惑于num这个参数,并且Google了下也没找到答案,就直接去源码中看实现了。

1
2
3
4
5
6
7
8
9
10
11
/* The input and output encrypted as though 128bit cfb mode is being
 * used.  The extra state information to record how much of the
 * 128bit block we have used is contained in *num;
 */

void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,
        size_t length, const AES_KEY *key,
        unsigned char *ivec, int *num, const int enc) {

        CRYPTO_cfb128_encrypt(in,out,length,key,ivec,num,enc,(block128_f)AES_encrypt);
}

上面这段代码注释中就是在讲这个参数的用途,但看了还是不明觉厉。下面是CRYPTO_cfb128_encrypt的实现(去掉了无关紧要的代码)。

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
void CRYPTO_cfb128_encrypt(const unsigned char *in, unsigned char *out,
            size_t len, const void *key,
            unsigned char ivec[16], int *num,
            int enc, block128_f block)
{
    unsigned int n;
    size_t l = 0;
    assert(in && out && key && ivec && num);
    n = *num;
    if (enc) {
        while (l<len) {
            if (n == 0) {
                (*block)(ivec, ivec, key);
            }
            out[l] = ivec[n] ^= in[l];
            ++l;
            n = (n+1) % 16;
        }
        *num = n;
    }
    else {
        while (l<len) {
            unsigned char c;
            if (n == 0) {
                (*block)(ivec, ivec, key);
            }
            out[l] = ivec[n] ^ (c = in[l]); ivec[n] = c;
            ++l;
            n = (n+1) % 16;
        }
        *num=n;
    }
}

看到这段代码就很清楚了,128位的CFB是对前一段数据的密文用块加密器加密后保存在IV中,之后用这128位数据与后面到来的128位数据异或,num用来记录自上次调用加密器后已经处理的数据长度(字节),当num重新变为0的时候就会再调用加密器,即每处理128位调用一次加密器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
auto key = from_hex_string("2B7E151628AED2A6ABF7158809CF4F3C");
auto i_vec = from_hex_string("000102030405060708090A0B0C0D0E0F");
auto plan_vec = from_hex_string("6BC1BEE22E409F96E93D7E117393172A52"); // 136 bits 17 bytes

AES_KEY aes_enc_ctx;
AES_set_encrypt_key(key.data(), 128, &aes_enc_ctx);
std::vector<unsigned char> cipher_vec(plan_vec.size());
int num = 0;
for(std::size_t index = 0; index < plan_vec.size(); ++index) {
    AES_cfb128_encrypt(&plan_vec[index], &cipher_vec[index], 1, &aes_enc_ctx, i_vec.data(), &num, AES_ENCRYPT);
    std::cout << "i_vec: " << to_hex_string(i_vec) << " num: " << num << std::endl;
}
std::cout << "cipher : " << to_hex_string(cipher_vec) << std::endl;

AES_KEY aes_dec_ctx;
AES_set_encrypt_key(key.data(), 128, &aes_dec_ctx);
std::vector<unsigned char> decrypt_vec(cipher_vec.size());
i_vec = from_hex_string("000102030405060708090A0B0C0D0E0F"); // reset i_vec
num = 0;
AES_cfb128_encrypt(cipher_vec.data(), decrypt_vec.data(), cipher_vec.size(), &aes_dec_ctx, i_vec.data(), &num, AES_DECRYPT);

std::cout << "decrypt: " << to_hex_string(decrypt_vec) << std::endl;

输出

1
i_vec: 3BFE67CC996D32B6DA0937E99BAFEC60 num: 1
i_vec: 3B3F67CC996D32B6DA0937E99BAFEC60 num: 2
i_vec: 3B3FD9CC996D32B6DA0937E99BAFEC60 num: 3
i_vec: 3B3FD92E996D32B6DA0937E99BAFEC60 num: 4
i_vec: 3B3FD92EB76D32B6DA0937E99BAFEC60 num: 5
i_vec: 3B3FD92EB72D32B6DA0937E99BAFEC60 num: 6
i_vec: 3B3FD92EB72DADB6DA0937E99BAFEC60 num: 7
i_vec: 3B3FD92EB72DAD20DA0937E99BAFEC60 num: 8
i_vec: 3B3FD92EB72DAD20330937E99BAFEC60 num: 9
i_vec: 3B3FD92EB72DAD20333437E99BAFEC60 num: 10
i_vec: 3B3FD92EB72DAD20333449E99BAFEC60 num: 11
i_vec: 3B3FD92EB72DAD20333449F89BAFEC60 num: 12
i_vec: 3B3FD92EB72DAD20333449F8E8AFEC60 num: 13
i_vec: 3B3FD92EB72DAD20333449F8E83CEC60 num: 14
i_vec: 3B3FD92EB72DAD20333449F8E83CFB60 num: 15
i_vec: 3B3FD92EB72DAD20333449F8E83CFB4A num: 0
i_vec: 348BCF60BEB005A35354A201DAB36BDA num: 1
cipher : 3B3FD92EB72DAD20333449F8E83CFB4A34
decrypt: 6BC1BEE22E409F96E93D7E117393172A52

CFB128是每处理128位数据调用一次加密器,此外还有两种常用的CFB是CFB8和CFB1,前者每处理8位调用一次加密器,后者每处理1位调用1次加密器,就运算量来讲CFB1是CFB8的8倍,是CFB128的128倍。对于CFB8和CFB1需要将IV作为移位寄存器。

CFB8的加密流程
  1. 使用加密器加密IV的数据;
  2. 将明文的最高8位与IV的最高8位异或得到8位密文;
  3. 将IV数据左移8位,最低8位用刚刚计算得到的8位密文补上。

重复1到3。

CFB1的加密流程
  1. 使用加密器加密IV的数据;
  2. 将明文的最高1位与IV的最高1位异或得到1位密文;
  3. 将IV数据左移1位,最低1位用刚刚计算得到的1位密文补上。

重复1到3。

OpenSSL中AES_cfb8_encrypt和AES_cfb1_encrypt分别用来加解密CFB8和CFB1。这两个函数的参数和AES_cfb128_encrypt完全一样,但num和length含义略有不同。

  • num: 应总是为0 否则会触发断言
  • length: CFB8单位为byte CFB1单位为bit
    CFB模式非常适合对流数据进行加密,解密可以并行计算。

OFB模式(输出反馈:Output feedback)

OFB是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的。

OFB加密流程(图片来自维基百科)OFB加密流程(图片来自维基百科)
OFB解密流程(图片来自维基百科)OFB解密流程(图片来自维基百科)

OpenSSL中用来加解密AES-OFB的函数是AES_ofb128_encrypt其参数和各参数的含义与AES_cfb128_encrypt完全一样,但由于OFB加密和解密是对称的所以没有参数enc。
OFB与CFB一样都非常适合对流数据的加密,OFB由于加密和解密都依赖与前一段数据,所以加密和解密都不能并行。

C语言实现AES加密解密 AES加密是美国联邦政府采用的一种块加密标准,如今已经被全世界广为使用。嵌入式开发中我们也经常会用到加密解密算法,如果没有硬件模块来实现,就需要用到C代码软件实现。下面介绍调用mbedTLS中的AES加密解密函数实现AES算法。 mbedTLS是一个开源TLS协议栈,主要用于嵌入式开发,其源代码网址为https://tls.mbed.org/aes-source-code。在该页面上点击downloadmbedTLS即可下载最新的协议栈,解压该压缩包就可以得到协议栈源代码。协议栈中的各种算法都独立封装在C 阅读详情

相关推荐

用Python实现AES加密和解密

一、前言 AES,高级加密标准(英语:Advanced Encryption Standard)。是用来替代DES,目前比较流行的加密算法。 它是一种对称加密算法,与上一篇博文提到过的RSA非对称算法不同,AES只有一个密钥,这个密钥既用来加密,也用于解密。 AES只是个基本算法,实现AES有几种模式,主要有ECB、CBC、CFB和OFB这几种(其实还有个CTR): 1.ECB模式(电

真新镇的涅法雷姆 1万+

什么是 AES-GCM加密算法

AES是一种对称加密算法,它的相关概念在此不赘述。 GCM ( Galois/Counter Mode) 是对称加密的一种加密模式。 在介绍AES-GCM之前,我们先了解一些相关概念。 下文中出现的符号: Ek 使用秘钥k对输入做对称加密运算 XOR 异或运算 Mh 将输入与秘钥h在有限域GF(2^128)上做乘法

T0mato_的博客 25万+

AES高级加密的工作模式(ECB、CBC、CFB、OFB)

高级加密标准(Advanced Encryption Standard: AES)是美国国家标准与技术研究院(NIST)在2001年建立了电子数据的加密规范。它是一种分组加密标准,每个加密块大小为128位,允许的密钥长度为128、192和256位。 ECB模式(电子密码本模式:Electronic codebook) ECB是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分...

知道的越多,不知道的越多。 1716

什么标准规定了aes加密_高级加密标准AES的工作模式(ECB、CBC、CFB、OFB)

高级加密标准(Advanced Encryption Standard: AES)是美国国家标准与技术研究院(NIST)在2001年建立了电子数据的加密规范。它是一种分组加密标准,每个加密块大小为128位,允许的密钥长度为128、192和256位。ECB模式(电子密码本模式:Electronic codebook)ECB是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分成若干块...

weixin_39609622的博客 1224

分组密码与高级加密标准(三)

AES具有128比特的分组长度,三种可选的密钥长度,即128比特、192比特和256比特。如果密钥长度为256比特,则Nr=14。下面是DES的四种工作模式:电码本模式( ECB 模式)、密码反馈模式( CFB 模式)、密码分组链接模式( CBC 模式)、输出反馈模式( OFB 模式)。现在,AES的工作模式正在研发,这些AES的工作模式可能会包括以前DES的工作模式,还有可能包括新加的工作模式。根据提示,补全右侧编辑器中 Begin-End 区间的代码,根据明密文对的个数,计算密钥比特的个数并打印输出。

Junds0的博客 920

AES(ECB、CBC、CFB、CTR)128/192/256加密算法C语言实现

1、完整的AES(ECB、CBC、CFB、CTR)算法,C语言实现,可用于金融POS安全认证; 2、AES-128、AES-192、AES-256加密/解密 3、内含测试程序,在Linux环境下进入目录后make即可编译,已经在ubuntu16.04环境下编译测试OK;

浅析AES加密算法

1、AES简介 高级加密标准(Advanced Encryption Standard, 简称AES)是最为常见的一种对称加密算法,其加密过程涉及到4种操作:字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。 其解密过程为别为对应的逆操作。由于每一步操作都是可逆的,按照相反的顺序进行解密即可恢复明文。 上面提到的对称加密算法是一种加密、解密使用相同密钥的加密算法,其特点是算法公开、计算量小、加密速度快、加密效率高。对称加密算法具体

进击の程序猿! 的博客 4207

CANoe软件之离线报文回放与分析实战

本文详细介绍了如何使用CANoe软件进行离线报文回放与分析,帮助工程师将现场故障数据转化为可反复研究的实验室资源。通过实战步骤,指导如何搭建分析工程、加载DBC文件解码信号,并利用Trace和Graphics窗口进行深度数据筛选与图形化分析,从而高效定位汽车电子故障,是提升诊断效率的关键技能。

weixin_29227585的博客 529

思科ei ccie里DES和AES加密原理必看

思科ei ccie里DES和AES加密原理必看,AES (Advanced Encryption Standard,高级加密标准)是美国国家标准与技术研究院(NIST)在2001年建立了电子数据的加密规范。它是一种分组加密标准,每个加密数据块大小固定为128位(16个字节),最终生成的加密密钥长度有128位、192位和256位这三种。另外,AES主要有五种工作模式(其实还有很多模式) :ECB (Electroniccodebook,电子密码本)、CBC (Cipher-block chaining,密码分

IE-lab网络实验室的博客 967

FOC实战笔记:RC吸收电路的设计考量与参数调优

本文深入探讨了FOC驱动器中RC吸收电路的设计与调优。针对MOS管开关过程中的电压尖峰问题,详细阐述了RC电路的钳位与能量吸收原理,并提供了从参数计算、实验调试到布局布线的完整实战指南,旨在帮助工程师有效提升系统可靠性与稳定性。

weixin_29219539的博客 276

全覆盖路径规划开源项目Clean-robot-turtlebot3关键函数解析(上)

本系列文章主要介绍全覆盖路径规划开源项目Clean-robot-turtlebot3的相关内容,包含如下四篇文章,分别介绍了开源项目Clean-robot-turtlebot3流程的概括总结、ROS坐标系常用坐标及其变换、Clean-robot-turtlebot3关键函数解析等内容。

慕羽★的博客 2778

AES加密原理

AES (Advanced Encryption Standard,高级加密标准)是美国国家标准与技术研究院(NIST)在2001年建立了电子数据的加密规范。它是一种分组加密标准,每个加密数据块大小固定为128位(16个字节),最终生成的加密密钥长度有128位、192位和256位这三种。另外, AES主要有五种工作模式(其实还有很多模式) :ECB (Electroniccodebook,电子密码本)、CBC (Cipher-block chaining,密码分组链接)、CFB (Cipher feedba

jiamisoft的博客 7448
上一篇: 原来java中也有类似goto语句的标签啊--java label标签(一)
下一篇: SM4密码算法(附源码)
ly0303521
博客等级 码龄17年 135粉丝 90原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值