密码学⼊门(⼀):⽤Python实现对称加密算法
0.前⾔
最开始只是想整理⼀下密码学课程的作业,后⾯越写越多,就索性写成⼀篇⼊门的介绍。我会把⾃⼰对对称加密的理解和⼀些作业的代码串起来,⼒图清晰明⽩地展⽰出来,⽂中所有代码都放在我的上,如果有错误之处还请轻拍。
⽂章地址:
代码地址:
1.对称密码基础
加密是为了防⽌要传达的内容被别⼈知道。例如,你如果想在课堂上传⼩纸条給后位⼩红说:i love coding,但⼜怕在递纸条的过程中被⽼师看到,知道了你的⼼思,于是将每个字母变字母表中的后⼀个字母(如a变成b,i变成j,z变成a),得到密⽂:j mpwf dpejoh,这样即⽼师⼈拿到这纸条,也不知道你说的是什么。
这就是⼀个加密的过程,把原本的内容称为明⽂,⼀般⽤p表⽰;加密后得到的内容称为密⽂,⼀般⽤c表⽰;⽽加密的这个过程可以看做是⼀个加密函数E,即
$$c = E( p )$$
E是指Encrypt,函数输⼊是明⽂,输出是加密之后的密⽂。上⾯的例⼦中i love coding便是明⽂,j mpwf dpejoh便是密⽂,⽽把字母在字母表中向后移动⼀位的操作就是加密函数。
在⼩红得到⼩纸条后,可以根据你加密的⽅法,将每个字母变成字母表中的前⼀个字母,就可以从你的密⽂⼩纸条得到你要说的内容i love coding,⼼领神会,顺便还会怀疑⼀下你的脑袋……⽆论怎样,这个解密的过程就也可以看做是⼀个解密函数D,即
$$p = D( c )$$
D是指Decrypt,函数输⼊是密⽂,输出是解密之后的明⽂。
在这个过程这种,⼩红能够成功解密⼩纸条的前提是,你得和她在课前约定好你加密的时候移动的是1位,2位还是⼏位,不然他就会和⽼师⼀样⼀脸懵逼,不知道你在说啥。你们提前约定好的这个“⼏位”,就是加密和解密的密钥k,你会根据这个秘钥来进⾏加密,⼩红会根据这个秘钥来进⾏解密。
所以你的传纸条的动作抽象成这个过程:
明⽂p---->加密函数E---->密⽂c---->传输---->密⽂c----->解密函数D---->明⽂p
或者⽤公式来表达是:
$$c = D_k( E_k( c ) )$$
⽤⼤⽩话说就是:明⽂⽤同⼀个密钥先加密再解密得到的还是同⼀个明⽂(等于没说...)
从这⾥我们可以总结出加密体质的五个要素:{明⽂p,密⽂c,密钥k,加密函数E,解密函数D},对称解密的的意思就是说,加密和解密的密钥是⼀样的,上⾯的过程是不是正好很对称呢?
为了⽅便使⽤,不⽤每次⾃⼰⼿动掰⼿指数字符,你还写了Python程序:
# 移位密码
def _move_leter(letter, n):
"""
把字母变为字母表后n位的字母,z后⾯接a
:param letter: ⼩写字母
:param n: 要移动的字母
:return: 移动的结果
"""
return chr((ord(letter) - ord('a') + n) % 26 + ord('a'))
def Encrypt(k, p):
"""
移位密码加密函数E
:param k: 秘钥k,每个字母在字母表中移动k位
:param p: 明⽂p
:return: 密⽂c
"""
letter_list = list(p.lower())
c = ''.join([_move_leter(x, k) for x in letter_list])
return c
def Decrypt(k, c):
"""
移位密码解密函数D
:param k: 秘钥k,每个字母在字母表中移动k位
:param c: 密⽂c
:return: 明⽂p
"""
letter_list = list(c.lower())
p = ''.join([_move_leter(x, -k) for x in letter_list])
return p
if __name__ == '__main__':
p = 'ilovecoding'
print('明⽂:' + p)
print('密⽂:' + Encrypt(1, p))
print('解密:' + Decrypt(1, Encrypt(1, p)))
assert Decrypt(1, Encrypt(1, p)) == p
运⾏这段代码,就可以看到输出了:
明⽂:ilovecoding
密⽂:jmpwfdpejoh
解密:ilovecoding
终于,现在你能和你的⼩红秘密地传达纸条内容了,迎来全班⼈羡慕的⽬光,从此⾛上⼈⽣巅峰,本⽂到此结束。
...Hey,醒醒...
2.密码分析
⾯对你俩⽇益频繁的纸条往来,⽼师终于坐不住了,他想知道你俩写的到底是啥,于是在某次逮到你递纸条之后,决定下功夫破解你所使⽤的密码,也就是密码分析。
根据他的了解,以你的⽔平,最可能⽤的就是移位密码,但具体每次移动了⼏位,⽆法直接观察得出。不过他⼜⼀想,你移动的位数顶多是25位,因为,移动26位的效果等于没移动,移27位的效果不就跟移动1位的效果是⼀样的嘛!这就是说,你的密码只能是0-25中的某⼀个数字,⽽不可能是其他的,就这么⼆⼗⼏个秘钥,⼀个⼀个试就能知道你写的是啥!
⽼师果然聪明绝顶,关键是也还会Python,就索性写了⼀个程序,每次尝试⽤不同的秘钥来进⾏解密,并观察解密出来的内容是否有意义:
def analyze(c):
"""
移位密码分析
:param c: 密⽂c
:return:
"""
for k in range(26):
# ⽤不同的秘钥k尝试解密
print('秘钥%d:' % k + Decrypt(k, c))
if __name__ == '__main__':
c = 'jmpwfdpejoh'
analyze(c)
运⾏程序输出结果为:
秘钥0:jmpwfdpejoh
秘钥1:ilovecoding
秘钥2:hknudbnchmf
秘钥3:gjmtcambgle
...........
逐⾏观察输出结果,到第⼆⾏的时候就能看到原来的明⽂,也就知道了你要对⼩红说的内容以及你们所约定的秘钥。⾯对你冒着巨⼤风险在课堂上所传递的纸条内容,⽼师⼼⾥可能也是复杂的...
python新手代码大全pdfAnyway,你的⼩秘密已经被⽼师知道了,此时⽐较灰⼼,⼀直在想,究竟是什么原因致使纸条计划失败?其实原因很明显,各位也看出来了,⼩明所使⽤的加密体制中,可⽤的秘钥太少,或者说秘钥空间太⼩,别⼈直接⼀⼀列举进⾏穷搜就能破解,这就提⽰我们:⼀个好的加密体制,它的秘钥空间应该是⾜够⼤的。
其实,你此次所⽤的移位密码是古典的加密体制之⼀,据说凯撒打仗时就⽤这种⽅法与将军们联系,所以位移密码也叫凯撒密码(Caesar cipher)。类似的还有代换密码,仿设射密码等等,都是将单个
字母替换成别的字母,来达到加密的⽬的。报纸上的猜谜游戏就经常⽤这些⽅法,⼀般根据字母频率进⾏破解,有兴趣可以进⾏进⼀步的了解。
所以到底要⽤什么样的加密⽅法,才能保证我和⼩红的秘密不被⼈偷窥呢?
2.1 密码分析情形
俗话说,知⼰知彼,百战不殆,了解破解者的密码分析⽅法,或许能够帮助我们想出更安全的密码体制。可以在不同的情形下考察密码体制的安全性,⼀般我们都假设破解者知道我们所使⽤的密码体制,也就是说,不把密码体制的安全性寄托在密码体制的保密性上,⽽是放在秘钥上。
破解者的⽬的就是出所使⽤的秘钥,常见的有以下⼏种攻击情形:
唯密⽂攻击: 破解者拥有密⽂c。这就是⽼师破解纸条的情形。
已知明⽂攻击: 破解者拥有⼀些明⽂p及其对应的密⽂c。考虑到实际情形,这个假设是⽐较合理的,例如破解者获得⼀封邮件加密后的密⽂,可以猜测⼀个词很可能是'hi'或者'dear',这样就可能到⼀个明⽂--密⽂对。
选择明⽂攻击: 破解者能够指定⼀个明⽂p,获得其对应的密⽂c,较强的假设。
选择密⽂攻击: 破解者指定⼀个密⽂c,获得其对应的明⽂,较强的假设。
天啊,你不禁惊呼,在这么强的假设下,真的会有密码体制能够存活吗?
答案是有,⽽且这种密码体制已经被⼴泛应⽤,甚⾄可以说⽆处不在,它就是AES(Advanced Encryption Standard)。
3.SPN⽹络
难道不是要介绍AES吗,怎么会变成SPN⽹络,这是啥?可以吃吗?
AES、DES等很多现代对称加密⽅法的核⼼就是SPN⽹络,它是代换-置换⽹络(Substitution-Permutation Network)的缩写,是现代对称加密⽅法设计的蓝本。可以说,了解SPN⽹络,就基本了解了AES。
很巧的是,这个⽹络正好是容易理解的。SPN⽹络的思想很简单:既然加密⼀次不够安全,那我就加密多次,把第⼀次加密产⽣的密⽂再进⾏加密,解密的时候我连续进⾏两次解密就可以了,这样是不是就安全了⼀些呢?
对于密码体制\(S_1\),其加密与解密函数为\(E_1\)与\(D_1\),对于密码体制\(S_2\),其加密与解密函数为\(E_2\)与\(D_2\),我构造出⼀个新的密码体制\(S_3\),其加密函数为:$$c = E_2( E_1( p ) )$$
解密函数为$$p=D_1( D_2( c ) )$$
记为$$S_3 = S_1 * S_2$$这样破解\(S_3\)就可能会困难些。这个想法是不是很直接呢?这个思想在1949年才被提出,⽽提出者,可能理科⽣都多少听过他的名字——⾹农(Shannon)。
注意,不是任何的加密体制都可以这样“乘”起来变得更强,例如对于你的移位密码,嵌套起来还是移位密码(为什么?),没有任何改善,即\(S_1*S_1=S_1\),这样的密码体制被称为幂等的。
如果密码体制不是幂等的,那么多次迭代就可能能够提⾼安全性,SPN就是使⽤这种思想,包含多轮的迭代,每轮的操作都是相同的。下⾯,介绍SPN单轮的操作:
3.1 SPN单轮操作
SPN⽹络是对⼀定长度的⽐特进⾏操作的,在本⽂中的SPN⽹络中,⼀次加密的长度为16个⽐特,即2字节,也就是说每次加密16⽐特的明⽂,输出16⽐特的密⽂。
⼀个SPN⽹络包含多轮迭代,每轮迭代的操作内容都⼀样是:异或运算-->分组代换-->单⽐特置换
3.1.1 第⼀步——异或运算
异或运算是⽐较常见的⼆元⽐特运算,⽤⊕表⽰,其规则就是“相同得0,不同得1”:
0 ⊕ 0 = 0
1 ⊕ 1 = 0
1 ⊕ 0 = 1
0 ⊕ 1 = 1
对于⽐特串,直接按每⼀位对应进⾏计算即可以了:
0011 ⊕ 1010 = 1001
异或的有⽐较有意思的性质:⼀个⽐特串亦或另⼀个⽐特串两遍,还是等于他⾃⼰,即a ⊕ b ⊕ b = a,这是因为a ⊕ b ⊕ b = a ⊕ ( b ⊕ b ) =a ⊕ 0 = a,可以带⼊⼀些例⼦试试看。
SPN⽹络中,每⼀轮的第⼀步就是把输⼊的⽐特串w和秘钥k进⾏亦或:u = w ⊕ k,如:
0001110000100011 = 0010011010110111 ⊕ 0011101010010100
这⼀步的⽬的是根据秘钥对明⽂进⾏混淆。如果你只知道输出u⽽不知道秘钥k,那么你就猜不出实际输⼊的w是什么,它是什么都可能,⽽且是等概率的。例如对于1 = a ⊕ b,不告诉你b是0还是1,你
就不知道a是什么。⽽对于和操作,如果知道1 = a and b,那么就能确定a与b 都是1。
这就是第⼀步,是不是很简单呢?
3.1.2 第⼆步——分组代换
这⼀步也很简单,将第⼀步输出的16⽐特的串分为4组,每组4⽐特,即0001110000100011写成0001 1100 0010 0011。然后对于每组再根据事先所定的表进⾏代换,代换表长这样:
代换前0123456789A B C D E F
代换后E4D12F B83A6C5907
就拿第⼀列来说,表的意思是:如果你是0(0000),那么我要把你换成E(1110),就是⼀个简单的映射操作。
原⽐特串长这样:0001 1100 0010 0011 <==> 1 C 2 3,再对每个字母查表得到:4 5 D 1 <==> 0100 0101 1101 0001,这样就得到代换后的⽐特串0100 0101 1101 0001,完成了第⼆步。
这个表⼀般称为S盒(Substitution),这个过程可以⽤v = S(u)表⽰,u是第⼀步异或的结果,也是第⼆步分组代换的输⼊,v是第⼆步的输出。需要注意,S盒的输⼊和输出⼀般是⾮线性的关系。
3.1.3 第三步——单⽐特置换
单⽐特置换是将16⽐特中的每⼀⽐特,根据P盒(Permutation)移动挪位,这样说很不直观,直接上例⼦,P盒长这样:
置换前的位置12345678910111213141516
置换后的位置15613261014371115481216
拿第⼆列来说,表的意思是:第⼆个⽐特要挪到第五个⽐特的位置,举个好看的例⼦:
0100 0000 0000 0000 置换后为==> 0000 1000 0000 0000
这个例⼦⾥⾯第⼆个⽐特的1挪到了第五的位置,⽽其他位置的⽐特都是0,挪位置之后还是0。
对于第⼆部输出的结果1100 1101 1100 0100,置换后的⽐特串为0010 1110 0000 0111,这样就完成了第三步。
这⼀步可以⽤W = S(v)表⽰,v是第⼆部的输出,也是第三步的输⼊,W是第三步的输出,P盒置换是⼀种线性的变换。
这三步放在⼀起结果如下,建议读者⾃⼰计算⼀遍:
w = 0010 0110 1011 0111
k = 0011 1010 1001 0100
第⼀步,异或运算:
u = w ⊕ k = 0001 1100 0010 0011
第⼆步,分组代换:
v = S(u) = 0100 0101 1101 0001
第三步,单⽐特置换:
W = P(v) = 0010 1110 0000 0111
可以写成:W = P( S(w ⊕ k) ),这样就完成了⼀轮迭代,⾥⾯⽤到的参数有k,S盒与P盒,如图(图⽚来⾃):

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。