【深圳大学网络安全】实验一 分组密码实验

实验目的与要求

  1. 理解对称密码体制和分组密码算法的基本思想
  2. 理解分组密码AES的基本原理
  3. 实现AES的加解密过程,可以对各种文件(word、txt、mp3、jpg)进行加解密
  4. 实现分组密码的密码分组链接工作模式与计算器工作模式

实验环境

Microsoft Visual Studio 2005等

实验原理

AES是一种对称密钥加密算法,使用相同的密钥来加密和解密数据。AES将数据分成固定长度的块(通常为128位),并对每个块进行加密。

AES的基本原理如下:

1.密钥扩展

在AES中,密钥扩展用于生成一系列轮密钥,以供加密和解密时使用。AES-128使用10个轮密钥,每个轮密钥128位,而AES-256使用14个轮密钥,每个轮密钥256位。这些轮密钥是从初始密钥生成的,而密钥扩展算法可以保证轮密钥的独立性和随机性。

2.轮变换

在AES中,每个加密轮由四个变换组成:字节替换变换(SubBytes)、行移位变换(ShiftRows)、列混淆变换(MixColumns)和轮密钥加变换(AddRoundKey)。

这些变换一起构成了AES加密的核心。

SubBytes:将每个输入字节替换为一个固定的值,这个值可以通过查找一个预定义的替换表来获得。这个替换表是通过一系列复杂的算法生成的,可以有效地抵御多种密码分析攻击。

ShiftRows:将每一行的字节循环左移不同的步数。这个变换可以保证不同行之间的数据独立性,增加加密的强度。

MixColumns:将每一列的四个字节进行线性变换,这个变换可以增加加密的强度,并且可以防止不同列之间的数据相关性。

AddRoundKey:使用当前轮密钥将输入数据的每个字节进行异或运算。这个变换可以增加加密的随机性,并且保证轮密钥的独立性。

最后一次轮变换不包括 InvMixColumns 步骤。

3.解密

AES的解密过程与加密过程类似,只是需要使用相同的密钥和相反的变换顺序。

解密过程由以下四个变换组成:轮密钥加变换、逆列混淆变换(InvMixColumns)、逆行移位变换(InvShiftRows)和逆字节替换变换(InvSubBytes)。

在解密过程中,轮密钥的顺序是反向的。

流程图如下:

实验内容

  1. 熟悉AES的加解密过程
  2. 采用自己熟悉的编程语言实现AES加密算法
  3. 将编写的AES用于加密各种文件(word、txt、mp3、jpg),并能成功解密
  4. 实现分组密码的密码分组链接工作模式与计算器工作模式

实验步骤与结果

1. AES加密算法的实现及介绍(将关键代码切图)

设置密钥,以二进制格式打开文件

逐个读入符存进数组

计算差值并将长度补齐成16的倍数

将数组加密后存入

加密算法,首先判断是否是16倍数

对密钥进行扩展操作

将字符数组变为矩阵

进行轮密钥加操作

进行10轮加密,1-9轮加密包括字节代换、行移位、列混合、轮密钥加操作。

第10轮加密不包括列混合操作。

字节代换通过查表进行替换。

行移位将每一行的字节循环左移不同的位数。

列混合将每一列的四个字节进行线性变换。

最后将加密后的矩阵转回字符串。

对密文进行解密并写入。

解密算法,先判断明文和密钥是否是16的倍数。

先进行轮密钥加,之后进行10轮解密,1-9轮解密包括逆行移位、逆字节代换、轮密钥加、逆列混合操作。

第10轮解密不包括逆列混合操作。

逆行移位

逆字节代换

逆列混合

2. AES用于加密各种文件(wordtxtmp3jpg)的截图

txt文件明文

txt密文

txt解密文

Word明文

Word密文(无法正常打开)

Word解密文

jpg明文

jpg密文(不可视)

jpg解密文

mp3明文(正常播放)

mp3密文(无法播放)

mp3解密文(正常播放)

3. 各种分组密码多种工作模式关键代码切图及简单介绍

密码分组链接模式(Cipher Block Chaining (CBC))

1.先将明文和上一组的密文进行XOR异或操作,然后再进行加密。

