AES加密

信息安全技术

Posted by Birdie on May 2, 2022

零 实验环境

​ 操作系统:Ubuntu 20.04

​ 程序语言:C++

​ 编译环境:gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)

​ 编码方式:utf-8

一 实验目标

​ 学习一个能够实现典型分组密码,如DES和AES的密码软件库,简单介绍它的功能,以及它在分组密码的工作方式和填充模式上的设置方法,以AES为例,给出实现不同工作模式和填充模式的加解密源代码。

二 实验内容

2.1 软件库——OpenSSL

2.1.1 基本功能

​ OpenSSL整个软件包大概可以分成三个主要的功能部分:SSL协议库、应用程序以及密码算法库。OpenSSL的目录结构自然也是围绕这三个功能部分进行规划的。

​ 作为一个基于密码学的安全开发包,OpenSSL提供的功能相当强大和全面,囊括了主要的密码算法、常用的密钥和证书封装管理功能以及SSL协议,并提供了丰富的应用程序供测试或其它目的使用。

2.1.2 辅助功能

​ BIO机制是OpenSSL提供的一种高层IO接口,该接口封装了几乎所有类型的IO接口,如内存访问、文件访问以及Socket等。这使得代码的重用性大幅度提高,OpenSSL提供API的复杂性也降低了很多。

​ OpenSSL对于随机数的生成和管理也提供了一整套的解决方法和支持API函数。随机数的好坏是决定一个密钥是否安全的重要前提。

​ OpenSSL还提供了其它的一些辅助功能,如从口令生成密钥的API,证书签发和管理中的配置文件机制等等。

2.1.3 对称加密

​ OpenSSL一共提供了8种对称加密算法,其中7种是分组加密算法,仅有的一种流加密算法是RC4。这7种分组加密算法分别是AES、DES、Blowfish、CAST、IDEA、RC2、RC5,都支持电子密码本模式(ECB)、加密分组链接模式(CBC)、加密反馈模式(CFB)和输出反馈模式(OFB)四种常用的分组密码加密模式。其中,AES使用的加密反馈模式(CFB)和输出反馈模式(OFB)分组长度是128位,其它算法使用的则是64位。事实上,DES算法里面不仅仅是常用的DES算法,还支持三个密钥和两个密钥3DES算法。

2.1.4 AES加解密API

(包含其在 AES 的工作方式和填充模式上的设置方法)


Int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key)

功能:用于生成加密密钥。

参数:

  • const unsigned char *userKey:密钥字符串

  • const int bits:密钥长度,以bit为单位,如果密钥数字是16个字节,则此参数值应为128

  • AES_KEY *key:AES_KEY对象指针,用于接收生成的加密密钥

返回值:

0:成功; -1:userkey,key为空;-2:密钥长度不是128,192,256


int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key)

功能:用于生成解密密钥。

参数:

  • const unsigned char *userKey:密钥字符串

  • const int bits:密钥长度,以bit为单位,如果密钥数字是16个字节,则此参数值应为128

  • AES_KEY *key:AES_KEY对象指针,用于接收生成的解密密钥

返回值:

0:成功; -1:userkey,key为空;-2:密钥长度不是128,192,256


void AES_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key)

功能:加密数据块。

参数:

  • const unsigned char *in:明文数据

  • unsigned char *out:密文数据(可以与in指向同一块内存区域,则密文会覆盖明文)

  • const AES_KEY *key:AES_KEY对象指针,加密密钥


void AES_decrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key)

功能:解密数据块。

参数:

  • const unsigned char *in:密文数据

  • unsigned char *out:明文数据(可以与in指向同一块内存区域,则明文会覆盖密文)

  • const AES_KEY *key:AES_KEY对象指针,解密密钥


void AES_ecb_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key, const int enc)

功能:以ECB模式加密/解密数据块。

参数:

  • const unsigned char *in:输入数据(加密时为明文,解密时为密文)

  • unsigned char *out:输出数据(加密时为密文,解密时为明文)

  • const AES_KEY *key:AES_KEY对象指针,加密/解密密钥

  • const int enc:加解密模式(AES_ENCRYPT 代表加密, AES_DECRYPT代表解密)


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

功能:以CBC模式加密/解密数据块。

