仿射密码
原理
仿射密码是一种表单代换密码,字母表的每个字母相应的值使用一个简单的数学函数对应一个数值,再把对应数值转换成字母。
加密函数
E(x)=(ax+b)(modm)
注意:
x表示明文按照某种编码得到的数字;
a和 m互质;
m是编码系统中字母的个数(通常都是26)。
看到以上加密形式,可以进一步推测是否为仿射加密
解密函数
D(x)=a−1(x−b)(modm)
注意:
a−1 是 a 在 Zm群的乘法逆元
这里先附上乘法逆元的相关脚本:
#欧几里德算法求最大公约数 def get_gcd(a, b): k = a // b remainder = a % b while remainder != 0: a = b b = remainder k = a // b remainder = a % b return b #改进欧几里得算法求线性方程的x与y def get_(a, b): if b == 0: return 1, 0 else: k = a // b remainder = a % b x1, y1 = get_(b, remainder) x, y = y1, x1 - k * y1 return x, y a = input('a:') b = input('b:') a, b = int(a), int(b) #将初始b的绝对值进行保存 if b < 0: m = abs(b) else: m = b flag = get_gcd(a, b) #判断最大公约数是否为1,若不是则没有逆元 if flag == 1: x, y = get_(a, b) x0 = x % m #对于Python '%'就是求模运算,因此不需要'+m' print("所求的逆元:",x0) #x0就是所求的逆元 else: print("Do not have!")
举例说明
我们以 E(x)=(5x+8) mod 26函数为例子进行介绍,加密字符串为 AFFINECIPHER,这里我们直接采用字母表26个字母作为编码系统
密文就是IHHWVCSWFRCP。
解密过程:
先求解5关于模26的乘法逆元,为21
解密函数就是D(x) = 21(x - 8) mod 26
解密如下图:
解密脚本:
#仿射密码解密
#改进欧几里得算法求线性方程的x与y
def get(a, b):
if b == 0:
return 1, 0
else:
k = a //b
remainder = a % b
x1, y1 = get(b, remainder)
x, y =y1, x1 - k * y1
return x, y
s = input("请输入解密字符:").upper()
a = int(input("请输入a:"))
b = int(input("请输入b:"))
#求a关于26的乘法逆元
x, y = get(a, 26)
a1 = x % 26
l= len(s)
for i in range(l):
cipher = a1 * (ord(s[i])- 65 - b) % 26
res=chr(cipher + 65)
print(res, end='')
得到:
看到的一些解密思路
我们可以看到的是,仿射密码对于任意两个不同的字母,其最后得到的密文必然不一样,所以其也具有最通用的特点。当密文长度足够长时,我们可以使用频率分析的方法来解决。
其次,我们可以考虑如何攻击该密码。可以看出当a=1时,仿射加密是凯撒加密。而一般来说,我们利用仿射密码时,其字符集都用的是字母表,一般只有26个字母,而不大于26的与26互素的个数一共有
ϕ(26)=ϕ(2)×ϕ(13)=12
算上b的偏移可能,一共有可能的密钥空间大小也就是
12×26=312
一般来说,对于该种密码,我们至少得是在已知部分明文的情况下才可以攻击。下面进行简单的分析。
这种密码由两种参数来控制,如果我们知道其中任意一个参数,那我们便可以很容易地快速枚举另外一个参数得到答案。
但是,假设我们已经知道采用的字母集,这里假设为26个字母,我们还有另外一种解密方式,我们只需要知道两个加密后的字母 y1,y2 。即可进行解密。
那么我们还可以知道
y1=(ax1+b)(mod26)y2=(ax2+b)(mod26)
两式相减,可得
y1−y2=a(x1−x2)(mod26)
这里 y1,y2已知
如果我们知道密文对应的两个不一样的字符 x1 与 x2 ,那么我们就可以很容易得到 a ,进而就可以得到 b 了。