以实验报告的形式,采用python语言实现密码学里的经典实验
目录
一、古典密码
1.移位密码
(1)设计思路
移位密码是将明文的每一个字母用它后面第k个字母代替,属于单表代替的一种,单表代替是加解密使用一个固定的替换表。
加密函数:
解密函数:
(2)实现代码
# 加密
def shift_cipher_encrypt(plaintext, key):
ciphertext = ""
for char in plaintext:
# 判断输入的是否为字母
if char.isalpha():
# 按字母的大小写进行加密
if (char.isupper()):
ciphertext += chr((ord(char) + key - 65) % 26 + 65)
else:
ciphertext += chr((ord(char) + key - 97) % 26 + 97)
else:
ciphertext += char
return ciphertext
# 解密
def shift_cipher_decrypt(ciphertext, key):
plaintext = ""
for char in ciphertext:
# 判断输入的是否为字母
if char.isalpha():
# 按字母的大小写进行解密
if (char.isupper()):
plaintext += chr((ord(char) - key - 65) % 26 + 65)
else:
plaintext += chr((ord(char) - key - 97) % 26 + 97)
else:
plaintext += char
return plaintext
# 主函数
def main():
while True:
x = input("请选择模式(1.加密 2.解密 3.退出):")
if x == "1":
print("-------------加密过程-------------")
plaintext = input("请输入明文: ")
key = int(input("请输入密钥: "))
print("加密结果: ", shift_cipher_encrypt(plaintext, key))
print("--------------------------------")
elif x == "2":
print("-------------解密过程-------------")
ciphertext = input("请输入密文: ")
key = int(input("请输入密钥: "))
print("解密结果: ", shift_cipher_decrypt(ciphertext, key))
print("--------------------------------")
else:
break
if __name__ == '__main__':
main()
(3)实验结果
使用移位密码加密:
使用移位密码解密:
恢复用移位密码加密的片段:
题目:在Alice和Bob的保密通信中,传送的密文是“rjjy rj ts ymj xfggfym bj bnqq inxhzxx ymj uqfs”,如果他们使用的是移位密码算法,试解密其通信内容
思路:密钥遍历(0~25),观察解密得到的结果
for key in range(26):
c = "rjjy rj ts ymj xfggfym bj bnqq inxhzxx ymj uqfs"
m = shift_cipher_decrypt(c,key)
print(m,"对应密钥为",key)
可以看到当密钥为5时解密得到的结果是有意义的。
(4)频率分析法
移位密码常用的分析方法是频率分析法,利用字母出现频率的统计特性,在没有密钥的情况下推测出密钥并解密。
使用频率分析法对密文“rjjy rj ts ymj xfggfym bj bnqq inxhzxx ymj uqfs”进行解密,结果如下
具体代码:通过统计频率并利用卡方检验来猜测移位密码的密钥
from collections import Counter
import string
# 加密
def shift_cipher_encrypt(plaintext, key):
ciphertext = ""
for char in plaintext:
# 判断输入的是否为字母
if char.isalpha():
# 按字母的大小写进行加密
if (char.isupper()):
ciphertext += chr((ord(char) + key - 65) % 26 + 65)
else:
ciphertext += chr((ord(char) + key - 97) % 26 + 97)
else:
ciphertext += char
return ciphertext
# 解密
def shift_cipher_decrypt(ciphertext, key):
plaintext = ""
for char in ciphertext:
# 判断输入的是否为字母
if char.isalpha():
# 按字母的大小写进行解密
if (char.isupper()):
plaintext += chr((ord(char) - key - 65) % 26 + 65)
else:
plaintext += chr((ord(char) - key - 97) % 26 + 97)
else:
plaintext += char
return plaintext
# 频率分析
# 标准英文文本的字母频率分布
ENGLISH_FREQUENCY = {
'a': 0.08167, 'b': 0.01492, 'c': 0.02782, 'd': 0.04253, 'e': 0.12702,
'f': 0.02228, 'g': 0.02015, 'h': 0.06094, 'i': 0.06966, 'j': 0.00153,
'k': 0.00772, 'l': 0.04025, 'm': 0.02406, 'n': 0.06749, 'o': 0.07507,
'p': 0.01929, 'q': 0.00095, 'r': 0.05987, 's': 0.06327, 't': 0.09056,
'u': 0.02758, 'v': 0.00978, 'w': 0.02360, 'x': 0.00150, 'y': 0.01974,
'z': 0.00074
}
# 计算文本中每个字母的出现频率
def calculate_frequency(text):
# 将输入的文本全部转换为小写,以确保大小写不影响统计
text = text.lower()
letter_counts = Counter(char for char in text if char in string.ascii_lowercase)
total_count = sum(letter_counts.values())
return {char: count / total_count for char, count in letter_counts.items()}
# 计算观测到的字母频率与预期的字母频率之间的卡方统计量
def chi_squared_stat(observed, expected):
return sum((observed.get(char, 0) - expected[char]) ** 2 / expected[char] for char in expected)
# 对密文进行频率分析,尝试找出最可能的密钥
def frequency_analysis(ciphertext):
ciphertext = ciphertext.lower()
best_key = None
best_chi_squared = float('inf')
# 尝试所有可能的密钥(0到25)
for key in range(26):
decrypted_text = ''.join(
chr((ord(char) - ord('a') - key) % 26 + ord('a')) if char in string.ascii_lowercase else char
for char in ciphertext
)# 使用当前密钥对密文进行解密。对于每个字母字符,计算其位移后的新字符;对于非字母字符,保持原样。
observed_freq = calculate_frequency(decrypted_text)
chi_squared = chi_squared_stat(observed_freq, ENGLISH_FREQUENCY)
if chi_squared < best_chi_squared:
best_chi_squared = chi_squared
best_key = key
return best_key
# 主函数
def main():
while True:
x = input("请选择模式(1.加密 2.解密 3.频率分析 4.退出):")
if x == "1":
print("-------------加密过程-------------")
plaintext = input("请输入明文: ")
key = int(input("请输入密钥: "))
print("加密结果: ", shift_cipher_encrypt(plaintext, key))
print("--------------------------------")
elif x == "2":
print("-------------解密过程-------------")
ciphertext = input("请输入密文: ")
key = int(input("请输入密钥: "))
print("解密结果: ", shift_cipher_decrypt(ciphertext, key))
print("--------------------------------")
elif x == "3":
print("-------------频率分析过程-------------")
ciphertext = input("请输入密文: ")
best_key = frequency_analysis(ciphertext)
print("推测的密钥: ", best_key)
print("解密结果: ", shift_cipher_decrypt(ciphertext, best_key))
print("--------------------------------")
else:
break
if __name__ == '__main__':
main()
2.仿射密码
(1)设计思路
仿射密码也是单表代替密码,通过仿射变换来构造代替表。
密钥k=(a,b),其中gcd(a,26)=1
加密函数:
解密函数:
(2)实现代码
# 加密
def affine_cipher_encrypt(plaintext, a, b):
ciphertext = ""
for char in plaintext:
# 判断输入的是否为字母
if char.isalpha():
# 按字母的大小写进行加密
if (char.isupper()):
ciphertext += chr((a*(ord(char) - 65) +b) % 26 + 65)
else:
ciphertext += chr((a*(ord(char) - 97) +b) % 26 + 97)
else:
ciphertext += char
return ciphertext
# 解密
def affine_cipher_decrypt(ciphertext, a, b):
plaintext = ""
a_inv = mod_inverse(a,26)
for char in ciphertext:
# 判断输入的是否为字母
if char.isalpha():
# 按字母的大小写进行解密
if (char.isupper()):
plaintext += chr(a_inv*((ord(char) - 65)-b) % 26 + 65)
else:
plaintext += chr(a_inv*((ord(char) - 97)-b) % 26 + 97)
else:
plaintext += char
return plaintext
def gcd(a, b):
while b != 0:
a, b = b, a % b
return a
def mod_inverse(a,m):
for x in range(1, m):
if (a * x) % m == 1:
return x
raise ValueError("没有模逆存在")
# 主函数
def main():
while True:
x = input("请选择模式(1.加密 2.解密 3.退出):")
if x == "1":
print("-------------加密过程-------------")
plaintext = input("请输入明文: ")
print("请输入密钥:")
a = int(input("a= "))
b = int(input("b= "))
if gcd(a, 26) != 1:
raise ValueError("a and 26 must be coprime.")
print("加密结果: ", affine_cipher_encrypt(plaintext, a, b))
print("--------------------------------")
elif x == "2":
print("-------------解密过程-------------")
ciphertext = input("请输入密文: ")
print("请输入密钥:")
a = int(input("a= "))
b = int(input("b= "))
if gcd(a, 26) != 1:
raise ValueError("a and 26 must be coprime.")
print("解密结果: ", affine_cipher_decrypt(ciphertext, a, b))
print("--------------------------------")
else:
break
if __name__ == '__main__':
main()
(3)实验结果
使用仿射密码加密:
使用仿射密码解密:
3.维吉尼亚密码
(1)设计思路
维吉尼亚密码是一种多表代替密码,同一明文字符可以被加密成不同的密文字符。使用一个固定长度的词组作密钥,通过重复密钥来决定每个字母的移位。
加密函数:
解密函数:
设计思路:把明密文和密钥放入列表中用下标索引读取再加解密
(2)实现代码
# 加密
def vigenere_cipher_encrypt(plaintext, key):
plaintext = ''.join(char for char in plaintext if 'a' <= char <= 'z')
ciphertext = ""
key_length = len(key)
key_int = [ord(i) for i in key]
plaintext_int = [ord(i) for i in plaintext]
for i in range(len(plaintext_int)):
value = (plaintext_int[i] - 97 + key_int[i % key_length] - 97) % 26
ciphertext += chr(value + 97)
return ciphertext
# 解密
def vigenere_cipher_decrypt(ciphertext, key):
plaintext = ""
key_length = len(key)
key_int = [ord(i) for i in key]
ciphertext_int = [ord(i) for i in ciphertext]
for i in range(len(ciphertext_int)):
value = (ciphertext_int[i] - key_int[i % key_length]) % 26
plaintext += chr(value + 97)
return plaintext
# 主函数
def main():
while True:
x = input("请选择模式(1.加密 2.解密 3.退出):")
if x == "1":
print("-------------加密过程-------------")
plaintext = input("请输入明文: ")
key = input("请输入密钥: ")
print("加密结果: ", vigenere_cipher_encrypt(plaintext, key))
print("--------------------------------")
elif x == "2":
print("-------------解密过程-------------")
ciphertext = input("请输入密文: ")
key = input("请输入密钥: ")
print("解密结果: ", vigenere_cipher_decrypt(ciphertext, key))
print("--------------------------------")
else:
break
if __name__ == '__main__':
main()
注:在解密过程中应该将密文字符和密钥字符的ASCLL码值各减去97再相减,由于在相减的过程中97被消去,故代码中直接将密文字符和密钥字符的ASCLL值相减
(3)实验结果
加密结果:
解密结果:
4.维吉尼亚密码分析
破译维吉尼亚密码的关键在于确定密钥的长度。一旦知道了密钥长度,密文就可以被分成多列,每列对应一个移位密码,从而可以单独进行破解。这里介绍一个常用的方法来确定密钥长度:重合指数法。
(1)重合指数的定义
重合指数(Index of Coincidence, IC)是用于评估文本中两个随机选取的字母相同的概率的统计量,在密码分析中帮助确定密钥长度。设某种语言由 m 个字母组成,每个字母 i出现的概率为 。重合指数是指两个随机选取的字母相同的概率,记为 IC,其计算公式为:。
(2)原理
使用重和指数区分随机文本和自然语言文本其原理是:随机文本和自然语言文本中字母出现的概率不同。
随机文本的IC值:对于完全随机的文本,由于每个字母出现的概率相等,重合指数接近于0.038。如果一个文本是由26个字母随机组成的,每个字母出现的概率为 ,两个字母相同的概率为 ,由于共有26个字母,故:。
自然语言文本的IC值:对于自然语言(如英文)文本,由于某些字母出现的频率较高(例如,字母E在英文中出现的频率最高),重合指数接近于0.065。英文文本中的字母出现频率不同,假设各字母出现的概率分别为 那么两个字母相同的概率为:
分组密文的IC值:在使用维吉尼亚密码加密时,如果我们猜测的密钥长度m是正确的,则密文可以被分成m个组,每一组都类似于自然语言文本的移位密码。因此,每一组的IC值应接近自然语言文本的IC值,即0.065。
(3)具体步骤
-
猜测密钥长度 m:从1开始逐渐增加,依次猜测不同的密钥长度。
-
将密文分为 m 组:将密文按密钥长度 m 分组。每一组相当于一个移位密码。
-
计算每组的IC值:
- 对于每一组,计算每个字母在该组中出现的次数 。
- 计算该组的重合指数(IC值)。
-
计算 m 组的IC均值:
- 计算所有分组的IC均值。
- 若均值接近0.065,则 m 是正确的密钥长度。
- 若均值接近0.038,则 m 不是正确的密钥长度。
(4)实现代码
代码思路:
1.从1开始猜测密钥长度(设置一个最长的值max_key_length)
2.使用重合指数法计算每一种密钥长度所对应的IC均值
3.比较IC均值,取IC均值最接近0.065对应的密钥长度
4.根据猜测的密钥长度将密文分组,每一组对应一个移位密码,根据移位密码的频率分析法确定密钥
5.根据密钥对每一组移位密码解密
具体代码:
from collections import Counter
# 计算重合指数
def calculate_ic(text):
n = len(text)
freqs = Counter(text)
ic = sum(f * (f - 1) for f in freqs.values()) / (n * (n - 1))
return ic
# 通过重合指数法找到最可能的密钥长度
def find_key_length_ic(ciphertext, max_key_length=20):
ciphertext = ''.join(char for char in ciphertext if 'a' <= char <= 'z')
best_key_length = 1
best_ic_mean = float('inf')
for m in range(1, max_key_length + 1):
ic_values = []
for i in range(m):
group = ''.join(ciphertext[j] for j in range(i, len(ciphertext), m))
if len(group) > 1:
ic_values.append(calculate_ic(group))
if ic_values:
ic_mean = sum(ic_values) / len(ic_values)
print(f"密钥长度 {m} 的 IC 均值为 {ic_mean:.4f}")
if abs(ic_mean - 0.065) < abs(best_ic_mean - 0.065):
best_ic_mean = ic_mean
best_key_length = m
return best_key_length
# 解密函数
def vigenere_decrypt(ciphertext, key):
key_length = len(key)
key_as_int = [ord(i) for i in key]
ciphertext_int = [ord(i) for i in ciphertext]
plaintext = ''
for i in range(len(ciphertext_int)):
value = (ciphertext_int[i] - key_as_int[i % key_length]) % 26
plaintext += chr(value + 65)
return plaintext.lower()
# 找到密钥的每一个字母
def find_vigenere_key(ciphertext, key_length):
ciphertext = ''.join(char for char in ciphertext if 'a' <= char <= 'z')
key = ''
for i in range(key_length):
segment = ''.join(ciphertext[j] for j in range(i, len(ciphertext), key_length))
key += find_caesar_shift(segment)
return key
# 通过频率分析法找到凯撒密码的偏移量
def find_caesar_shift(segment):
english_frequencies = {
'a': 0.082, 'b': 0.015, 'c': 0.028, 'd': 0.043, 'e': 0.127, 'f': 0.022,
'g': 0.020, 'h': 0.061, 'i': 0.070, 'j': 0.002, 'k': 0.008, 'l': 0.040,
'm': 0.024, 'n': 0.067, 'o': 0.075, 'p': 0.019, 'q': 0.001, 'r': 0.060,
's': 0.063, 't': 0.091, 'u': 0.028, 'v': 0.010, 'w': 0.023, 'x': 0.001,
'y': 0.020, 'z': 0.001
}
segment_frequencies = Counter(segment)
n = len(segment)
chi_squared_statistics = []
for shift in range(26):
chi_squared = 0
for letter in english_frequencies:
observed = segment_frequencies[chr((ord(letter) - 97 + shift) % 26 + 97)]
expected = english_frequencies[letter] * n
chi_squared += (observed - expected) ** 2 / expected
chi_squared_statistics.append(chi_squared)
return chr(chi_squared_statistics.index(min(chi_squared_statistics)) + 97)
def main():
# 示例密文
#ciphertext = "vvhqwvvrhmusgjgthkihtssejchlsfcbgvwcrlryqtfsvgahwkcuhwauglqhnslrljshbltspisprdxljsveegh lqwkasskuwepwqtwvspgoelkcqyfnsvwljsniqkgnrgybwlwgoviokhkazkqkxzgyhcecmeiujoqkwfwvefqhkijrclrlkbienqfrjljsdhgrhlsfqtwlauqrhwdmwlgusgikkflryvcwvspgpmlkassjvoqxeggveyg gzmljcxxljsvpaivwikvrdrygfrjljslveggveyggeiapuuisfpbtgnwwmuczrvtwglrwugumnczvile"
ciphertext = input("请输入密文:")
key_length = find_key_length_ic(ciphertext)
print(f"推测的密钥长度为: {key_length}")
key = find_vigenere_key(ciphertext, key_length)
print(f"推测的密钥为: {key}")
plaintext = vigenere_decrypt(ciphertext, key)
print(f"解密后的明文为: {plaintext}")
if __name__ == '__main__':
main()
实验结果:
二、分组密码
分组密码也叫块密码(Block Cipher),明、密文分组(64bit,128bit等)进行加解密,加解密使用相同的密钥。
1.DES加密算法
(1)设计思路
算法原理:DES算法原理及实现 (ruanx.net)(这篇介绍很详细)
加密流程:
设计目标:给定明文0x0123456789abcdef,密钥0x1f1f1f1f0e0e0e0e,得到相应密文
设计思路:
1.明密文、密钥的输入输出处理(输入和输出的都是是十六进制数,但在加密过程中使用的是元素为0\1的列表)
(1)十六进制数与n位二进制列表的转换:
def hex2bit(hex_val,n): bit_list = [int(i) for i in bin(hex_val)[2:].zfill(n)]# 确保hex_val是一个int整型 return bit_list assert hex2bit(0x1a,10) == [0, 0, 0, 0, 0, 1, 1, 0, 1, 0]# 测试案例def bit2hex(bit_list): bit_str = ''.join(str(bit) for bit in bit_list)# 将二进制列表里的元素转化为二进制字符串 hex_val = hex(int(bit_str ,2))# 将二进制字符串转换为十六进制字符串 return hex_val assert bit2hex([0, 0, 0, 0, 0, 1, 1, 0, 1, 0]) == '0x1a'2.基础运算
(1)循环左移n位
def leftRotate(bit_list, n): return bit_list[n:] + bit_list[:n] assert leftRotate([1, 1, 0, 0], 3) == [0, 1, 1, 0](2)异或运算
def bitXor(bit_list_1, bit_list_2): return [a^b for a, b in zip(bit_list_1, bit_list_2)] assert bitXor([1, 1, 1, 0], [0, 1, 0, 1]) == [1, 0, 1, 1](3)置换操作
给定一个置换列表,将二进制列表中的元素进行换位,以IP置换为例
def IP(a): ip = [58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7] return [a[x - 1] for x in ip]# 返回一个列表,列表中的第一个元素是a的第58个元素(即a[57]),第2个元素是a的第50个元素(即a[49])3.S盒的输入输出(48bit→32bit)
(1)DES有8个S盒,输入的48bit分成8组6bit,每组进入一个S盒。每个S盒都是一个4行16列的表,表中每个元素为一个4位二进制(以十进制数表示,即0~15),具体的S盒替代过程如下:
输入6bit,取最高位和最低位作为行数,中间4位作为列数
在S盒中找对应行数和列数的值作为输出
(2)对于一个S盒(长度为4*16的列表),怎么找到特定行列的元素
思路:例如一个2*4的列表,两行可以用1比特表示(第0行:0;第1行:1),四列用2比特表示(第0列:00;第1列:01;第2列:10;第3列:11),那么这个列表中每个元素的索引位置的二进制表示如下:
[000 001 010 011
100 101 110 111]
说明行号的比特位在列号的比特位前面
举例:现在想找到一个2*4列表(记为a)的第1行第3列的元素,行号1=0b1,列号3=0b11,拼起来就是0b111(行在前列在后),对应十进制的7,那么这个元素就是a[7]
(3)具体代码
# 导入numpy包用于数组处理
import numpy as np
a = np.array(a).reshape(8, 6)# 把输入的48bit分成8组,每组单独处理,注意重塑之前要先用array转为可重塑的数组
(2)实现代码
import numpy as np
# 把输入的十六进制数转化为二进制列表表示
def hex2bin(hex_val,n):
bin_list = [int(i) for i in bin(hex_val)[2:].zfill(n)]
return bin_list
# 把输入的二进制列表转化成十六进制字符串
def bin2hex(bit_list):
bin_str = ''.join(str(bit) for bit in bit_list)# 将二进制列表里的元素转化为二进制字符串
hex_val = hex(int(bin_str ,2))# 将二进制字符串转换为十六进制字符串
return hex_val
# 循环左移运算
def leftRotate(bit_list, n):
return bit_list[n:] + bit_list[:n]
assert leftRotate([1, 1, 0, 0], 3) == [0, 1, 1, 0]
# 异或运算
def bitXor(bit_list_1, bit_list_2):
return [a^b for a, b in zip(bit_list_1, bit_list_2)]
assert bitXor([1, 1, 1, 0], [0, 1, 0, 1]) == [1, 0, 1, 1]
# IP置换(IP置换后分成左右两部分)
def IP(a):
ip1 = [58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8]
ip2 = [ 57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7]
return [a[x-1] for x in ip1], [a[x-1] for x in ip2]
# IP置换的逆,也是最终置换
def FP(a):
fp = [40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25]
return [a[x-1] for x in fp]
# 选择置换1
def PC1(key):
pc1_l = [57, 49, 41, 33, 25, 17, 9,
1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27,
19, 11, 3, 60, 52, 44, 36]
pc1_r = [63, 55, 47, 39, 31, 23, 15,
7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29,
21, 13, 5, 28, 20, 12, 4]
return [key[x - 1] for x in pc1_l], [key[x - 1] for x in pc1_r]
# 选择置换2
def PC2(key):
pc2 = [14, 17, 11, 24, 1, 5,
3, 28, 15, 6, 21, 10,
23, 19, 12, 4, 26, 8,
16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55,
30, 40, 51, 45, 33, 48,
44, 49, 39, 56, 34, 53,
46, 42, 50, 36, 29, 32]
return [key[x-1] for x in pc2]
# 密钥扩展算法(从64bit的密钥生成16个48bit的子密钥)
def keyGen(key):
assert len(key) == 64
l,r = PC1(key)
off = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]# 循环左移的位数
res = []
for i in range(16):
l = leftRotate(l, off[i])
r = leftRotate(r, off[i])
res.append(PC2(l + r))
return res
# S盒替换
def S(a):
assert len(a) == 48
S_box = [[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]]
a = np.array(a).reshape(8,6)# 注意,数组重塑行列之前要把列表转化为数组即先使用array
res = []
for i in range(8):
# 把二进制列表转化为十进制数
str_val = ''.join(str(num) for num in [a[i][0],a[i][5],a[i][1],a[i][2],a[i][3],a[i][4]])
j = int(str_val, base = 2)
s = S_box[i][j]
res += [int(x) for x in bin(s)[2:].zfill(4)]
return res
# E扩展(32比特→48比特)
def Expand(a):
assert len(a) == 32
e = [32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13,
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29,
28, 29, 30, 31, 32, 1]
return [a[x-1] for x in e]
# P置换
def P(a):
assert len(a) == 32
p = [16, 7, 20, 21,
29, 12, 28, 17,
1, 15, 23, 26,
5, 18, 31, 10,
2, 8, 24, 14,
32, 27, 3, 9,
19, 13, 30, 6,
22, 11, 4, 25]
return [a[x-1] for x in p]
# F函数
def F(r, k):
assert len(r) == 32
assert len(k) == 48
r = Expand(r)
s = S(bitXor(r, k))
p = P(s)
return p
# 轮函数
def goRound(l,r,sub_key):
return r, bitXor(l, F(r, sub_key))
def des(plaintext, key, method):
subkeys = keyGen(hex2bin(key, 64))
if method == 'decrypt':
subkeys = subkeys[::-1]# 加解密的算法一致,只是密钥使用顺序相反
l, r = IP(hex2bin(plaintext, 64))
for i in range(16):
l ,r = goRound(l, r, subkeys[i])
return bin2hex(FP(r + l))
def main():
while True:
x = input("请选择模式:1.DES加密 2.DES解密 3.退出")
if x == '1':
print("-------------加密过程-------------")
plaintext = int(input("请输入明文:"), 16)
key = int(input("请输入密钥:"), 16)
ciphertext = des(plaintext,key,'encrypt')
print("加密结果:",ciphertext)
print("--------------------------------")
elif x == '2':
print("-------------解密过程-------------")
ciphertext = int(input("请输入密文:"), 16)
key = int(input("请输入密钥:"), 16)
plaintext = des(ciphertext, key, 'decrypt')
print("解密结果:", plaintext)
print("--------------------------------")
else:
break
if __name__ == '__main__':
main()
(3)实验结果
(a)测试:
加密:明文:0x11aabbccddeeff01;密钥:0xcafababedeadbeaf ;加密后的结果:0x2973a7e54ec730a3
解密:密文:0x2973a7e54ec730a3;密钥:0xcafababedeadbeaf;解密后的结果:0x11aabbccddeeff01
(b) 加密结果:
2.AES加密算法
(1)设计思路
state:是一个4×4的矩阵,由16个字节组成,例如:
state = [
[0x00,0x04,0x08,0x0c],
[0x01,0x05,0x09,0x0d],
[0x02,0x06,0x0a,0x0e],
[0x03,0x07,0x0b,0x0f]
]
(2)实现代码
S_BOX = [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
]
R_CON = [0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000]
# 字节代换
def sub_bytes(state):
for i in range(4):
for j in range(4):
state[i][j] = S_BOX[state[i][j]]
return state
assert sub_bytes([[0x1a,0x1a,0x1a,0x1a],[0x1a,0x1a,0x1a,0x1a],[0x1a,0x1a,0x1a,0x1a],[0x1a,0x1a,0x1a,0x1a]]) == \
[[0xA2,0xA2,0xA2,0xA2],[0xA2,0xA2,0xA2,0xA2],[0xA2,0xA2,0xA2,0xA2],[0xA2,0xA2,0xA2,0xA2]]
# 行移位
def shift_rows(state):
for i in range(4):
state[i] = state[i][i:] + state[i][:i]
return state
assert shift_rows([[0x00,0x04,0x08,0x0c],[0x01,0x05,0x09,0x0d],[0x02,0x06,0x0a,0x0e],[0x03,0x07,0x0b,0x0f]]) == \
[[0x00,0x04,0x08,0x0c],[0x05,0x09,0x0d,0x01],[0x0a,0x0e,0x02,0x06],[0x0f,0x03,0x07,0x0b]]
def gmul(a, b):
"""GF(2^8)有限域上的乘法"""
p = 0
for _ in range(8):
if b & 1:
p ^= a
hi_bit_set = a & 0x80 # 检查a的第7位是否为1
a <<= 1
if hi_bit_set:
a ^= 0x1b # 0x11b是AES中使用的不可约多项式
b >>= 1
return p & 0xFF
assert gmul(0xc3,0x23) == 0x4d
def mix_single_column(a):
"""对一列进行列混合"""
t = a[0] ^ a[1] ^ a[2] ^ a[3]
b0 = a[0] ^ t ^ gmul(a[0] ^ a[1], 0x02)
b1 = a[1] ^ t ^ gmul(a[1] ^ a[2], 0x02)
b2 = a[2] ^ t ^ gmul(a[2] ^ a[3], 0x02)
b3 = a[3] ^ t ^ gmul(a[3] ^ a[0], 0x02)
return [b0, b1, b2, b3]
assert mix_single_column([0x87, 0x6e, 0x46, 0xa6]) == [0x47, 0x37, 0x94, 0xed]
# 列混合
def mix_columns(state):
for i in range(4):
col = [state[j][i] for j in range(4)]
col = mix_single_column(col)
for j in range(4):
state[j][i] = col[j]
return state
def sub_word(word):
"""对一个4字节的word执行S-box替换"""
return [S_BOX[b] for b in word]
def rot_word(word):
"""对一个4字节的word执行循环左移"""
return word[1:] + word[:1]
# 密钥扩展
def key_expansion(key):
assert len(key) == 16 # AES-128 使用16字节的密钥
# 生成44个32位的字
round_keys = []
for i in range(4):
round_keys.append([key[i * 4], key[i * 4 + 1], key[i * 4 + 2], key[i * 4 + 3]])
for i in range(4, 4 * (10 + 1)):
temp = round_keys[i - 1]
if i % 4 == 0:
temp = sub_word(rot_word(temp))
rcon = (R_CON[i // 4 - 1] >> 24) # 提取 R_CON 的高字节
temp[0] ^= rcon
round_keys.append([round_keys[i - 4][j] ^ temp[j] for j in range(4)])
# 将所有的轮密钥连接成一个大的数组
expanded_key = []
for word in round_keys:
expanded_key.extend(word)
return [expanded_key[i:i + 16] for i in range(0, len(expanded_key), 16)]
# 轮密钥加
def add_round_key(state, round_key):
for i in range(4):
for j in range(4):
state[j][i] ^= round_key[i * 4 + j]
return state
# AES加密算法
def aes_encrypt(plaintext, key):
state = [[0] * 4 for _ in range(4)]
for i in range(16):
state[i % 4][i // 4] = plaintext[i]
round_keys = key_expansion(key)
state = add_round_key(state, round_keys[0])
for round in range(1, 10):
state = sub_bytes(state)
state = shift_rows(state)
state = mix_columns(state)
state = add_round_key(state, round_keys[round])
state = sub_bytes(state)
state = shift_rows(state)
state = add_round_key(state, round_keys[10])
ciphertext = [0] * 16
for i in range(16):
ciphertext[i] = state[i % 4][i // 4]
return bytes(ciphertext)
def main():
plaintext = b'\x32\x43\xf6\xa8\x88\x5a\x30\x8d\x31\x31\x98\xa2\xe0\x37\x07\x34'
key = b'\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\xcf\x1f\x19\x2b\x21\x37'
ciphertext = aes_encrypt(plaintext, key)
print("加密结果是:",ciphertext.hex())
if __name__ == '__main__':
main()
(3)实验结果
3.SM4加密算法
(1)设计思路
SM4分组密码算法国标:
(2)实现代码
S_BOX = [0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87,
0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E,
0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1,
0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F,
0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51,
0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8,
0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0,
0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48
]
FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]
CK = [
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
]
# 循环移位
def rot(X, n):
return (X << n) & 0xffffffff | (X >> (32 - n))
# 轮函数F
def F(X, rk):
A = X[1] ^ X[2] ^ X[3] ^ rk
B = S_BOX[(A >> 24) & 0xff] << 24 | S_BOX[(A >> 16) & 0xff] << 16 | S_BOX[(A >> 8) & 0xff] << 8 | S_BOX[A & 0xff]
C = B ^ rot(B, 2) ^ rot(B, 10) ^ rot(B, 18) ^ rot(B, 24)
return X[0] ^ C
# 密钥扩展算法里的轮函数
def F_(X, i):
A = X[i + 1] ^ X[i + 2] ^ X[i + 3] ^ CK[i]
B = S_BOX[(A >> 24) & 0xff] << 24 | S_BOX[(A >> 16) & 0xff] << 16 | S_BOX[(A >> 8) & 0xff] << 8 | S_BOX[A & 0xff]
C = B ^ rot(B, 13) ^ rot(B, 23)
return X[i] ^ C
# 密钥扩展算法
def generateKey(MK):
K = []
for i in range(4):
K.append(((MK >> (32 * (3 - i))) & 0xffffffff) ^ FK[i])
for i in range(32):
K.append(F_(K, i))
return K[4:]
# SM4加密算法
def SM4Encryption(plaintext, key):
rk = generateKey(key)
X = []
for i in range(4):
X.append((plaintext >> (32 * (3 - i))) & 0xffffffff)
for i in range(32):
X.append(F(X[i:i + 4], rk[i]))
Y = ''.join([hex(X[i])[2:].zfill(8) for i in range(-1, -5, -1)])
return hex(int(Y,base = 16))
# SM4解密算法
def SM4Decryption(ciphertext, key):
rk = generateKey(key)
X = []
for i in range(4):
X.append((ciphertext >> (32 * (3 - i))) & 0xffffffff)
for i in range(32):
X.append(F(X[i:i + 4], rk[31 - i]))# 加解密算法一致,只是密钥使用顺序相反
Y = ''.join([hex(X[i])[2:].zfill(8) for i in range(-1, -5, -1)])
return hex(int(Y,base = 16))
if __name__ == "__main__":
while True:
x = int(input("请选择模式:1.SM4加密 2.SM4解密 3.退出"))
if x == 1:
print("-------------SM4加密过程-------------")
plaintext = int(input("请输入明文:"), base=16)
key = int(input("请输入密钥:"), base=16)
ciphertext = SM4Encryption(plaintext, key)
print("加密结果为:", ciphertext)
print("-----------------------------------")
elif x == 2:
print("-------------SM4解密密过程-------------")
ciphertext = int(input("请输入密文:"), base=16)
key = int(input("请输入密钥:"), base=16)
plaintext = SM4Decryption(ciphertext, key)
print("解密结果为:", plaintext)
print("-----------------------------------")
else:
break
(3)实验结果
选择明文:0x0123456789ABCDEFFEDCBA9876543210 及密钥:0x0123456789ABCDEFFEDCBA9876543210 输出的密文如下,与上面的示例相同
三、序列密码(ZUC算法)
序列密码也称流密码(Stream Cipher),明、密文序列的1位(1 bit或1 byte)进行加解密,加解密使用相同的密钥。
1.算法原理
参考:
在线预览|GB/T 33133.1-2016 (gb688.cn)(国标里ZUC算法的描述,来自国家标准全文公开 (samr.gov.cn),其中ZUC算法的标准号是33133)
【以ZUC算法为例介绍学习方法】| 祖冲之序列密码算法| 序列密码| 加密算法| 密码学| |国密| 学习方法_哔哩哔哩_bilibili
(【可厉害土豆】的视频讲解,结构讲得超清晰,下面很多结构图也是截取她视频里的)
算法结构分成上中下三层:
(1)LFSR
初始化模式:输入一个31比特字,对16个31比特寄存器单元变量进行更新(更新前的初始状态是由密钥k和初始向量iv决定的)
工作模式:无输入,直接对寄存器单元变量进行更新
(2)比特重组BR
输入LFSR寄存器单元变量,输出4个32比特字(即从寄存器单元变量从抽取128比特组成4个32比特的字)
(3)非线性函数F
输入(来自上层BR的)3个32比特字,输出一个32比特字(非线性函数F相当于一个把96比特压缩成32比特的压缩函数),非线性函数F包含一个S盒和2个32比特的存储单元
2.算法执行
ZUC算法的执行分成两个阶段
(1)初始化阶段
密钥和初始向量初始化,算法运行但不产生任何输出。目的是得到16个寄存器单元变量
(2)工作阶段
每次运行产生1个字的输出并且更新寄存器单元变量在下一轮的BR中使用,共运行L次(L由明文长度决定,即明文长度除32再向上取整)
3.算法实现
import math
S0 = [
0x3E, 0x72, 0x5B, 0x47, 0xCA, 0xE0, 0x00, 0x33, 0x04, 0xD1, 0x54, 0x98, 0x09, 0xB9, 0x6D, 0xCB,
0x7B, 0x1B, 0xF9, 0x32, 0xAF, 0x9D, 0x6A, 0xA5, 0xB8, 0x2D, 0xFC, 0x1D, 0x08, 0x53, 0x03, 0x90,
0x4D, 0x4E, 0x84, 0x99, 0xE4, 0xCE, 0xD9, 0x91, 0xDD, 0xB6, 0x85, 0x48, 0x8B, 0x29, 0x6E, 0xAC,
0xCD, 0xC1, 0xF8, 0x1E, 0x73, 0x43, 0x69, 0xC6, 0xB5, 0xBD, 0xFD, 0x39, 0x63, 0x20, 0xD4, 0x38,
0x76, 0x7D, 0xB2, 0xA7, 0xCF, 0xED, 0x57, 0xC5, 0xF3, 0x2C, 0xBB, 0x14, 0x21, 0x06, 0x55, 0x9B,
0xE3, 0xEF, 0x5E, 0x31, 0x4F, 0x7F, 0x5A, 0xA4, 0x0D, 0x82, 0x51, 0x49, 0x5F, 0xBA, 0x58, 0x1C,
0x4A, 0x16, 0xD5, 0x17, 0xA8, 0x92, 0x24, 0x1F, 0x8C, 0xFF, 0xD8, 0xAE, 0x2E, 0x01, 0xD3, 0xAD,
0x3B, 0x4B, 0xDA, 0x46, 0xEB, 0xC9, 0xDE, 0x9A, 0x8F, 0x87, 0xD7, 0x3A, 0x80, 0x6F, 0x2F, 0xC8,
0xB1, 0xB4, 0x37, 0xF7, 0x0A, 0x22, 0x13, 0x28, 0x7C, 0xCC, 0x3C, 0x89, 0xC7, 0xC3, 0x96, 0x56,
0x07, 0xBF, 0x7E, 0xF0, 0x0B, 0x2B, 0x97, 0x52, 0x35, 0x41, 0x79, 0x61, 0xA6, 0x4C, 0x10, 0xFE,
0xBC, 0x26, 0x95, 0x88, 0x8A, 0xB0, 0xA3, 0xFB, 0xC0, 0x18, 0x94, 0xF2, 0xE1, 0xE5, 0xE9, 0x5D,
0xD0, 0xDC, 0x11, 0x66, 0x64, 0x5C, 0xEC, 0x59, 0x42, 0x75, 0x12, 0xF5, 0x74, 0x9C, 0xAA, 0x23,
0x0E, 0x86, 0xAB, 0xBE, 0x2A, 0x02, 0xE7, 0x67, 0xE6, 0x44, 0xA2, 0x6C, 0xC2, 0x93, 0x9F, 0xF1,
0xF6, 0xFA, 0x36, 0xD2, 0x50, 0x68, 0x9E, 0x62, 0x71, 0x15, 0x3D, 0xD6, 0x40, 0xC4, 0xE2, 0x0F,
0x8E, 0x83, 0x77, 0x6B, 0x25, 0x05, 0x3F, 0x0C, 0x30, 0xEA, 0x70, 0xB7, 0xA1, 0xE8, 0xA9, 0x65,
0x8D, 0x27, 0x1A, 0xDB, 0x81, 0xB3, 0xA0, 0xF4, 0x45, 0x7A, 0x19, 0xDF, 0xEE, 0x78, 0x34, 0x60
]
S1 = [
0x55, 0xC2, 0x63, 0x71, 0x3B, 0xC8, 0x47, 0x86, 0x9F, 0x3C, 0xDA, 0x5B, 0x29, 0xAA, 0xFD, 0x77,
0x8C, 0xC5, 0x94, 0x0C, 0xA6, 0x1A, 0x13, 0x00, 0xE3, 0xA8, 0x16, 0x72, 0x40, 0xF9, 0xF8, 0x42,
0x44, 0x26, 0x68, 0x96, 0x81, 0xD9, 0x45, 0x3E, 0x10, 0x76, 0xC6, 0xA7, 0x8B, 0x39, 0x43, 0xE1,
0x3A, 0xB5, 0x56, 0x2A, 0xC0, 0x6D, 0xB3, 0x05, 0x22, 0x66, 0xBF, 0xDC, 0x0B, 0xFA, 0x62, 0x48,
0xDD, 0x20, 0x11, 0x06, 0x36, 0xC9, 0xC1, 0xCF, 0xF6, 0x27, 0x52, 0xBB, 0x69, 0xF5, 0xD4, 0x87,
0x7F, 0x84, 0x4C, 0xD2, 0x9C, 0x57, 0xA4, 0xBC, 0x4F, 0x9A, 0xDF, 0xFE, 0xD6, 0x8D, 0x7A, 0xEB,
0x2B, 0x53, 0xD8, 0x5C, 0xA1, 0x14, 0x17, 0xFB, 0x23, 0xD5, 0x7D, 0x30, 0x67, 0x73, 0x08, 0x09,
0xEE, 0xB7, 0x70, 0x3F, 0x61, 0xB2, 0x19, 0x8E, 0x4E, 0xE5, 0x4B, 0x93, 0x8F, 0x5D, 0xDB, 0xA9,
0xAD, 0xF1, 0xAE, 0x2E, 0xCB, 0x0D, 0xFC, 0xF4, 0x2D, 0x46, 0x6E, 0x1D, 0x97, 0xE8, 0xD1, 0xE9,
0x4D, 0x37, 0xA5, 0x75, 0x5E, 0x83, 0x9E, 0xAB, 0x82, 0x9D, 0xB9, 0x1C, 0xE0, 0xCD, 0x49, 0x89,
0x01, 0xB6, 0xBD, 0x58, 0x24, 0xA2, 0x5F, 0x38, 0x78, 0x99, 0x15, 0x90, 0x50, 0xB8, 0x95, 0xE4,
0xD0, 0x91, 0xC7, 0xCE, 0xED, 0x0F, 0xB4, 0x6F, 0xA0, 0xCC, 0xF0, 0x02, 0x4A, 0x79, 0xC3, 0xDE,
0xA3, 0xEF, 0xEA, 0x51, 0xE6, 0x6B, 0x18, 0xEC, 0x1B, 0x2C, 0x80, 0xF7, 0x74, 0xE7, 0xFF, 0x21,
0x5A, 0x6A, 0x54, 0x1E, 0x41, 0x31, 0x92, 0x35, 0xC4, 0x33, 0x07, 0x0A, 0xBA, 0x7E, 0x0E, 0x34,
0x88, 0xB1, 0x98, 0x7C, 0xF3, 0x3D, 0x60, 0x6C, 0x7B, 0xCA, 0xD3, 0x1F, 0x32, 0x65, 0x04, 0x28,
0x64, 0xBE, 0x85, 0x9B, 0x2F, 0x59, 0x8A, 0xD7, 0xB0, 0x25, 0xAC, 0xAF, 0x12, 0x03, 0xE2, 0xF2
]
D = [
0x44D7, 0x26BC, 0x626B, 0x135E, 0x5789, 0x35E2, 0x7135, 0x09AF,
0x4D78, 0x2F13, 0x6BC4, 0x1AF1, 0x5E26, 0x3C4D, 0x789A, 0x47AC
]
# 初始
S = [0]*16
X = [0, 0, 0, 0]
R1 = 0
R2 = 0
Key_result = []
m = 2 ** 32
# 线性反馈移位寄存器LFSR的初始化模式
def LFSRWithInitMode(u):
v = (2**15*S[15] + 2**17*S[13] + 2**21*S[10] + 2**20*S[4] + (1+2**8)*S[0]) % (2**31-1)
S.append((v + u) % (2**31-1))
if S[16] == 0:
S[16] = 2**31 - 1
S.pop(0)
# 线性反馈移位寄存器LFSR的工作模式
def LFSRWithWorkMode():
S.append((2**15*S[15] + 2**17*S[13] + 2**21*S[10] + 2**20*S[4] + (1+2**8)*S[0]) % (2**31-1))
if S[16] == 0:
S[16] = 2**31 - 1
S.pop(0)
def H(W):# 取出高16位
return (W >> 15) & 0xffff
def L(W):# 取出低16位
return W & 0xffff
def rot(W, n):# 循环左移n位
return W << n & 0xffffffff | W >> (32-n)
# 比特重组BR
def BitReconstruction():
X[0] = H(S[15]) << 16 | L(S[14])
X[1] = L(S[11]) << 16 | H(S[9])
X[2] = L(S[7]) << 16 | H(S[5])
X[3] = L(S[2]) << 16 | H(S[0])
def L1(X):
return X ^ rot(X, 2) ^ rot(X, 10) ^ rot(X, 18) ^ rot(X, 24)
def L2(X):
return X ^ rot(X, 8) ^ rot(X, 14) ^ rot(X, 22) ^ rot(X, 30)
def S_Box(X):
x0 = (X>>(32-4)&0xf)<<4 | X>>(32-8)&0xf
x1 = (X>>(32-12)&0xf)<<4 | X>>(32-16)&0xf
x2 = (X>>(32-20)&0xf)<<4 | X>>(32-24)&0xf
x3 = (X>>(32-28)&0xf)<<4 | X&0xf
Y = S0[x0]<<24 | S1[x1]<<16 | S0[x2]<<8 | S1[x3]
return Y
# 非线性函数F
def F(X0, X1, X2):
global R1, R2, W, S
W = ((X0 ^ R1) + R2) % m
W1 = (R1 + X1) % m
W2 = R2 ^ X2
R1 = S_Box(L1(((W1<<16) | (W2>>16)) & 0xffffffff))
R2 = S_Box(L2(((W2<<16) | (W1>>16)) & 0xffffffff))
# 初始化步骤
def initialize(key, iv):
global S
S = [(key[i] << 23) | (D[i] << 8) | iv[i] for i in range(16)]
for i in range(32):
BitReconstruction()
F(X[0], X[1], X[2])
LFSRWithInitMode(W >> 1)
# 工作步骤
def work():
BitReconstruction()
F(X[0], X[1], X[2])
LFSRWithWorkMode()
for i in range(keylen):
BitReconstruction()
F(X[0], X[1], X[2])
Key_result.append(W ^ X[3])
LFSRWithWorkMode()
def print_key(): # 打印密钥
for i in range(keylen):
print('KEY' + str(i) + ':\033[1;36m' + hex(Key_result[i]).replace('0x', '') + '\033[0m ', end='')
if __name__ == '__main__':
# 初始密钥和iv
key = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
iv = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
initialize(key, iv)
while True:
x = int(input("请选择模式:1.获得密钥字 2.ZUC加密 3.退出"))
if x == 1:
print("-------------获得密钥字-------------")
keylen = int(input("请输入需要的密钥字个数:"))
work()
print_key()
print("\n----------------------------------")
elif x == 2:
print("-------------ZUC加密--------------")
plaintext = input("请输入需要加密的明文:")
keylen = math.ceil(len(plaintext.replace("0x", ""))/8)
work()
key_result = int(''.join(str(i) for i in Key_result))
ciphertext = hex(int(plaintext, base=16) ^ key_result)
print("加密的结果是:",ciphertext)
print("----------------------------------")
else:
break
加密结果:
注:在函数内部对全局变量进行修改需要在函数内使用global关键字声明该变量
四、公钥密码(RSA)
分组密码和序列密码都属于对称密码,即加解密使用相同的密钥。公钥密码属于非对称密码,加解密使用不同的密钥,其中公开的密钥称为公钥,私有的密钥称为私钥。
(1)RSA算法描述
RSA算法是一种广泛使用的公钥加密算法,基于大整数分解困难,安全性较高。
1. 密钥生成
选择两个大质数:这两个质数应尽可能大且随机选取。
计算模数:。这个值将用于公钥和私钥中。
计算欧拉函数。
选择公钥指数: 选择一个整数 ,使得 且与互质。常见的选择是 65537。
计算私钥指数:计算使得,即是关于的模逆元。公钥:
私钥:2. 加密
明文:将待加密的信息转化为数字,要求。
密文:使用公钥对明文进行加密,计算。3. 解密
解密明文:使用私钥对密文进行解密,计算。
(2)设计思路
1.确保选择的两个大数是质数【Miller-Rabin素性测试】
Miller-Rabin 素性测试是一种基于概率的算法,用于判断一个数是否为素数。如果所有测试都没有指出n是合数,那么n是素数的概率非常高。
原理:【朝夕的ACM笔记】数论-Miller Rabin素数判定 - 知乎 (zhihu.com)
2.根据公钥计算私钥【扩展欧几里得算法】
扩展欧几里得算法(Extended Euclidean Algorithm)是一种用于计算两个整数 a 和 b(假设 b≠0)的最大公因数(GCD, Greatest Common Divisor)的算法,并且还能找到整数 x 和 y,使得 ax+by=gcd(a,b)。算法核心:递归调用。
公钥e所对应的私钥d满足,转化为求中的x。
3.加速加密【快速指数算法】
RSA加密算法的核心在于大数运算,特别是模幂运算,这些运算在加密和解密过程中占据了大量的计算资源。快速指数算法(也称为快速幂算法)是一种高效的计算幂的方法,特别适用于大数的幂运算。
4.加速解密【中国剩余定理】
中国剩余定理可以将一个大的模幂运算分解为几个较小的模幂运算,从而显著减少计算量,提高解密速度。
(3)算法实现
import random
def MillerRabinTest(n, t):
if n < 3 or (n & 1 == 0):
return n == 2
k, q = 0, n - 1
# 计算 q 和 k
while not q & 1:
q = q // 2
k = k + 1
tested = []
for _ in range(t):
composite = True
a = random.randint(2, n - 2) # 随机生成2~n-2之间的整数
while a in tested:
a = random.randint(2, n - 2)
tested.append(a)
r = pow(a, q, n) # 调用快速模幂算法,计算 a^q mod n
if r == 1 or r == n - 1:
composite = False
else:
# 计算 a^(q·2^j) mod n
for j in range(1, k):
r = (r * r) % n
if r == n - 1:
composite = False
break
if composite:
return False
return True
def extendedGcd(a, b):
"""
扩展欧几里得算法
:return:最大公约数gcd,满足ax+by=gcd的一组解x,y
"""
if a == 0:
return b, 0, 1
gcd, x1, y1 = extendedGcd(b % a, a)
x = y1 - (b // a) * x1
y = x1
return gcd, x, y
def generateKeys(p, q):
n = p * q
phi = (p - 1) * (q - 1)
e = random.randrange(2, phi)
gcd, x, y = extendedGcd(e, phi)
while gcd != 1:
e = random.randrange(2, phi)
gcd, x, y = extendedGcd(e, phi)
d = x % phi
return (e, n), (d, n)
def fastExponentiation(base, exp, mod):
result = 1
while exp > 0:
if exp % 2 == 1:
result = (result * base) % mod
base = (base * base) % mod
exp //= 2
return result
def rsaEncrypt(plaintext, public_key):
e, n = public_key
return fastExponentiation(plaintext, e, n)
def rsaDecrypt(ciphertext, private_key):
d, n = private_key
return fastExponentiation(ciphertext, d, n)
def chineseRemainderTheoremDecrypt(ciphertext, p, q, d):
dp = d % (p - 1)
dq = d % (q - 1)
q_inv = pow(q, -1, p)# q^(-1) mod p
m1 = fastExponentiation(ciphertext, dp, p)# c^dp mod p
m2 = fastExponentiation(ciphertext, dq, q)# c^dq mod q
h = (q_inv * (m1 - m2)) % p
return (m2 + h * q) % (p * q)
if __name__ == '__main__':
while True:
x = int(input("请输入模式:1.Miller-Rabin素性测试 2.扩展欧几里得算法计算公私钥 3.快速指数算法实现RSA的加密 4.使用中国剩余定理解密"))
if x == 1:
print("----------Miller-Rabin素性测试----------")
n = int(input("请输入待检测的数:"))
t = int(input("请输入检测次数:"))
print(f'{n}是否为素数:{MillerRabinTest(n, t)}')
print("---------------------------------------")
elif x == 2:
print("----------扩展欧几里得算法得到公私钥对----------")
p = 101
q = 113
public_key, private_key = generateKeys(p, q)
print("公钥对:", public_key)
print("私钥对:", private_key)
print("---------------------------------------")
elif x == 3:
print("----------快速指数算法实现RSA的加密-----------")
plaintext = int(input("请输入明文:"))
p = 101
q = 113
public_key = 41, 11413
ciphertext = rsaEncrypt(plaintext, public_key)
print("加密结果为:", ciphertext)
print("---------------------------------------")
elif x == 4:
print("---------使用中国剩余定理进行解密-----------")
private_key = 9561, 11413
ciphertext = 2596
p,q=101,113
decrypted_text_direct = rsaDecrypt(ciphertext, private_key)
decrypted_text_crt = chineseRemainderTheoremDecrypt(ciphertext, p, q, private_key[0])
print("直接解密:", decrypted_text_direct)
print("使用中国剩余定理解密:", decrypted_text_crt)
print("---------------------------------------")
else:
break