参数:

  • const unsigned char *in:输入数据(加密时为明文,解密时为密文)
  • unsigned char *out:输出数据(加密时为密文,解密时为明文)
  • size_t length:数据块长度(单位为字节)
  • const AES_KEY *key:AES_KEY对象指针,加密/解密密钥
  • unsigned char *ivec:初始向量
  • const int enc:加解密模式(AES_ENCRYPT 代表加密, AES_DECRYPT代表解密)

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)

功能:以CFB128位模式加密/解密数据块。

参数:

  • const unsigned char *in:输入数据(加密时为明文,解密时为密文)
  • unsigned char *out:输出数据(加密时为密文,解密时为明文)
  • size_t length:数据块长度(单位为字节)
  • const AES_KEY *key:AES_KEY对象指针,加密/解密密钥
  • unsigned char *ivec:初始向量
  • int *num:输出参数,计数加密的CFB数据块个数
  • const int enc:加解密模式(AES_ENCRYPT 代表加密, AES_DECRYPT代表解密)

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

功能:以CFB1位模式加密/解密数据块。

参数:

  • const unsigned char *in:输入数据(加密时为明文,解密时为密文)
  • unsigned char *out:输出数据(加密时为密文,解密时为明文)
  • size_t length:数据块长度(单位为字节)
  • const AES_KEY *key:AES_KEY对象指针,加密/解密密钥
  • unsigned char *ivec:初始向量
  • int *num:输出参数,计数加密的CFB数据块个数
  • const int enc:加解密模式(AES_ENCRYPT 代表加密, AES_DECRYPT代表解密)

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

功能:以CFB8位模式加密/解密数据块。

参数:

  • const unsigned char *in:输入数据(加密时为明文,解密时为密文)
  • unsigned char *out:输出数据(加密时为密文,解密时为明文)
  • size_t length:数据块长度(单位为字节)
  • const AES_KEY *key:AES_KEY对象指针,加密/解密密钥
  • unsigned char *ivec:初始向量
  • int *num:输出参数,计数加密的CFB数据块个数
  • const int enc:加解密模式(AES_ENCRYPT 代表加密, AES_DECRYPT代表解密)

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

功能:以OFB128位模式加密/解密数据块。

参数:

  • const unsigned char *in:输入数据(加密时为明文,解密时为密文)
  • unsigned char *out:输出数据(加密时为密文,解密时为明文)
  • size_t length:数据块长度(单位为字节)
  • const AES_KEY *key:AES_KEY对象指针,加密/解密密钥
  • unsigned char *ivec:初始向量
  • int *num:输出参数,计数加密的OFB数据块个数

2.1.5 DES及其他分组密码设置方法

​ DES及其他分组密码设置方法和AES的设置方法大同小异,OpenSSL在分组密码的工作方式和填充模式上的设置方法具体来说,就是调用不同的函数,每个工作方式和填充模式对应着不同接口的函数,传入正确的参数即可调用。为避免篇幅冗余,在此不一一列举。

​ 在此给出查找OpenSSL的API的文档的地址:https://wiki.openssl.org/index.php/API。

2.2 AES加解密

2.2.0 前置知识

​ 素域、扩展域(具体解释见《近世代数》)。

​ 符号表示:$GF(2^m)$表示模$2^m$意义下的扩展域。

2.2.1 分组密码简述

​ 分组分组顾名思义就是将明文消息分成组来进行加密,也就是说,加密器每次只能处理特定长度的一组数据。简单来说,如果明文和密文的分组长度都为 n 比特, 则明文的每一个分组都有2的 n 次方个可能的取值。为保证加密后得到的密文可以通过解密运算还原成为明文消息,明文的每一个分组都应产生唯一的一个密文分组。

2.2.2 AES概述

​ AES 加密算法是代换-置换网络的一种经典算法,其加密过程涉及 $4$ 种操作:字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。AES 加解密的流程如下:

从图中可以看出:

  1. 解密算法的每一步分别对应加密算法的逆操作;
  2. 加解密所有操作的顺序正好是相反的;
  3. 加密算法与解密算法每步的操作互逆

​ 正是由于这几点才得以保证了算法的正确性。加解密中每轮的密钥分别由种子密钥经过密钥扩展算法得到。由于 AES 是分组密码,所以实际上在进行加解密的时候都是分成 16 字节一组进行的。算法中 16 字节的明文、密文和轮子密钥都以一个4 x 4的矩阵表示。在 AES 当中的计算都是在 $GF(2^8)$ 的有限域下进行的,其模数是 $x^8 + x^4 + x^3 + x + 1$。下面我们就按着字节替代,行位移,列混淆和轮密钥加的顺序一一进行讲解。

