第3关:仿射密码:
任务描述
仿射密码为单表加密的一种,字母系统中所有字母都利用一个简单数学方程加密,对应至数值或转回字母。
本关任务:用 c++ 实现仿射密码,然后对输入的密文字符串进行仿射解密后打印输出。
相关知识
为了完成本关任务,你需要掌握: 1.同余方程; 2.欧拉函数; 3.逆元; 4.仿射密码的密码体制; 5.费马小定理; 在仿射密码中,加密函数定义成为: e(x)=(ax+b)(mod 26) 我们知道,密码体制需要满足单射性,在当前密码体制下,一个明文只能对应一个密文,所以为了保证上面加密的式子的单射性,我们需要加一些限制条件,这些限制条件由一些定理来支撑,下面给出这些定理。
同余方程
定义 设a∈Zm
,对任意的b∈Zm
,同余方程ax≡b(mod m)
有唯一解x∈Zm
的充分必要条件是gcd(a,m)=1
(这里的gcd
的意思是最大公约数的意思)。
下面我们会看到只有a
和m
互质,a
才会有关于m
的逆元,才会有唯一解。 假设m=26
,因为26=2×13
,故所有的与26互质的数为a=1,3,5,7,9,11,15,17,19,21,23,25
。 b
的取值可以是Z26
中的任何数。因此仿射密码的密钥空间为12×26=312
。 下面是代码实现:
constexpr int m=26; // 模数 mod 取值为26
cout<<"a=";
for(int i=1; i<m; i++)
if(m%i!=0) //判断m是否可以整除i
cout<<i<<' '; //输出与m互质的数
输出结果:
a=1 3 5 7 9 11 15 17 19 21 23 25
欧拉函数
定义 设a≥1,m≥2
且均为整数。如果gcd(a,m)=1
,则称a与 m 互质。Zm
中所有的与 m互质的个数使用ϕ(m)
来表示(这个函数称为欧拉函数)。 定义 假定 m=∏i=1npiei 这里的pi
均为素数且互不相同,且ei>0,1≤i≤n
。则 ϕ(m)=∏i=1n(piei−piei−1)
根据上面关于欧拉函数ϕ(m)
的公式,可推出来在仿射密码中密钥空间的大小为m∗ϕ(m)
。例如,如果m=60
,ϕ(60)=2×2×2=16
,那么此仿射密码的密钥空间大小为60×16=960
。 但是想要解同余方程y≡ax+b(mod 26)
,还需要有效的方法来实现。下面给出逆元的概念。
逆元
定义:设a∈Zm
,若存在a′∈Zm
,使得aa′≡a′a≡1(mod m)
,则a′
称为a
在Zm
上的乘法逆,将其记为a−1(mod m)
。在m
是固定的情形下,一般也可以将其简记为a−1
。 当gcd(a,m)=1
时,a
在Zm
上存在乘法逆元,并且如果逆元存在,那么该逆元在(mod m)
下是唯一的。另外还可以发现如果b≡a−1(mod m)
,那么a≡b−1(mod m)
。 下面给出在Z26
的情况下的逆元 1−1=1 3−1=9 5−1=21 7−1=15 11−1=19 17−1=23 25−1=25 说到这里,就可以给出解密函数了, y≡ax+b(mod 26) 等价于 ax≡y−b(mod 26) 因为gc(a,26)=1
,所以 a 在Z26
上存在逆元,那么在两边同时乘上a−1
,有: (a−1)ax≡a−1(y−b)(mod 26) 根据之前的乘法结合律有 x≡a−1(y−b)(mod 26) 那么相应的解密变换为: d(y)≡a−1(y−b)(mod 26)
仿射密码体制
令P=C=Z26
且K=(a,b)∈Z26×Z26:gcd(a,26)=1
,对任意的K=(a,b)∈K,x,y∈Z26
,定义加密变换为: ek(x)=(ax+b)(mod 26)
相应的解密变换为: dk(y)=a−1(y−b)(mod 26) 下面给出一个例子: 设密钥K=(7,3)
,由前所得,有7−1(mod 26)=15
。加密函数为ek=7x+3(mod 26)
,因为7×15≡1(mod 26)
,相应的解密变换为: dk(y)=15(y−3)(mod 26)=15y−45(mod 26) 上面的运算均是在Z26
上完成的。下面来验证对于任意的x∈Z26
,都有dk(ek(x))=x
,下面是验证过程: dk(ek(x))=dk(7x+3)
=15(7x+3)−45
=x+45−45
=x
验证完毕。
费马小定理
经过前面的学习,相信你对仿射的密码的体系结构已经有了深刻的理解,但是你有没有发现对于逆元的求解,依旧是一团迷雾,下面给出一种逆元的求法。 在gcd(a,m)==1
的条件下,根据费马小定理有 aϕ(m)≡1(mod m) 那么两边同时除以a
可得aϕ(m)−1≡a−1(mod m)
,那么a−1≡aϕ(m)−1(mod m)
,这里就把a−1(mod m)
给求出来了。但是这里还有一个问题就是aϕ(m)−1
怎么快速求出来,这里给出快速幂的板子,供大家学习。
求x^y(modm)
constexpr int mod=26; // 模数取值为26
size_t quick(size_t x,size_t y)
{
size_t ans=x; //设置初始值
while(y)
{
if(y&1) //判断y是不是奇数
ans=ans*x%mod; //计算答案贡献
x=x*x%mod; //每次都乘方
y>>=1; //每次除以2
}
return ans; //返回结果
}
编程要求
本关的编程任务是,补全右侧编辑器中 Begin-End 区间的代码,实现给定字符串的解密功能,具体要求如下:
- 对于给定的密钥对以及密文串,要求计算并打印出解密后的明文串。
测试说明
平台会对你编写的代码进行测试:
测试输入:
2 3
qwercvzzxx
预期输出:
asqqkcooii
测试输入:
5 10
qwercvzzxx
预期输出:
gmuhslppnn
代码:
#include<bits/stdc++.h>
using namespace std;
char A[103];
constexpr int mod=26;
//在下面Begin和End之间补全代码,对输入的字符串进行仿射密码解密
/*********** Begin ***********/
size_t quick(size_t x,size_t y)
{
size_t ans=x;
while(y)
{
if(y&1)
ans=ans*x%mod;
x=x*x%mod;
y>>=1;
}
return ans;
}
int main()
{
int a,b;
cin>>a>>b;
cin>>A;
int x=quick(a,15);
for(int i=0; i<strlen(A); i++)
printf("%c",'a'+(x*((A[i]-'a'-b+mod)%mod)%mod));
return 0;
}
/*********** End ***********/