零 实验环境
操作系统: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 加解密的流程如下:
从图中可以看出:
- 解密算法的每一步分别对应加密算法的逆操作;
- 加解密所有操作的顺序正好是相反的;
- 加密算法与解密算法每步的操作互逆
正是由于这几点才得以保证了算法的正确性。加解密中每轮的密钥分别由种子密钥经过密钥扩展算法得到。由于 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$ 个新的密钥。其具体生成步骤如下:
-
将种子密钥按下图 $(a)$ 的格式排列,其中 $k_0$、$k_1$、……、$k_{15}$ 依次表示种子密钥的一个字节;排列后用 $4$ 个 $32$ 比特的字表示,分别记为 $w[0]$、$w[1]$、$w[2]$、$w[3]$;
-
按照如下方式,依次求解 $w[j]$,其中 $j$ 是整数并且属于 $[4,43]$;
-
若 $j \% 4 = 0$,则 $w[j]=w[j-4]\bigoplus g(w[j-1])$,否则 $w[j]=w[j-4]\bigoplus w[j-1]$,在这里函数g的流程说明如下:
- 将w循环左移8比特。
- 分别对每个字节做S盒置换。
- 与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 工作模式
分组密码加密有五种模式:
- 电码本模式(Electronic Codebook Book (ECB) )
- 密码分组链接模式(Cipher Block Chaining (CBC) )
- 计算器模式(Counter (CTR) )
- 密码反馈模式(Cipher FeedBack (CFB) )
- 输出反馈模式(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 的加解密过程。这次的代码量较大对于代码能力不强的我来说是一次极大的历练,中途一度想放弃摆烂。不过柳暗花明,最后还是成功完成了整个实验,至此还是非常有成就感的。
总结一下吧:这是一次非常有意义让我学到很多东西的实验!
五 参考资料
- OpenSSL百度百科:https://baike.baidu.com/item/openssl/5454803
- OpenSSL官网:https://www.openssl.org/
- API - OpenSSLWiki:https://wiki.openssl.org/index.php/API