字节替代

​ 字节代替提供了算法的混淆性

​ 字节代替的主要功能是通过 S 盒完成一个字节到另外一个字节的映射,S 盒的详细构造方法如下。首先我们对于我们有限域内的数 $(a_7,a_6,a_5,a_4,a_3,a_2,a_1,a_0)$,我们先求解出它的逆 $(a’7,a’_6,a’_5,a’_4,a’_3,a’_2,a’_1,a’_0)$,然后我们定义 $(c_7,c_6,c_5,c_4,c_3,c_2,c_1,c_0)$ 为 $(0,1,1,0,0,0,1,1)$。则我们通过 $S$ 盒产生的数为 $(b_7,b_6,b_5,b_4,b_3,b_2,b_1,b_0)$,其中 $b_i = a_i \bigoplus a{i + 4} \bigoplus a_{i + 5} \bigoplus a_{i + 6} \bigoplus a_{i + 7} \bigoplus c_i$,其中这里的下标都是在模 8 意义下的。

​ 而由于 S 盒逆是 S 盒的逆变换,我们先考虑简单的一维情况,对于 $a[i]$ 是一组排列,其逆 $Inva[i]$ 必然是 $Inva[a[i]] = i$。而我们拓展到二维的情况,由于我们的 S 盒和 S 盒逆是 16 x 16 的,所以对于 S 盒 $S[i][j] = m$,其逆必然有 $InvS[m / 16][m \% 16] = i * 16 + j$。

​ 下面给出S 盒和 S 盒逆的一个例子:

其工作机制如下:

行位移

​ 行位移提供了算法的扩散性

​ 行移位是一个4 x 4的矩阵内部字节之间的置换,具体分为正向行位移和逆向行位移。

​ 正向行移位用于加密:第一行保持不变,第二行循环左移 8 比特,第三行循环左移 16 比特,第四行循环左移 24 比特。而逆向行位移则用于解密,和正向行位移恰好相反。第一行保持不变,第二行循环右移 8 比特,第三行循环右移 16 比特,第四行循环右移 24 比特。

其工作机制如下:

列混淆

​ 列混淆同样用于提供算法的扩散性。列混淆利用 $GF(2^8)$ 优先域上算术特性的一个代替,以下所有运算都在 $GF(2^8)$ 下进行。

​ 列混淆也分为正向列混淆和逆向列混淆。正向列混淆是用于加密,其方法为左乘一个矩阵 A:

​ 逆向列混淆是用于解密,其方法为左乘一个矩阵 $A^{-1}$:

轮密钥加

​ 算法原理是任何数和自身的异或结果为 0。加密过程中,每轮的输入与轮子密钥异或一次;因此,解密时再异或上该轮的轮子密钥即可恢复。 一共要做 10 轮,所以一共需要 11 个轮密钥,在算法的最开始的时候只有一个初始密钥,运用密钥扩展算法依次生成 $10$ 个新的密钥。其具体生成步骤如下:

  1. 将种子密钥按下图 $(a)$ 的格式排列,其中 $k_0$、$k_1$、……、$k_{15}$ 依次表示种子密钥的一个字节;排列后用 $4$ 个 $32$ 比特的字表示,分别记为 $w[0]$、$w[1]$、$w[2]$、$w[3]$;

  2. 按照如下方式,依次求解 $w[j]$,其中 $j$ 是整数并且属于 $[4,43]$;

  3. 若 $j \% 4 = 0$,则 $w[j]=w[j-4]\bigoplus g(w[j-1])$,否则 $w[j]=w[j-4]\bigoplus w[j-1]$,在这里函数g的流程说明如下:

    1. 将w循环左移8比特。
    2. 分别对每个字节做S盒置换。
    3. 与32比特的常量($RC[j/4],0,0,0$)进行异或。$RC$ 是一个一维数组,其值如下:RC = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36}。

2.2.3 填充模式

​ 如果明文的长度不是128bit的倍数,就会存在一个分块不足128bit,那如何对这个分块进行加密?因此,需要对明文进行填充。

​ AES中常用的 6 种填充:NoPadding,PKCS5/PKCS7Padding,ISO10126Padding,ANSIX923Padding,ZerosPadding。

假定块长度为 8,数据长度为 9,则填充用八位字节数等于 7,数据等于 FF FF FF FF FF FF FF FF FF。

