多表代换加解密基本实现
hill逆矩阵.ppt 下载
第1章 密码学概述.ppt 下载
markdown中公式编辑教程 - 简书 (jianshu.com)
原理 多表代换密码首先将明文M分为由n个字母构成的分组M1 ,M2 ,M3 ,… ,Mj ,对每个分组Mi 的加密为: $$ C_i \equiv\ AM_i + B\pmod n , i = 1,2,3,\ldots,j $$ 其中(A,B)是密钥,A是n*n的可逆矩阵,满足 $$ gcd(\begin{vmatrix}A\end{vmatrix},N)=1 (\begin{vmatrix}A\end{vmatrix}是行列式) $$ 其中: $$ \begin{array}{} B=(B_1,B_2,B_3,\ldots,B_n)^T \ C=(C_1,C_2,C_3,\ldots,C_n)^T \ M=(M_1,M_2,M_3,\ldots,M_n)^T \ \end{array} $$ 对密文分组Ci 的解密为: $$ M_i \equiv\ A^{-1} (C_i-B) \pmod n , i = 1,2,3,\ldots,j $$
准备 需要对矩阵进行转置,求行列式,求逆矩阵,求伴随矩阵,取模等,用到了numpy
库的函数。安装:
矩阵必是二维的,所以使用的np.matrix
对象存储数据,我在前面提到了array对象的各种用法 ,matrix和array对象大同小异,在个别方法的调用上有所不同。
下面简单介绍一下代码用到的函数,主要是matrix提供了矩阵乘运算,与array的按位乘有所不同。
np.linalg.det(matrix)
求伴随矩阵
np.transpose(matrix)
求转置矩阵,相当于matrix.T
matrix.I
求逆矩阵
方法变换返回的矩阵元素都是浮点数
np.round(array)
求四舍五入,此外: np.floor()
、np.ceil()
、np.trunc()
:向上,向下,截断
方法对矩阵元素取整
关键点 解密算法中,需要在mod26的情况下于对矩阵求逆,我们根据公式:
$$ \begin{bmatrix}M_n\end{bmatrix}^{-1} \equiv\ \cfrac 1 {\begin{vmatrix}Mn\end{vmatrix}} {\begin{vmatrix}M_n\end{vmatrix}}^* \pmod n $$ 单纯的调用matix.I
是不行的,数学方法里求逆矩阵,中间的1/mn 是直接对行列式求倒,算法里这一步应该是求取行列式的逆元,也就是说 $$ \cfrac 1 {\begin{vmatrix}M_n\end{vmatrix}}\pmod n $$ 在数学和密码学里的意义是不同的,numpy没有直接求伴随矩阵的函数,所以我们需要反过来利用matrix.I
和np.linalg.det(matrix)
按位乘求出伴随矩阵,再对其乘法逆元后,这才是密码学要的 1/mn (mod26)
1 2 3 4 5 6 7 def inv (a:np.matrix )->np.matrix: a_inv = a.I a_det = np.linalg.det(a) a_adju = np.round ((a_det * a_inv)).astype(np.int16) a_det_inv = ex_gcd(int (round (np.linalg.det(a))), mod) get_inv = np.round ((a_det_inv * a_adju)).astype(np.int16) % mod return get_inv
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 import numpy as npimport mathmod=26 def ex_gcd (a, mod ): """扩展欧几里德算法,内置""" if math.gcd(a, mod) != 1 : return None u1, u2, u3 = 1 , 0 , a v1, v2, v3 = 0 , 1 , mod while v3 != 0 : q = u3 // v3 v1, v2, v3, u1, u2, u3 = (u1 - q * v1), (u2 - q * v2), (u3 - q * v3), v1, v2, v3 return u1 % mod choose = input ("加密E/解密D\n > " ) a = np.mat(eval (input ("输入矩阵A:(例二重矩阵格式为:[[a,b] , [c,d]]):\n > " )),dtype=int ) b = np.mat(eval (input ("输入矩阵B:(例二重矩阵格式为:[[e,f]]):\n > " )),dtype=int ) if math.gcd(int (np.linalg.det(a)),mod)!=1 : print("a行列式的值" ,int (np.linalg.det(a)),mod,"不互素,gcd=" ,math.gcd(int (np.linalg.det(a)),mod)) exit("a输入的行列式值不与26互素" ) if np.linalg.det(a)==0 : exit("a没有逆矩阵" ) b = np.transpose(b) get_m = input ("输入加密或解密字符,小写字母:" ).strip().lower() if not get_m.islower(): print(get_m) exit("你的明文不是小数字母,不能还原" ) m_ord=[] for i in get_m: m_ord.append(ord (i)-97 ) m_list=np.array(m_ord) m_list.resize((len (get_m)//a.shape[0 ],a.shape[0 ],1 )) def encode (): c_list=[] for i in range (len (m_list)): m=m_list[i] c=((np.dot(a,m))+b)%26 c_list.append(c) text=[] c_list=np.array(c_list) for i in c_list.flatten(): text.append(chr (i+97 )) return '' .join(text) def inv (a:np.matrix )->np.matrix: a_inv = a.I a_det = np.linalg.det(a) a_adju = np.round ((a_det * a_inv)).astype(np.int16) a_det_inv = ex_gcd(int (round (np.linalg.det(a))), mod) get_inv = np.round ((a_det_inv * a_adju)).astype(np.int16)%mod return get_inv def decode (): c_list = [] for i in range (len (m_list)): m = m_list[i] c = (np.dot(inv(a), (m-b))) % 26 c_list.append(c) text = [] c_list = np.array(c_list) for i in c_list.flatten(): text.append(chr (i+97 )) return '' .join(text) if __name__ == "__main__" : if choose == "E" or choose == "e" : print(encode()) elif choose == "D" or choose == "d" : print(decode())
总结 这次作业还有很多需要完善的地方,为了方便,只能对小写字母进行加密,而且当输入的m不是a阶数的倍数时应该会有异常。其实原来我只是想找一份代码水一水就提交的,参考了这个博主的代码: python实现多表代换加密与解密 - 朱鱼鱼的小本本朱鱼鱼的小本本 (zuicy.party) ,然后发现些许逻辑错误和待完善地方,看得出来作者本来想对明文进行分组的,从这里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 e = input ("输入原文:" ) elist = list (e) for i in range (len (elist)): elist[i] = ord (elist[i]) - 97 x = np.zeros((3 , int (len (elist) / 3 )), dtype=int ) m = np.mat(x) i = 0 h = 0 for i in range (int (len (elist) / 3 )): j = 0 for j in range (3 ): m[j, int (h / 3 )] = elist[h] h = h + 1
博主对e进行了分组,但是后面直接一个c = np.dot(a,m) + b
完事了,导致程序一次运行只能加密一个单元的m(m=3或者m=9),而对矩阵的手动循环赋值也显得很难受。但是博主的欧几里得法求逆元和求逆矩阵的函数还是很有参考价值。自己写这个作业时,就直接贴进代码里了。
多表代换对参数a的判断很严格,需要a的行列式和26互素,课本出的例题似乎并未满足这个条件。