2.第一个数据块进行加密之前需要用初始化向量IV进行异或操作。

3.是一种最常用的加密模式。

4.优点是不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。

5.缺点是加密是连续的,不能并行处理,并且与ECB一样消息块必须填充到块大小的整倍数。

加密算法

解密算法

计算器模式(Counter (CTR))

计算器模式不常见,在CTR模式中, 有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次。CTR的示意图如下所示:

加密算法

递归实现计数器加一

解密算法

实验结论

高级加密标准(AES, Advanced Encryption Standard)为最常见的对称加密算法,对称加密算法也就是加密和解密用相同的密钥。AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。

分组密码有五种工作体制:1.电码本模式(Electronic Codebook Book (ECB));2.密码分组链接模式(Cipher Block Chaining (CBC));3.计算器模式(Counter (CTR));4.密码反馈模式(Cipher FeedBack (CFB));5.输出反馈模式(Output FeedBack (OFB))。

ECB(Electronic Codebook, 电子密码本)模式是最简单的加密模式,明文消息被分成固定大小的块(分组),并且每个块被单独加密。这种方法一旦有一个块被破解,使用相同的方法可以解密所有的明文数据,安全性比较差。可以进行并行计算,运行时间较短。加密前需要把明文数据填充到块大小的整倍数。

密码分组链接模式(Cipher Block Chaining (CBC))先将明文和上一组的密文进行XOR异或操作,然后再进行加密。第一个数据块进行加密之前需要用初始化向量IV进行异或操作。是一种最常用的加密模式。优点是不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。缺点是加密是连续的,不能并行处理,并且与ECB一样消息块必须填充到块大小的整倍数。

计算器模式(Counter (CTR))不常见,在CTR模式中, 有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。这种加密方式简单快速,安全可靠,而且可以并行加密。

 CFB(Cipher Feedback, 密码反馈)模式和CBC模式比较相似,前一个分组的密文加密后和当前分组的明文XOR异或操作生成当前分组的密文。CFB模式的解密和CBC模式的加密在流程上其实是非常相似的。优点:1.隐藏了明文模式;2.分组密码转化为流模式;3.可以及时加密传送小于分组的数据。缺点:1.不利于并行计算;2.误差传送:一个明文单元损坏影响多个单元;3.唯一的IV,导致安全性降低。

OFB(Output Feedback, 输出反馈)模式将分组密码转换为同步流密码,也就是说可以根据明文长度先独立生成相应长度的流密码。OFB和CFB非常相似,CFB是前一个分组的密文加密后XOR当前分组明文,OFB是前一个分组与前一个明文块异或之前的流密码XOR当前分组明文。由于异或操作的对称性,OFB模式的解密和加密完全一样的流程。优点:1.隐藏了明文模式;2.分组密码转化为流模式;3.可以及时加密传送小于分组的数据; 缺点:1.不利于并行计算;2.对明文的主动攻击是可能的;3.误差传送:一个明文单元损坏影响多个单元。

实验代码

aes.h

#ifndef MY_AES_H
#define MY_AES_H

/**
 * 参数 p: 明文的字符串数组。
 * 参数 plen: 明文的长度,长度必须为16的倍数。
 * 参数 key: 密钥的字符串数组。
 */
void aes(char* p, int plen, char* key);

/**
 * 参数 c: 密文的字符串数组。
 * 参数 clen: 密文的长度,长度必须为16的倍数。
 * 参数 key: 密钥的字符串数组。
 */
void deAes(char* c, int clen, char* key);

#endif

aes.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "aes.h"

/**
 * S盒
 */
static const int S[16][16] = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };

/**
 * 逆S盒
 */
static const int S2[16][16] = { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };

/**
 * 获取整形数据的低8位的左4个位
 */
static int getLeft4Bit(int num) {
	int left = num & 0x000000f0;
	return left >> 4;
}

/**
 * 获取整形数据的低8位的右4个位
 */
static int getRight4Bit(int num) {
	return num & 0x0000000f;
}
/**
 * 根据索引,从S盒中获得元素
 */
static int getNumFromSBox(int index) {
	int row = getLeft4Bit(index);
	int col = getRight4Bit(index);
	return S[row][col];
}

/**
 * 把一个字符转变成整型
 */
