1.了解分组密码的结构特点;
2.掌握传统分组密码结构AES,以及AES在两种工作模式CBC和CTR下的实现;
3.通过使用Python(推荐)或者C,编程分别实现CBC和CTR模式下的AES加密解密。
在本次实验中,需要实现两个加密/解密系统,一个在密文分组链接模式(CBC)下使用AES,另一个在计数器模式(CTR)中使用AES。
完成程序后,使用附件的test.txt中给出的四组密钥和密文(十六进制形式)来验证你的代码。
1.在两种模式下,16字节的加密IV都要求是随机生成的,并被添加到密文前面;
2.对于CBC加密,要求使用PKCS5填充方案;
3.对于AES的基本实现,你可以使用现有的加密库,如PyCrypto(Python),Crypto++(C++)或任何其他语言和库;
4.要求自己实现CBC和CTR模式,而不是直接调用AES库的内置功能;
CBC模式的全称是Cipher Block Chaining模式(密文分组链接模式),之所以叫这个名字,是因为密文分组像链条一样相互连接在一起。
在CBC模式中,首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密。
加解密如图:
文章借鉴:里面讲述了CBC实现中某些分组受影响的状况分析
(1)用CBC 是分组密码的一种工作模式,在加密前要对最后一块明文进行填充,实验要求使用 PKCS5 填充方案。
PKCS5 是按 8 字节分组对数据进行填充的:如果要填充 1 个字节,那填入的值就是 0x01;如果要填充 2 个字节,那么填入的值就是 0x02,以此类推。但若待加密数据长度正好为 8 的整数倍时,则需要填入 8 个 0x08。
h<0x07><0x07><0x07><0x07><0x07><0x07><0x07> 7
he<0x06><0x06><0x06><0x06><0x06><0x06> 6
hel<0x05><0x05><0x05><0x05><0x05> 5
hell<0x04><0x04><0x04><0x04> 4
hello<0x03><0x03><0x03> 3
hello <0x02><0x02> 2
hello w<0x01> 1
hello wo<0x08><0x08><0x08><0x08><0x08><0x08><0x08><0x08> 8 // 数据块
hello wor<0x07><0x07><0x07><0x07><0x07><0x07><0x07> 7
hello word<0x06><0x06><0x06><0x06><0x06><0x06> 6
文章借鉴:PKCS7 / PKCS5 填充算法
(2)加密过程只能串形实现
(3)加密公式:
C 1 = E ( K , [ P 1 ⨁ i v ] ) C_1=E(K,[P_1\bigoplus iv]) C1=E(K,[P1⨁iv])
C i = E ( K , [ P j ⨁ C ( j − 1 ) ] ) C_i=E(K,[P_j \bigoplus C_(j-1)]) Ci=E(K,[Pj⨁C(j−1)])
def encrypt(self, plainText, iv):
plainText = bytes(plainText)
blockSZ = self.block_size
iv = bytes(iv)
# PKCS#7填充规则
padLen = (blockSZ - len(plainText) % blockSZ)
plainText = plainText + bytes([padLen] * padLen)
#填充密文内容
cipherText = bytearray(blockSZ + len(plainText))
cipherText[0:blockSZ] = iv
for i in range(blockSZ, len(cipherText), blockSZ):
#块长度
j = i + blockSZ
#异或操作
after_xor=bytes(xor_block(plainText[i - blockSZ:i], cipherText[i - blockSZ:i]))
#调用加密接口
cipherText[i:j] = self.my_cipher.encrypt(after_xor)
return cipherText
(1)首先IV获取,根据题目中要求,密文前16个字节是被随机生成的IV值
(2)加密填充时应用的是PKCS7(PKCS5是其中的一个特例),所以解密时要去掉后面的填充
(3)解密可以并行实现,因为C1,C2…Cn均为已知,两个输入的异或均可以同时得到。
(4)解密式子:
P 1 = D ( K , C 1 ) ⨁ I V P_1=D(K,C_1) \bigoplus IV P1=D(K,C1)⨁IV
P j = D ( K , C j ) ⨁ C ( j − 1 ) P_j=D(K,C_j) \bigoplus C_(j-1) Pj=D(K,Cj)⨁C(j−1)
并行运算下,可以:
[ P 1 , P 2 . . . . . P N ] = [ D ( K , C 1 ) , D ( K , C 2 ) . . . . , D ( K , C N ) ) ] ⨁ [ I V , C 1 . . . . . , C N ] [P_1,P_2.....P_N]=[D(K,C_1),D(K,C_2)....,D(K,C_N))] \bigoplus [IV,C_1.....,C_N] [P1,P2.....PN]=[D(K,C1),D(K,C2)....,D(K,CN))]⨁[IV,C1.....,CN]
def decrypt(self, cipherText):
blockSZ = self.block_size
cipherText = bytes(cipherText)
after_decrypt=self.my_cipher.decrypt(cipherText[blockSZ:])
blocks = xor_block(after_decrypt, cipherText[:-blockSZ])
plainText = bytes(blocks)
return plainText[:-plainText[-1]]
CTR模式全称CounTeR模式(计数器模式)。CTR模式是一种通过将逐次累加的计数器进行加密来生成密钥流的流密码。自增的算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次。
每次加密时都会生成一个不同的值(nonce)作为计数器的初始值。当分组长度为128比特时,计数器的初始值可能如下图所示:在这里插入图片描述
加密过程中计数器的值会产生如下变化:
借鉴文章:CTR模式解读
首先生成count值
def _get_timers(self,iv, msgLen):
#iv: 计时器初值
#msgLen: 密文长度(明文)
blockSZ = self.block_size
blocks = int((msgLen + blockSZ - 1) //blockSZ)
timer = int_from_bytes(iv)
timers = iv
for i in range(1, blocks):
timer += 1
timers += int_to_bytes(timer)
return timers
然后进行加密过程
def encrypt(self, plainText, count):
count= bytes(count)
#各个计数器值
counters= self._get_timers(count,len(plainText))
blocks = xor_block(self._cipher.encrypt(counters), plainText)
ciphertext = bytes(blocks)
return count+ciphertext[:len(plainText)]
def decrypt(self, cipherText):
blockSZ = self.block_size
# 加密和解密只有输入不同
pt = self.encrypt(cipherText[blockSZ:], cipherText[:blockSZ])
return pt[blockSZ:]
import Crypto.Cipher.AES as AES
import operator
from binascii import a2b_hex
from Crypto import Random
# 进行异或(bytes ^ bytes) 按位异或,迭代器
def xor_block(left, right):
return map(operator.xor,left,right)
#CBC模式实现:
class CBC_Cipher(object):
def __init__(self, key):
self.my_cipher = AES.new(key, AES.MODE_ECB)
self.block_size = AES.block_size
def encrypt(self, plainText, iv):
plainText = bytes(plainText)
blockSZ = self.block_size
iv = bytes(iv)
# PKCS#7填充规则
padLen = (blockSZ - len(plainText) % blockSZ)
plainText = plainText + bytes([padLen] * padLen)
#填充密文内容
cipherText = bytearray(blockSZ + len(plainText))
cipherText[0:blockSZ] = iv
for i in range(blockSZ, len(cipherText), blockSZ):
#块长度
j = i + blockSZ
#异或操作
after_xor=bytes(xor_block(plainText[i - blockSZ:i], cipherText[i - blockSZ:i]))
#调用加密接口
cipherText[i:j] = self.my_cipher.encrypt(after_xor)
return cipherText
def decrypt(self, cipherText):
blockSZ = self.block_size
cipherText = bytes(cipherText)
after_decrypt=self.my_cipher.decrypt(cipherText[blockSZ:])
blocks = xor_block(after_decrypt, cipherText[:-blockSZ])
plainText = bytes(blocks)
return plainText[:-plainText[-1]]
#**************************************
#CTR模式实现
#转化为bytes类型
def int_to_bytes(x):
return x.to_bytes((x.bit_length()+7)//8, 'big')
#bytes转为十进制整数
def int_from_bytes(xbytes):
return int.from_bytes(xbytes, 'big')
class CTRCipher(object):
def __init__(self, key):
self._cipher = AES.new(key, AES.MODE_ECB)
self.block_size = AES.block_size
def encrypt(self, plainText, count):
count= bytes(count)
#各个计数器值
counters= self._get_timers(count,len(plainText))
blocks = xor_block(self._cipher.encrypt(counters), plainText)
ciphertext = bytes(blocks)
return count+ciphertext[:len(plainText)]
def decrypt(self, cipherText):
blockSZ = self.block_size
# 加密和解密只有输入不同
pt = self.encrypt(cipherText[blockSZ:], cipherText[:blockSZ])
return pt[blockSZ:]
#生成各个计数器的值
def _get_timers(self,iv, msgLen):
#iv: 计时器初值
#msgLen: 密文长度(明文)
blockSZ = self.block_size
blocks = int((msgLen + blockSZ - 1) //blockSZ)
timer = int_from_bytes(iv)
timers = iv
for i in range(1, blocks):
timer += 1
timers += int_to_bytes(timer)
return timers
if __name__ == '__main__':
#CBC模式
cbc_key = "140b41b22a29beb4061bda66b6747e14"
cbc_cipher1 ="4ca00ff4c898d61e1edbf1800618fb2828a226d160dad07883d04e008a7897ee2e4b7465d5290d0c0e6c6822236e1daafb94ffe0c5da05d9476be028ad7c1d81"
cbc_cipher2 ="5b68629feb8606f9a6667670b75b38a5b4832d0f26e1ab7da33249de7d4afc48e713ac646ace36e872ad5fb8a512428a6e21364b0c374df45503473c5242a253"
cbc_key=a2b_hex(cbc_key)
decryptor1=CBC_Cipher(cbc_key)
print("CBC模式下第一个明文结果:",decryptor1.decrypt(a2b_hex(cbc_cipher1)))
print("CBC模式下第二个明文结果:",decryptor1.decrypt(a2b_hex(cbc_cipher2)))
iv = Random.new().read(AES.block_size)
print("iv随机生成值",iv)
a=decryptor1.encrypt(b"this is what i want",iv)
print("CBC模式下加密",a)
print("加密后解密结果:",decryptor1.decrypt(a))
print("****************************************")
#CTR模式
ctr_key = "36f18357be4dbd77f050515c73fcf9f2"
ctr_cipher1 = "69dda8455c7dd4254bf353b773304eec0ec7702330098ce7f7520d1cbbb20fc388d1b0adb5054dbd7370849dbf0b88d393f252e764f1f5f7ad97ef79d59ce29f5f51eeca32eabedd9afa9329"
ctr_cipher2 = "770b80259ec33beb2561358a9f2dc617e46218c0a53cbeca695ae45faa8952aa0e311bde9d4e01726d3184c34451"
decryptor2=CTRCipher(a2b_hex(ctr_key))
print("CTR模式下第一个明文结果:",decryptor2.decrypt(a2b_hex(ctr_cipher1)))
print("CTR模式下第二个明文结果:",decryptor2.decrypt(a2b_hex(ctr_cipher2)))
a=decryptor2.encrypt(b"this is what i want",iv)
print("CTR加密后的结果:",a)
print("CTR解密后",decryptor2.decrypt(a))
开始实验要求理解错了,花了好长时间去看接口啊,我不能白写,我得贴上来==。
from Crypto.Cipher import AES
from binascii import b2a_hex,a2b_hex
from Crypto.Util import Counter
import binascii
import os
from Crypto import Random
BS = AES.block_size
class aesCBC:
#CBC加密
def encrypt(self,key,text):
#若密文数量不是块大小的倍数,补齐
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
text = pad(text)
#随机生成IV向量
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
return b2a_hex(iv + cipher.encrypt(text))
#CBC解密
def decrypt(self,key,cipher):
cipher=a2b_hex(cipher)
#获得IV向量值
iv=cipher[:16]
cipher=cipher[16:]
res_cipher=AES.new(key, AES.MODE_CBC, iv)
#从后向前除去填充值
unpad = lambda s: s[0:-(s[-1])]
return unpad(res_cipher.decrypt(cipher))
class aesCTR:
def int_of_string(self,s):
return int(binascii.hexlify(s), 16)
def encrypt_message(self,key, plaintext):
count= os.urandom(16)
ctr = Counter.new(128, initial_value=self.int_of_string(count))
aes = AES.new(a2b_hex(key), AES.MODE_CTR, counter=ctr)
return count+ aes.encrypt(plaintext)
def decrypt_message(self,key, ciphertext):
count=ciphertext[:16]
ctr = Counter.new(128, initial_value=self.int_of_string(count))
aes =AES.new(a2b_hex(key), AES.MODE_CTR, counter=ctr)
return aes.decrypt(ciphertext[16:])
if __name__ == '__main__':
#CBC模式
cbc_key = "140b41b22a29beb4061bda66b6747e14"
cbc_cipher1 ="4ca00ff4c898d61e1edbf1800618fb2828a226d160dad07883d04e008a7897ee2e4b7465d5290d0c0e6c6822236e1daafb94ffe0c5da05d9476be028ad7c1d81"
cbc_cipher2 ="5b68629feb8606f9a6667670b75b38a5b4832d0f26e1ab7da33249de7d4afc48e713ac646ace36e872ad5fb8a512428a6e21364b0c374df45503473c5242a253"
decryptor1=aesCBC()
cbc_key=a2b_hex(cbc_key)
print("CBC模式下第一个明文结果:",decryptor1.decrypt(cbc_key,cbc_cipher1))
print("CBC模式下第二个明文结果:",decryptor1.decrypt(cbc_key,cbc_cipher2))
a=decryptor1.encrypt(cbc_key,"this is what i want")
print("CBC模式下加密",a)
print("加密后解密结果:",decryptor1.decrypt(cbc_key,a))
print("****************************************")
#CTR模式
ctr_key = "36f18357be4dbd77f050515c73fcf9f2"
ctr_cipher1 = "69dda8455c7dd4254bf353b773304eec0ec7702330098ce7f7520d1cbbb20fc388d1b0adb5054dbd7370849dbf0b88d393f252e764f1f5f7ad97ef79d59ce29f5f51eeca32eabedd9afa9329"
ctr_cipher2 = "770b80259ec33beb2561358a9f2dc617e46218c0a53cbeca695ae45faa8952aa0e311bde9d4e01726d3184c34451"
decryptor2=aesCTR()
a=decryptor2.encrypt_message(ctr_key,"this is what i want")
print("CTR加密后的结果:",a)
print("CTR解密后",decryptor2.decrypt_message(ctr_key,a))
print("CTR模式下第一个明文结果:",decryptor2.decrypt_message(ctr_key,a2b_hex(ctr_cipher1)))
print("CTR模式下第二个明文结果:",decryptor2.decrypt_message(ctr_key,a2b_hex(ctr_cipher2)))