古代密码
Scytale密码
- 使用一条纸袋作为载体,环绕在一根固定半径的圆柱上。
加密
- 在绕好的纸带上写上明文。
- 解开缠绕后,就是加密好的、无序的密文。
- 圆柱的半径就是密钥。
解密
- 找到相同大小的圆柱,将纸带缠绕在援助上,即得到密文。
棋盘密码
- 将
26
个字母放进一个5x5
的表格中(ij
放在一起)。
加密
- 将明文对照表格找到横纵坐标,横坐标+纵坐标即为单个字符的密文。
- 其中这个表格就是密钥。
解密
凯撒密码
- 是一种移位密码,将字母对应到相应的数字(
A
对应为0
)后,通过循环移位,改变对应的字母。
加密
- 先将每个字母对应到
0~25
的数字。
- 再进行移
c
位,这里的c
就是密钥。
c
=
(
m
+
k
)
%
26
c=(m+k)\%26
c=(m+k)%26
凯撒密码的c
为3
。
解密
m
=
(
c
−
k
)
%
26
m=(c-k)\%26
m=(c−k)%26
古典密码
置换密码
- 字或者字母本身不变,位置发生改变,形成密文。
- 又称为易位密码。
- 下面举出报文倒置法的例子。
加密
解密
单表代替密码——加法密码
加密
//m is message, k is key,l is the length of message
void f(int*m, int k,int l){
for(int i=0;i<l;i++){
printf("%c",(m[i]+k)%26+97);
}
}
解密
//m is message, k is key,l is the length of message
void f(int*c, int k,int l){
for(int i=0;i<l;i++){
printf("%c",(c[i]-k+26)%26+97);
}
}
单表代替密码——乘法密码
加密
c
=
k
×
m
(
m
o
d
26
)
c=k\times m(mod26)
c=k×m(mod26)
//m is message, k is key,l is the length of message
void f(int *m,int k,int l){
for(int i = 0;i < l;i++){
printf("%c",m[i]*k%26+97);
}
}
解密
m
=
k
−
1
×
c
(
m
o
d
26
)
m=k^{-1}\times c(mod26)
m=k−1×c(mod26)
int prim[12] = {1,3,5,7,9,11,15,17,19,21,23,25};
//m is message, k is key,l is the length of message
void f(int *c,int k,int l){
int d;
for(int i = 0;i < 12;i++){
if(k*prim[i]%26 == 1){
d = prim[i];
}
}
for(int i = 0;i < l;i++){
printf("%c",c[i]*d%26+97);
}
}
这里由于集合中素数只有12个,依次枚举即可得到密钥的逆。
int prim[12] = {1,3,5,7,9,11,15,17,19,21,23,25};
//decode with exgcd
void exgcd(int a,int b,int*yp){
int x;
if(!b){
x = 1;
*yp = 0;
return;
}
int x1=0,x2=1,y1=1,y2=0,q,r;
while(b>0){
q = a/b;
r = a-q*b;
x = x2-q*x1;
*yp = y2-q*y1;
a = b;
b = r;
x2 = x1;
x1 = x;
y2 = y1;
y1 = *yp;
}
*yp = y2;
}
void f(int *c,int k,int l){
int d;
exgcd(26,k,&d);
for(int i = 0;i < l;i++){
printf("%c",c[i]*d%26+97);
}
}
单表代替密码——仿射密码
加密
c
=
a
×
m
+
b
(
m
o
d
26
)
c=a\times m+b(mod26)
c=a×m+b(mod26)
解密
m
=
a
−
1
(
c
−
b
)
(
m
o
d
26
)
m=a^{-1}(c-b)(mod26)
m=a−1(c−b)(mod26)
单表代替密码——密钥短语代替
- 这种密码选用一个英文短语或者单词串作为密钥,称为密钥字或密钥短语。
加密
- 构造乱序字母表作为密钥,将明文中的字符进行映射。
- 例如下表(一三行为明文表,二四行为对应密文)。
a |
b |
c |
d |
e |
f |
g |
h |
i |
j |
k |
l |
m |
H |
A |
P |
Y |
N |
E |
W |
R |
B |
C |
D |
F |
G |
n |
o |
p |
q |
r |
s |
t |
u |
v |
w |
x |
y |
z |
I |
J |
K |
L |
M |
O |
Q |
S |
T |
U |
V |
X |
Z |
解密
通过大量密文统计每个字符出现概率,在单一密文里面找到对应概率的字符进行替换尝试,可能导致在没有字母表时密文被破解。
多表代换密码
- 每一位字符对应不同的加密方法,构造二维表作为密钥。
加密
- 构造可逆模
26
互素矩阵,利用它和明文串相乘得到密文串。
C
i
=
A
M
i
+
B
(
m
o
d
N
)
C_i=AM_i+B(mod N)
Ci=AMi+B(modN)
- 由于在构造二维表时,
我太懒了难以找到满足条件的表,这里代码只给出3
阶密钥(老师给的)。
int key[NUM][NUM] = {{11,2,19},{5,23,25},{20,7,17}};
void f(int *m,int l){
char c;
for(int i = 0;i < l;i++){
c = 0;
for(int j = 0;j < l;j++){
c += key[i][j]*m[j]%26;
}
printf("%c",c%26+97);
}
}
解密
- 利用矩阵可逆的条件,构造逆矩阵作为乘法矩阵。
- 密文串和逆矩阵做乘法之后便得到明文串。
M
i
=
A
−
1
(
C
i
−
B
)
(
m
o
d
N
)
M_i=A^{-1}(C_i-B)(modN)
Mi=A−1(Ci−B)(modN)
int dkey[NUM][NUM] = {{10,23,7},{15,9,22},{5,9,21}};
void f(int *c,int l){
char m;
for(int i = 0;i < NUM;i++){
m = 0;
for(int j = 0;j < NUM;j++){
m += dkey[i][j]*c[j]%26;
}
printf("%c",m%26+97);
}
}
int dkey[NUM][NUM] = {{10,23,7},{15,9,22},{5,9,21}};
void f(int *c,int l){
char m;
for(int i = 0;i < NUM;i++){
m = 0;
for(int j = 0;j < NUM;j++){
m += dkey[i][j]*c[j]%26;
}
printf("%c",m%26+97);
}
}
实际上统一密文以l
为单位长度