static int getIntFromChar(char c) {
	int result = (int)c;
	return result & 0x000000ff;
}

/**
 * 把16个字符转变成4X4的数组,
 * 该矩阵中字节的排列顺序为从上到下,
 * 从左到右依次排列。
 */
static void convertToIntArray(char* str, int pa[4][4]) {
	int k = 0;
	int i, j;
	for (i = 0; i < 4; i++)
		for (j = 0; j < 4; j++) {
			pa[j][i] = getIntFromChar(str[k]);
			k++;
		}
}

/**
 * 打印4X4的数组
 */
static void printArray(int a[4][4]) {
	int i, j;
	for (i = 0; i < 4; i++) {
		for (j = 0; j < 4; j++)
			printf("a[%d][%d] = 0x%x ", i, j, a[i][j]);
		printf("\n");
	}
	printf("\n");
}

/**
 * 打印字符串的ASSCI,
 * 以十六进制显示。
 */
static void printASSCI(char* str, int len) {
	int i;
	for (i = 0; i < len; i++)
		printf("0x%x ", getIntFromChar(str[i]));
	printf("\n");
}

/**
 * 把连续的4个字符合并成一个4字节的整型
 */
static int getWordFromStr(char* str) {
	int one, two, three, four;
	one = getIntFromChar(str[0]);
	one = one << 24;
	two = getIntFromChar(str[1]);
	two = two << 16;
	three = getIntFromChar(str[2]);
	three = three << 8;
	four = getIntFromChar(str[3]);
	return one | two | three | four;
}

/**
 * 把一个4字节的数的第一、二、三、四个字节取出,
 * 入进一个4个元素的整型数组里面。
 */
static void splitIntToArray(int num, int array[4]) {
	int one, two, three;
	one = num >> 24;
	array[0] = one & 0x000000ff;
	two = num >> 16;
	array[1] = two & 0x000000ff;
	three = num >> 8;
	array[2] = three & 0x000000ff;
	array[3] = num & 0x000000ff;
}

/**
 * 将数组中的元素循环左移step位
 */
static void leftLoop4int(int array[4], int step) {
	int temp[4];
	int i;
	int index;
	for (i = 0; i < 4; i++)
		temp[i] = array[i];

	index = step % 4 == 0 ? 0 : step % 4;
	for (i = 0; i < 4; i++) {
		array[i] = temp[index];
		index++;
		index = index % 4;
	}
}

/**
 * 把数组中的第一、二、三和四元素分别作为
 * 4字节整型的第一、二、三和四字节,合并成一个4字节整型
 */
static int mergeArrayToInt(int array[4]) {
	int one = array[0] << 24;
	int two = array[1] << 16;
	int three = array[2] << 8;
	int four = array[3];
	return one | two | three | four;
}

/**
 * 常量轮值表
 */
static const int Rcon[10] = { 0x01000000, 0x02000000,
	0x04000000, 0x08000000,
	0x10000000, 0x20000000,
	0x40000000, 0x80000000,
	0x1b000000, 0x36000000 };
/**
 * 密钥扩展中的T函数
 */
static int T(int num, int round) {
	int numArray[4];
	int i;
	int result;
	splitIntToArray(num, numArray);
	leftLoop4int(numArray, 1);//字循环

	//字节代换
	for (i = 0; i < 4; i++)
		numArray[i] = getNumFromSBox(numArray[i]);

	result = mergeArrayToInt(numArray);
	return result ^ Rcon[round];
}

//密钥对应的扩展数组
static int w[44];
/**
 * 打印W数组
 */
static void printW() {
	int i, j;
	for (i = 0, j = 1; i < 44; i++, j++) {
		printf("w[%d] = 0x%x ", i, w[i]);
		if (j % 4 == 0)
			printf("\n");
	}
	printf("\n");
}


/**
 * 扩展密钥,结果是把w[44]中的每个元素初始化
 */
static void extendKey(char* key) {
	int i, j;
	for (i = 0; i < 4; i++)
		w[i] = getWordFromStr(key + i * 4);

	for (i = 4, j = 0; i < 44; i++) {
		if (i % 4 == 0) {
			w[i] = w[i - 4] ^ T(w[i - 1], j);
			j++;//下一轮
		}
		else {
			w[i] = w[i - 4] ^ w[i - 1];
		}
	}

}