填充算法 说明 填充后
NoPadding 不填充 FF FF FF FF FF FF FF FF FF
PKCS5/PKCS7 PKCS #7 填充字符串由一个字节序列组成,每个字节填充该字节序列的长度。 FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07
ISO10126 ISO10126 填充字符串由一个字节序列组成,此字节序列的最后一个字节填充字节序列的长度,其余字节填充随机数据。 FF FF FF FF FF FF FF FF FF 7D 2A 75 EF F8 EF 07
ANSIX923 ANSIX923 填充字符串由一个字节序列组成,此字节序列的最后一个字节填充字节序列的长度,其余字节均填充数字零。 FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 07
Zeros 填充字符串由设置为零的字节组成。 FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00

2.2.4 工作模式

​ 分组密码加密有五种模式:

  1. 电码本模式(Electronic Codebook Book (ECB) )
  2. 密码分组链接模式(Cipher Block Chaining (CBC) )
  3. 计算器模式(Counter (CTR) )
  4. 密码反馈模式(Cipher FeedBack (CFB) )
  5. 输出反馈模式(Output FeedBack (OFB) )。
加密模式 方式 优点 缺点
电码本模式(ECB) 将整个明文分成若干段相同的小段,然后对每一小段进行加密。 简单,可并行,不传送误差。 可对明文进行主动攻击。
密码分组链接模式(CBC) 先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。 能掩盖明文结构信息,保证相同密文可得不同明文,所以不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL和IPSec的标准。 不利于并行计算;传递误差——前一个出错则后续全错;第一个明文块需要与一个初始化向量IV进行抑或,初始化向量IV的选取比较复杂。
输出反馈模式(OFB) 密码算法的输出(指密码key而不是密文)会反馈到密码算法的输入中,OFB模式并不是通过密码算法对明文直接加密,而是通过将明文分组和密码算法的输出进行XOR来产生密文分组。 隐藏了明文模式;结合了分组加密和流密码(分组密码转化为流模式);可以及时加密传送小于分组的数据。 不利于并行计算;需要生成秘钥流;对明文的主动攻击是可能的。
计数器模式(CTR) 完全的流模式。将瞬时值与计数器连接起来,然后对此进行加密产生密钥流的一个密钥块,再进行XOR操作 。 不泄露明文;仅需实现加密函数;无需填充;可并行计算。 需要瞬时值IV,难以保证IV的唯一性。
密码反馈模式(CFB) 把分组密码当做流密码使用,即密码反馈模式可将DES分组密码置换成流密码。流密码具有密文和明文长度一致、运行实时的性质,这样数据可以在比分组小得多的单元里进行加密。如果需要发送的每个字符长为8比特,就应使用8比特密钥来加密每个字符。如果长度超过8比特,则造成浪费。但是要注意,由于CFB模式中分组密码是以流密码方式使用,所以加密和解密操作完全相同,因此无法适用于公钥密码系统,只能适用于对称密钥密码系统。 可以处理任意长度的消息,能适应用户不同数据格式的需要。可实现自同步功能。就有有限步的错误传播,除能获得保密性外,还可用于认证。 对信道错误较敏感,且会造成错误传播。数据加密的速率被降低。

2.3 代码实现

2.3.1 设置初始向量

#define AESIV "2841ae97419c2973296a0d4bdfe19a4f"

2.3.2 输入密钥并初始化

AES_KEY encryptkey;
AES_KEY decryptkey;

unsigned char *key;
unsigned char *stdiv;

// 输入密钥
char AESKEY[255];
printf("Input the AES key(length must be 16, 24, or 32) : \n");
scanf("%s", AESKEY);
// 密钥长度必须满足16 or 24 or 32
if (strlen(AESKEY) != 16 && strlen(AESKEY) != 24 && strlen(AESKEY) != 32) {
    printf("the length of AES key must be 16, 24, or 32!\n");
    return 0;
}
// 将密钥转换成hex形式
key = str2hex(AESKEY);
stdiv = str2hex(AESIV);
// 生成加密密钥和解密密钥
AES_set_encrypt_key(key, 128, &encryptkey);
AES_set_decrypt_key(key, 128, &decryptkey);
// 过滤掉没有读入的回车
char unl; scanf("%c", &unl);

2.3.3 读取原文

​ 原文的长度必须是16的倍数,如果不满足,则需要填充至16的倍数。最后原文的总长度为:(L / 16 + 1) * 16。

