目录
1 设计要求
2 实验环境
3 实验原理
3.1 RSA
3.2 AES
4 实验流程
5 代码地址
6 运行演示
1 设计要求
编写一段程序,实现两个主机之间的密钥分发和加密传输。
要求:
- 用RSA算法实现两个主机之间的密钥分发,分发的密钥是“12345678”;
- 用分发的密钥和AES加密算法,实现两个主机之间的加密数据传输,传输的数据是“NPU”;
- 两个步骤在程序中自动执行完,无手动参与;程序可以在同一台主机上完成,但数据必须经过网络传输(可以本地发送,本地接收);
- RSA和AES算法必须是源码编译得到,不能直接用编译过的库文件;RSA和AES算法的源码可以来自于网络或其他任意渠道;
- 用Python或C/C++语言实现程序,写出实现技术文档。
2 实验环境
- Python 3.6.3 :: Anaconda, Inc.
- PyCharm 2020.1.2
- Socket库
3 实验原理
3.1 RSA
RSA加密算法是一种非对称加密算法,在公开密钥加密和电子商业中被广泛使用。RSA是由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)在1977年一起提出的。当时他们三人都在麻省理工学院工作。RSA 就是他们三人姓氏开头字母拼在一起组成的。
对极大整数做因数分解的难度决定了 RSA 算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA 算法愈可靠。假如有人找到一种快速因数分解的算法的话,那么用 RSA 加密的信息的可靠性就会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的 RSA 钥匙才可能被强力方式破解。到目前为止,世界上还没有任何可靠的攻击RSA算法的方式。只要其钥匙的长度足够长,用RSA加密的信息实际上是不能被破解的。
RSA属于公钥加密算法中的一个重要应用。RSA加密算法由五个部分组成:
原文(Message):要加密的信息,可以是数字、文字、视频、音频等,用M表示。
密文(Ciphertext):加密后得到的信息,用C表示。
公钥(Public Key)和私钥(Private Key),用PU和PR表示。
加密算法(Encryption):若E(x) 为加密算法,加密过程可以理解为C=E(M) ,根据原文和加密算法得到密文。
解密算法(Decryption):若D(x) 为解密算法,解密过程可以理解为M=D(C) ,根据密文和解密算法得到原文。
假设Alice和Bob要在网上进行加密通信,应用RSA来加密和解密信息。步骤如下:
3.2 AES
恰好我大二时候手写过AES加密算法。
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。现在,高级加密标准已然成为对称密钥加密中最流行的算法之一。该算法为比利时密码学家Joan Daemen和Vincent Rijmen所设计,结合两位作者的名字,以Rijndael为名投稿高级加密标准的甄选流程。
AES的加密公式为 ,在加密函数E中,会执行一个轮函数,并且执行10次这个轮函数,这个轮函数的前9次执行的操作是一样的,只有第10次有所不同。也就是说,一个明文分组会被加密10轮。AES的核心就是实现一轮中的所有操作。AES算法主要可以分为秘钥扩展、字节替换、行移位、列混合和轮秘钥加这5个步骤。
图3.1 AES的十轮原理图
在密钥加法层中有两个输入的参数,分别是明文和子密钥,而且这两个输入都是128位的。在扩展域中加减法操作和异或运算等价,所以这里的处理只需要将两个输入的数据进行按字节异或操作就会得到运算的结果。
# Schedule a secret key for use.
def ScheduleKey(self, w, Nk):
Rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]
for r in range(4):
for c in range(4):
w[0][r][c] = self.key[r + c * 4]
for i in range(1, self.Nr + 1, 1): # 问题1
for j in range(Nk):
t = [0 for x in range(4)]
for r in range(4):
if j:
t[r] = w[i][r][j - 1]
else:
t[r] = w[i - 1][r][3]
if j == 0:
temp = t[0]
for r in range(3):
t[r] = self.sbox[t[(r + 1) % 4]]
t[3] = self.sbox[temp]
t[0] ^= int(Rcon[i - 1])
for r in range(4):
w[i][r][j] = w[i - 1][r][j] ^ t[r]
图3.2 密钥扩展示意图
字节代换层的主要功能就是让输入的数据通过S_box表完成从一个字节到另一个字节的映射,这里的S_box表是通过某种方法计算出来的。S_box表是一个拥有256个字节元素的数组,可以将其定义为一维数组,也可以将其定义为16·16的二维数组,如果将其定义为二维数组,读取S_box数据的方法就是要将输入数据的每个字节的高四位作为第一个下标,第四位作为第二个下标,略有点麻烦。这里建议将其视作一维数组即可。逆S盒与S盒对应,用于解密时对数据处理,对解密时的程序处理称作逆字节代换,只是使用的代换表盒加密时不同而已。
# SubBytes
def SubBytes(self):
for x in range(16):
self.blk[x] = self.sbox[self.blk[x]]
图3.3 字节代换示意图
行位移操作最为简单,它是用来将输入数据作为一个 的字节矩阵进行处理的,然后将这个矩阵的字节进行位置上的置换。ShiftRows子层属于AES手动的扩散层,目的是将单个位上的变换扩散到影响整个状态,从而达到雪崩效应。在加密时行位移处理与解密时的处理相反,我们这里将解密时的处理称作逆行位移。它之所以称作行位移,是因为它只在 矩阵的行间进行操作,每行4字节的数据。在加密时,保持矩阵的第一行不变,第二行向左移动8Bit(一个字节)、第三行向左移动2个字节、第四行向左移动3个字节。而在解密时恰恰相反,依然保持第一行不变,将第二行向右移动一个字节、第三行右移2个字节、第四行右移3个字节。
# ShiftRows:Shifts the entire block
def ShiftRows(self):
# 2nd row
t = self.blk[1]
self.blk[1] = self.blk[5]
self.blk[5] = self.blk[9]
self.blk[9] = self.blk[13]
self.blk[13] = t
# 3nd row
t = self.blk[2]
self.blk[2] = self.blk[10]
self.blk[10] = t
t = self.blk[6]
self.blk[6] = self.blk[14]
self.blk[14] = t
# 4nd row
t = self.blk[15]
self.blk[15] = self.blk[11]
self.blk[11] = self.blk[7]
self.blk[7] = self.blk[3]
self.blk[3] = t
图3.4 行移位示意图
列混淆子层是AES算法中最为复杂的部分,属于扩散层,列混淆操作是AES算法中主要的扩散元素,它混淆了输入矩阵的每一列,使输入的每个字节都会影响到4个输出字节。行位移子层和列混淆子层的组合使得经过三轮处理以后,矩阵的每个字节都依赖于16个明文字节成可能。其中包含了矩阵乘法、伽罗瓦域内加法和乘法的相关知识。
在加密的正向列混淆中,我们要将输入的 矩阵左乘一个给定的 矩阵。而它们之间的加法、乘法都在扩展域GF(2^8)中进行。
# MixColumns: Process the entire block
def MixColumns(self):
tmp = [0 for t in range(4)]
xt = [0 for q in range(4)]
n = 0
for x in range(4):
xt[0] = self.xtime(self.blk[n])
xt[1] = self.xtime(self.blk[n + 1])
xt[2] = self.xtime(self.blk[n + 2])
xt[3] = self.xtime(self.blk[n + 3])
tmp[0] = xt[0] ^ xt[1] ^ self.blk[n + 1] ^ self.blk[n + 2] ^ self.blk[n + 3]
tmp[1] = self.blk[n] ^ xt[1] ^ xt[2] ^ self.blk[n + 2] ^ self.blk[n + 3]
tmp[2] = self.blk[n] ^ self.blk[n + 1] ^ xt[2] ^ xt[3] ^ self.blk[n + 3]
tmp[3] = xt[0] ^ self.blk[n] ^ self.blk[n + 1] ^ self.blk[n + 2] ^ xt[3]
self.blk[n] = tmp[0]
self.blk[n + 1] = tmp[1]
self.blk[n + 2] = tmp[2]
self.blk[n + 3] = tmp[3]
n = n + 4
图3.5 列混合示意图
子密钥的生成是以列为单位进行的,一列是32Bit,四列组成子密钥共128Bit。生成子密钥的数量比AES算法的轮数多一个,因为第一个密钥加法层进行密钥漂白时也需要子密钥。密钥漂白是指在AES的输入盒输出中都使用的子密钥的XOR加法。子密钥在图中都存储在W[0]、W[1]、…、W[43]的扩展密钥数组之中。k1-k16表示原始密钥对应的字节,而子密钥k0与原始子密钥相同。在生成的扩展密钥中W的下标如果是4的倍数时(从零开始)需要对异或的参数进行G函数处理。扩展密钥生成有关代码如下:
# AddRoundKey
def AddRoundKey(self, key):
x = 0
k = [0 for m in range(16)]
for c in range(4):
for r in range(4):
k[x] = key[r][c]
x = x + 1
for y in range(16):
self.blk[y] ^= int(k[y])
def show(self):
for i in range(16):
print(hex(self.blk[i]))
图3.6 密钥加示意图
解密流程与加密流程对应,对此不再赘述。
图3.7 AES解密流程图
4 实验流程
图4.1 实验流程图
本设计采用的是socket模拟两主机的发送与接收,通过本地环回地址127.0.0.1的6666端口发送与接收数据。首先生成RSA的公钥和私钥,公钥公开,私钥只有接收方有。然后发送方用RSA的公钥加密AES密钥“12345678”,用AES密钥加密需要发送的信息“NPU”,然后将二者打包发送给接收方。
接收方一直监听该端口,获得信息之后,首先用自己的RAS私钥解密出AES密钥“12345678”,之后再用AES密钥解密出密文,得到发送方发送的消息“NPU”。