/**
 * 轮密钥加
 */
static void addRoundKey(int array[4][4], int round) {
	int warray[4];
	int i, j;
	for (i = 0; i < 4; i++) {

		splitIntToArray(w[round * 4 + i], warray);

		for (j = 0; j < 4; j++) {
			array[j][i] = array[j][i] ^ warray[j];
		}
	}
}

/**
 * 字节代换
 */
static void subBytes(int array[4][4]) {
	int i, j;
	for (i = 0; i < 4; i++)
		for (j = 0; j < 4; j++)
			array[i][j] = getNumFromSBox(array[i][j]);
}

/**
 * 行移位
 */
static void shiftRows(int array[4][4]) {
	int rowTwo[4], rowThree[4], rowFour[4];
	int i;
	for (i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}

	leftLoop4int(rowTwo, 1);
	leftLoop4int(rowThree, 2);
	leftLoop4int(rowFour, 3);

	for (i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}

/**
 * 列混合要用到的矩阵
 */
static const int colM[4][4] = { 2, 3, 1, 1,
	1, 2, 3, 1,
	1, 1, 2, 3,
	3, 1, 1, 2 };

static int GFMul2(int s) {
	int result = s << 1;
	int a7 = result & 0x00000100;

	if (a7 != 0) {
		result = result & 0x000000ff;
		result = result ^ 0x1b;
	}

	return result;
}

static int GFMul3(int s) {
	return GFMul2(s) ^ s;
}

static int GFMul4(int s) {
	return GFMul2(GFMul2(s));
}

static int GFMul8(int s) {
	return GFMul2(GFMul4(s));
}

static int GFMul9(int s) {
	return GFMul8(s) ^ s;
}

static int GFMul11(int s) {
	return GFMul9(s) ^ GFMul2(s);
}

static int GFMul12(int s) {
	return GFMul8(s) ^ GFMul4(s);
}

static int GFMul13(int s) {
	return GFMul12(s) ^ s;
}

static int GFMul14(int s) {
	return GFMul12(s) ^ GFMul2(s);
}

/**
 * GF上的二元运算
 */
static int GFMul(int n, int s) {
	int result;

	if (n == 1)
		result = s;
	else if (n == 2)
		result = GFMul2(s);
	else if (n == 3)
		result = GFMul3(s);
	else if (n == 0x9)
		result = GFMul9(s);
	else if (n == 0xb)//11
		result = GFMul11(s);
	else if (n == 0xd)//13
		result = GFMul13(s);
	else if (n == 0xe)//14
		result = GFMul14(s);

	return result;
}
/**
 * 列混合
 */
static void mixColumns(int array[4][4]) {

	int tempArray[4][4];
	int i, j;
	for (i = 0; i < 4; i++)
		for (j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for (i = 0; i < 4; i++)
		for (j = 0; j < 4; j++) {
			array[i][j] = GFMul(colM[i][0], tempArray[0][j]) ^ GFMul(colM[i][1], tempArray[1][j])
				^ GFMul(colM[i][2], tempArray[2][j]) ^ GFMul(colM[i][3], tempArray[3][j]);
		}
}
/**
 * 把4X4数组转回字符串
 */
static void convertArrayToStr(int array[4][4], char* str) {
	int i, j;
	for (i = 0; i < 4; i++)
		for (j = 0; j < 4; j++)
			*str++ = (char)array[j][i];
}
/**
 * 检查密钥长度
 */
static int checkKeyLen(int len) {
	if (len == 16)
		return 1;
	else
		return 0;
}


/**
 * 参数 p: 明文的字符串数组。
 * 参数 plen: 明文的长度。
 * 参数 key: 密钥的字符串数组。
 */
void aes(char* p, int plen, char* key) {

	int keylen = strlen(key);
	int pArray[4][4];
	int k, i;

	if (plen == 0 || plen % 16 != 0) {
		printf("明文字符长度必须为16的倍数!\n");
		exit(0);
	}

	if (!checkKeyLen(keylen)) {
		printf("密钥字符长度错误!长度必须为16。当前长度为%d\n", keylen);
		exit(0);
	}

	extendKey(key);//扩展密钥

	for (k = 0; k < plen; k += 16) //一个一个矩阵进行加密
	{
		convertToIntArray(p + k, pArray);

		addRoundKey(pArray, 0);//一开始的轮密钥加

		for (i = 1; i < 10; i++) {

			subBytes(pArray);//字节代换

			shiftRows(pArray);//行移位

			mixColumns(pArray);//列混合

			addRoundKey(pArray, i);

		}

		subBytes(pArray);//字节代换

		shiftRows(pArray);//行移位

		addRoundKey(pArray, 10);//轮密钥加

		convertArrayToStr(pArray, p + k);//转回字符串
	}
}
/**
 * 根据索引从逆S盒中获取值
 */
static int getNumFromS1Box(int index) {
	int row = getLeft4Bit(index);
	int col = getRight4Bit(index);
	return S2[row][col];
}
/**
 * 逆字节代换
 */
static void deSubBytes(int array[4][4]) {
	int i, j;
	for (i = 0; i < 4; i++)
		for (j = 0; j < 4; j++)
			array[i][j] = getNumFromS1Box(array[i][j]);
}
/**
 * 把4个元素的数组循环右移step位
 */
static void rightLoop4int(int array[4], int step) {
	int temp[4];
	int i;
	int index;
	for (i = 0; i < 4; i++)
		temp[i] = array[i];

	index = step % 4 == 0 ? 0 : step % 4;
	index = 3 - index;
	for (i = 3; i >= 0; i--) {
		array[i] = temp[index];
		index--;
		index = index == -1 ? 3 : index;
	}
}

/**
 * 逆行移位
 */
static void deShiftRows(int array[4][4]) {
	int rowTwo[4], rowThree[4], rowFour[4];
	int i;
	for (i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}

	rightLoop4int(rowTwo, 1);
	rightLoop4int(rowThree, 2);
	rightLoop4int(rowFour, 3);

	for (i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}
/**
 * 逆列混合用到的矩阵
 */
static const int deColM[4][4] = { 0xe, 0xb, 0xd, 0x9,
	0x9, 0xe, 0xb, 0xd,
	0xd, 0x9, 0xe, 0xb,
	0xb, 0xd, 0x9, 0xe };

/**
 * 逆列混合
 */
static void deMixColumns(int array[4][4]) {
	int tempArray[4][4];
	int i, j;
	for (i = 0; i < 4; i++)
		for (j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for (i = 0; i < 4; i++)
		for (j = 0; j < 4; j++) {
			array[i][j] = GFMul(deColM[i][0], tempArray[0][j]) ^ GFMul(deColM[i][1], tempArray[1][j])
				^ GFMul(deColM[i][2], tempArray[2][j]) ^ GFMul(deColM[i][3], tempArray[3][j]);
		}
}
/**
 * 把两个4X4数组进行异或
 */
static void addRoundTowArray(int aArray[4][4], int bArray[4][4]) {
	int i, j;
	for (i = 0; i < 4; i++)
		for (j = 0; j < 4; j++)
			aArray[i][j] = aArray[i][j] ^ bArray[i][j];
}
/**
 * 从4个32位的密钥字中获得4X4数组,
 * 用于进行逆列混合
 */
static void getArrayFrom4W(int i, int array[4][4]) {
	int index, j;
	int colOne[4], colTwo[4], colThree[4], colFour[4];
	index = i * 4;
	splitIntToArray(w[index], colOne);
	splitIntToArray(w[index + 1], colTwo);
	splitIntToArray(w[index + 2], colThree);
	splitIntToArray(w[index + 3], colFour);

	for (j = 0; j < 4; j++) {
		array[j][0] = colOne[j];
		array[j][1] = colTwo[j];
		array[j][2] = colThree[j];
		array[j][3] = colFour[j];
	}

}

/**
 * 参数 c: 密文的字符串数组。
 * 参数 clen: 密文的长度。
 * 参数 key: 密钥的字符串数组。
 */
void deAes(char* c, int clen, char* key) {

	int cArray[4][4];
	int keylen, k;
	keylen = strlen(key);
	if (clen == 0 || clen % 16 != 0) {
		printf("密文字符长度必须为16的倍数!现在的长度为%d\n", clen);
		exit(0);
	}

	if (!checkKeyLen(keylen)) {
		printf("密钥字符长度错误!长度必须为16、24和32。当前长度为%d\n", keylen);
		exit(0);
	}

	extendKey(key);//扩展密钥

	for (k = 0; k < clen; k += 16) {
		int i;
		int Array[4][4];

		convertToIntArray(c + k, Array);//将密文转为矩阵

		addRoundKey(Array, 10);//轮密钥加

		for (i = 9; i > 0 ; i--) {
			
			deShiftRows(Array);//逆行移位

			deSubBytes(Array);//逆字节代换

			addRoundKey(Array, i);//轮密钥加

			deMixColumns(Array);//逆列混合
		}

		deShiftRows(Array);//逆行移位
		
		deSubBytes(Array);//逆字节代换

		addRoundKey(Array, 0);//轮密钥加

		convertArrayToStr(Array, c + k);//将矩阵转换为字符串

	}
}

main.cpp

#include "aes.h"
#include<iostream>
#include<fstream>
using namespace std;
// 其它头文件

/*
void CBC()//仅用于演示核心思想,运行需要修改加解密代码
{
    ifstream input("D:\\test\\1.txt", ios::binary);
    ofstream ouput("D:\\test\\2.txt", ios::binary);
    char sjuzhen[16];
    char c[1];
    char temp[16] = { 'a','b','c','d','e','f','g','h','i','j','k','l' ,'m','n','o','p' };
    char key[16] = { '1','2','3','4','5','6','7' ,'8','9','0','1','2','3','4','5','6' };
    int i, j;

    //加密
    while (input)
    {
        for (i = 0; i <= 15;i++)//一个一个字符读取二进制值
        {
            if (input.read(c, 1))
            {
                sjuzhen[i] = c[0] ^ temp[i];
                j = i;
            }
            else//当最后一次加密不足128bit时
            {
                sjuzhen[i] = '$' ^ temp[i];//在空位补$(0x24)
            }
        }

        aes(sjuzhen, key);//加密

        for (i = 0; i <=15 ; i++)//将本轮加密密文保存
        {
            temp[i] = sjuzhen[i];
        }
        ouput.write(sjuzhen, 16);//将加密后密文输出到文件中
    }
	ouput.close();
    input.close(); 
}

void deCBC()
{
	ifstream input("D:\\test\\2.txt", ios::binary);
	ofstream output("D:\\test\\3.txt", ios::binary);
	char temp[16], miwen[16];
	char suanzi[16] = { 'a','b','c','d','e','f','g','h','i','j','k','l' ,'m','n','o','p' };
	char key[16] = { '1','2','3','4','5','6','7' ,'8','9','0','1','2','3','4','5','6' };
	int i, k;
	char c[1];

	//解密
	input.read(temp, 16);//第一次读入密文到temp保存
	while (1)
	{
		for (i = 0; i <=15; i++)
		{
			miwen[i] = temp[i];//将密文输入解密序列中
		}

		deAes(miwen, key);//解密

		for (i = 0; i <=15; i++)//将解密出的密文与上一轮密文做异或
		{
			miwen[i] = miwen[i] ^ suanzi[i];
			suanzi[i] = temp[i];//保存本次密文用于下轮解密
		}

		if (!input.read(temp, 16))//读取下轮解密密文(判断是否是最后一轮)
		{
			for (i = 0, k = 0; i <=15; i++)//最后一轮去除填充的$符号并写入文件
				if (miwen[i] == '$' && k == 0)
					k = i;
				else if (miwen[i] != '$')
					k = 0;
			
			for (i = 0; i < k; i++)
			{
				c[0] = miwen[i];
				output.write(c, sizeof(char));
			}
			break;
		}
		else//非最后一轮直接写入到文件中
		{
			for (i = 0; i < 16; i++)
			{
				c[0] = miwen[i];
				output.write(c, 1);
			}
		}
	}
	output.close();
	input.close();
	
}

void addOne(char count[16], int i)
{
	if (i != 0 && count[i] == 255)//进位时
	{
		addOne(count, i - 1);//递归调用
	}
	count[i]++;//加一
}

void CTR()
{
	ifstream input("D:\\test\\1.txt", ios::binary);
	ofstream output("D:\\test\\2.txt",ios::binary);
	char c[1];
	char sjuzhen[16];
	char count[16] = { 'a','b','c','d','e','f','g','h','i','j','k','l' ,'m','n','o','p' };//计数器初始序列
	char key[16] = { '1','2','3','4','5','6','7' ,'8','9','0','1','2','3','4','5','6' };//密钥
	int i;

	//加密
	while (true)
	{
		for (i = 0; i <=15; i++)//提取当前的计数器
		{
			sjuzhen[i] = count[i];
		}

		aes(sjuzhen, key);//将计算的值加密

		for (i = 0; i <=15; i++)//对接下来16个字符逐个加密
		{
			if (input.read(c, 1))
			{
				c[0] = c[0] ^ sjuzhen[i];
				output.write(c, sizeof(char));
			}
			else
				break;
		}
		if (i != 16)//本轮明文不足16个字节,说明加密结束
			break;

		addOne(count, 15);//计数器加1
	}
	output.close();
	input.close();
	
}

void deCTR()
{
	ifstream input("D:\\test\\2.txt", ios::binary);
	ofstream output("D:\\test\\3.txt", ios::binary);
	char c[1];
	char sjuzhen[16];
	char count[16] = { 'a','b','c','d','e','f','g','h','i','j','k','l' ,'m','n','o','p' };//计数器初始序列
	char key[16] = { '1','2','3','4','5','6','7' ,'8','9','0','1','2','3','4','5','6' };
	int i;

	//解密
	while (1)
	{
		for (i = 0; i <=15; i++)		
			sjuzhen[i] = count[i];	

		aes(sjuzhen, key);

		for (i = 0; i <=15; i++)
		{
			if (input.read(c, sizeof(char)))
			{
				c[0] = c[0] ^ sjuzhen[i];
				output.write(c, sizeof(char));
			}
			else
			{
				break;
			}
		}
		if (i != 16)
			break;

		addOne(count, 15);
	}
	output.close();
	input.close();
	
}
*/

int main() 
{

    char* key = new char[32], * p;
    p = new char[100000000];//分配数组
    key = "1234567890123456";

    ifstream ifs("D:\\test\\1.txt",  ios::binary);        //注意文件路径的格式          
    if (!ifs.is_open()) {
        cout << "Failed to open file!" << endl;              //手动更换后缀
        return 1;                                            // txt docx jpg mp3              
    }                                                        //1.txt是明文,2.txt是密文,3.txt是解密文
    
    char c[1];
    int size = 0;
    while (ifs.read(c,sizeof(char)))//逐个字符读入
    {
        //if (c == '\r')
        //    continue;
        //cout << c;
        p[size] = c[0];
        size++;
    }
    //for (int i = 0; i < size; i++)
    //    cout << p[i];

    ifs.close();

    //file.seekg(0, ios::end);//获取文件大小
    //int size = file.tellg();
    //file.seekg(0, ios::beg);

    
    int size2, cha=0;
    if (size % 16 != 0)
    {
        size2 = (size / 16 + 1) * 16;
        cha = size2 - size;
    }
    else
        size2 = size;

    
    //file.read(p, size); // 读取文件 
    //file.close(); // 关闭文件 

    int i, j;
    for (i = size2 - cha; i < size2; i++)   //补0凑成16的倍数
        p[i] = '$';
    
    

    // 加密, 其中p是明文字符数组, size2是长度, key是密钥
    aes(p, size2, key);

    ofstream ofs("D:\\test\\2.txt", ios::binary);   //千万不要忘了以二进制格式输出!!!!!裂 开
    ofs.write(p, size2);
    ofs.close();


    //解密,其中p是密文字符数组, size2是长度, key是密钥
    deAes(p, size2, key);
    
    ofstream ofs2("D:\\test\\3.txt", ios::binary);
    ofs2.write(p, size);
    ofs2.close();
    
    /*
        ofstream outfile4("D:\\test\\4.txt");//测试
        outfile4.write(p, size);
        file.close();
    */
}

(by 归忆)

  • 27
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

归忆_AC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值