// 读取原文
unsigned char plain_text[255];

char str[255];
printf("Input the plain text(Maximal Length should not exceed 255) : \n");
scanf("%[^\n]", str);
// 原文长度必须是16的倍数,计算所需填充长度
int L = strlen(str);
int rL = (L / 16 + 1) * 16;
memcpy(plain_text, str, L);
puts("\n");

2.3.4 填充原文

​ 设计了5种填充模式:no-padding、zero-padding、PKCS5/PKCS7 、ISO10126以及 ANSIX923。

printf("Choose the Padding Model : \n");
printf("0 -- no-padding  1 -- zero-padding  2 -- PKCS5/PKCS7  3 -- ISO10126  4 -- ANSIX923\n");
int opt; scanf("%d", &opt);
if (opt == 0) {
    rL = L;
} else if (opt == 1) {
    memset(plain_text + L, 0, rL - L);
} else if (opt == 2) {
    for (int i = L; i < rL; i++)
        plain_text[i] = rL - L;
} else if (opt == 3) {
    srand(time(0));
    for (int i = L; i < rL - 1; i++)
        plain_text[i] = rand() % 256;
    plain_text[rL - 1] = rL - L;
} else if (opt == 4) {
    memset(plain_text + L, 0, rL - L);
    plain_text[rL - 1] = rL - L;
} else {
    puts("No this model!");
    return 0;
}
puts("\n");

2.3.5 进行加密操作

​ 设置了5种加密模式:ECB、CBC、OFB128、CFB128、CFB8和CFB1。

需要注意的是:

  • CFB模式加密和解密均使用加密key,这一点比较反常,务必记住。
  • CFB模式不需要对输入数据进行填充。
  • AES_cfb128_encrypt函数length参数,为输入数据长度,字节数。这一点与CFB1模式中有所不同。
  • AES_cfb1_encrypt函数length参数,为输入数据的位数,即输入数据长度*8,而不是字节数。
  • AES_ofb128_encrypt函数既是加密,又是解密。当in为明文时,执行的是加密操作;当in为密文时,执行的是解密操作,相当于是对等的。
printf("Choose the Encrypted Model : \n");
printf("1 -- ECB  2 -- CBC  3 -- OFB128  4 -- CFB128  5 -- CFB8  6 -- CFB1\n");
scanf("%d", &opt);
if (opt == 1)
    AES_ecb_encrypt(plain_text, encrypted_text, &encryptkey, AES_ENCRYPT);
else if (opt == 2)
    AES_cbc_encrypt(plain_text, encrypted_text, rL, &encryptkey, tmpiv, AES_ENCRYPT);
else if (opt == 3) {
    if (rL != L) {
        printf("CFB128 model must use no-padding!\n");
        return 0;
    }
    int* num = (int*)malloc(sizeof(int));
    AES_ofb128_encrypt(plain_text, encrypted_text, rL, &encryptkey, tmpiv, num);
}
else if (opt == 4) {
    if (rL != L) {
        printf("CFB128 model must use no-padding!\n");
        return 0;
    }
    int* num = (int*)malloc(sizeof(int));
    AES_cfb128_encrypt(plain_text, encrypted_text, rL, &encryptkey, tmpiv, num, AES_ENCRYPT);
} else if (opt == 5) {
    if (rL != L) {
        printf("CFB8 model must use no-padding!\n");
        return 0;
    }
    int* num = (int*)malloc(sizeof(int));
    AES_cfb8_encrypt(plain_text, encrypted_text, rL, &encryptkey, tmpiv, num, AES_ENCRYPT);
} else if (opt == 6) {
    if (rL != L) {
        printf("CFB1 model must use no-padding!\n");
        return 0;
    }
    int* num = (int*)malloc(sizeof(int));
    AES_cfb1_encrypt(plain_text, encrypted_text, rL * 8, &encryptkey, tmpiv, num, AES_ENCRYPT);
} else {
    puts("No this model!");
    return 0;
}
puts("\n");

2.3.6 解密验证结果

unsigned char decrypted_text[255];
memset(decrypted_text, 0, rL);
memcpy(tmpiv, stdiv, 16);
if (opt == 1)
    AES_ecb_encrypt(encrypted_text, decrypted_text, &decryptkey, AES_DECRYPT);
else if (opt == 2)
    AES_cbc_encrypt(encrypted_text, decrypted_text, rL, &decryptkey, tmpiv, AES_DECRYPT);
else if (opt == 3) {
    int* num = (int*)malloc(sizeof(int));
    AES_ofb128_encrypt(encrypted_text, decrypted_text, rL, &encryptkey, tmpiv, num);
}
else if (opt == 4) {
    int* num = (int*)malloc(sizeof(int));
    AES_cfb128_encrypt(encrypted_text, decrypted_text, rL, &encryptkey, tmpiv, num, AES_DECRYPT);
} else if (opt == 5) {
    int* num = (int*)malloc(sizeof(int));
    AES_cfb8_encrypt(encrypted_text, decrypted_text, rL, &encryptkey, tmpiv, num, AES_DECRYPT);
} else if (opt == 6) {
    int* num = (int*)malloc(sizeof(int));
    AES_cfb1_encrypt(encrypted_text, decrypted_text, rL * 8, &encryptkey, tmpiv, num, AES_DECRYPT);
} else {
    puts("No this model!");
    return 0;
}
puts("\n");

三 实验结果

​ 经过全排列测试,结果均正确。在此仅展示部分结果:

使用 0-padding 填充模式和 CBC 模式:

Input the AES key(length must be 16, 24, or 32) : 
19308030fangzhan
Input the plain text(Maximal Length should not exceed 255) : 
I like Du Yusong!


Choose the Padding Model : 
0 -- no-padding  1 -- zero-padding  2 -- PKCS5/PKCS7  3 -- ISO10126  4 -- ANSIX923
1


plain_text: 49 20 6C 69 6B 65 20 44 75 20 59 75 73 6F 6E 67 21 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 


Choose the Encrypted Model : 
1 -- ECB  2 -- CBC  3 -- OFB128  4 -- CFB128  5 -- CFB8  6 -- CFB1
2


encrypted_text: D8 A0 6A 96 42 3C 93 D5 8F A3 CC 30 A2 B9 1B 53 F9 4D 35 2D F6 EE B4 B4 5B 44 FF 02 C0 9A 32 C0 


decrypted_text: 49 20 6C 69 6B 65 20 44 75 20 59 75 73 6F 6E 67 21 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

使用 no-padding 填充模式和 OFB128 加密模式:

Input the AES key(length must be 16, 24, or 32) : 
19308030fangzhan
Input the plain text(Maximal Length should not exceed 255) : 
I like Xinxi Anquan!


Choose the Padding Model : 
0 -- no-padding  1 -- zero-padding  2 -- PKCS5/PKCS7  3 -- ISO10126  4 -- ANSIX923
0


plain_text: 49 20 6C 69 6B 65 20 58 69 6E 78 69 20 41 6E 71 75 61 6E 21 


Choose the Encrypted Model : 
1 -- ECB  2 -- CBC  3 -- OFB128  4 -- CFB128  5 -- CFB8  6 -- CFB1
3


encrypted_text: E2 B1 5F 99 C5 04 FB BC F7 7C 04 08 F0 C2 F5 6C FB E0 6E 3B 


decrypted_text: 49 20 6C 69 6B 65 20 58 69 6E 78 69 20 41 6E 71 75 61 6E 21 

四 实验感想

实验中还是遇到了一些困难的:

  • CFB模式加密和解密均使用加密key,这一点比较反常,务必记住。
  • CFB模式不需要对输入数据进行填充。
  • AES_cfb128_encrypt函数length参数,为输入数据长度,字节数。这一点与CFB1模式中有所不同。
  • AES_cfb1_encrypt函数length参数,为输入数据的位数,即输入数据长度*8,而不是字节数。
  • AES_ofb128_encrypt函数既是加密,又是解密。当in为明文时,执行的是加密操作;当in为密文时,执行的是解密操作,相当于是对等的。

不过总的来说,通过这次实验,我独立完成了 AES 的加解密过程。这次的代码量较大对于代码能力不强的我来说是一次极大的历练,中途一度想放弃摆烂。不过柳暗花明,最后还是成功完成了整个实验,至此还是非常有成就感的。

​ 总结一下吧:这是一次非常有意义让我学到很多东西的实验!

五 参考资料

  1. OpenSSL百度百科:https://baike.baidu.com/item/openssl/5454803
  2. OpenSSL官网:https://www.openssl.org/
  3. API - OpenSSLWiki:https://wiki.openssl.org/index.php/API