简述

维吉尼亚密码是在代换密码(即单表代换)基础上,衍生出来的多表代换密码。

与单表代换相同,维吉尼亚密码也采用明文字母与密钥字母(即26字母表)间建立一一对应关系。

但是不同的是,单表代换密码中一旦密钥字母确定,相同的明文就只能产生唯一的密文;

而维吉尼亚密码则是在单表的基础上,加入了密钥字,使用密钥字对明文进行分组加密,因此即使密钥(打乱的字母表)确定了,密钥字不同,也会产生不同的密文,即非固定式对应。

加解密

在之后的表述中:
<密钥>表示26字母顺序表
<密钥字>表示输入的一串辅助加密的消息序列

现假定输入的密钥字为k1到km,总长为m个字母;明文为x1到xm;表字母以初始26字母顺序表为例,进行加解密说明:

  • 加密

维吉尼亚密码的加密定义如下:
在这里插入图片描述
即:

  1. 将明文分成若干个分组,每个分组为m个字母长度(即密钥字的长度);
  2. 再找出分组中的字母和密钥字的字母在代换表中的对应数字;
  3. 将其对应位两两相加得出的数字,再代入代换表中,查出对应的密文字母;
  4. 将所有明文分组都如此操作,即得到密文。

加密示例:

  1. 字母表为原始A-Z
    因此00对应A,……,25对应Z

  2. 输入密钥字为Computer
    因此 m = 8,key =(02,14,12,15,20,19,04,17);

  3. 输入明文为Block cipher design principles。

将明文分为8个字母一组,再进行找序号求和取余的运算,得出序号对应的密文字母:

比如 B(序号为01),对应的密钥字为C(序号为02),相加为03,再模26得03(即D),因此明文的B对应的密文字母为 D 。

所有加密步骤图如下:
在这里插入图片描述
可得出密文为:Dzarevmgjsdylmxpddxhvmgnse

  • 解密

由于维吉尼亚密码也是映射型密码,因此只需要进行加密的逆过程即可解密。即:
在这里插入图片描述

代码实现

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
import re

letterList = [] # 字母列表
# 初始化字母列表(此处以顺序表为例)
for i in range(ord("a"), ord("z") + 1):
letterList.append(chr(i))

# 加密
def encrypt():
ciphertext = ""
# 接收键入函数返回的列表
massageAndKeyList = inputMassage()
plaintextList = massageAndKeyList[0]
keyList = massageAndKeyList[1]
for i in range(len(plaintextList)):
# 明文和密钥在表中的对应数值
plaIndex = letterList.index(plaintextList[i])
keyIndex = letterList.index(keyList[i % len(keyList)])
# 做加法后区取余运算
ciphertext += letterList[(plaIndex + keyIndex) % 26]
return ciphertext

# 解密(加密的逆过程)
def decrypt():
plaintext = ""
# 接收键入函数返回的列表
massageAndKeyList = inputMassage()
ciphertextList = massageAndKeyList[0]
keyList = massageAndKeyList[1]
for i in range(len(ciphertextList)):
# 密文和密钥在表中的对应数值
cipIndex = letterList.index(ciphertextList[i])
keyIndex = letterList.index(keyList[i % len(keyList)])
# 取余运算
plaintext += letterList[(cipIndex - keyIndex) % 26]
return plaintext

# 键入
def inputMassage():
massageList = [] # 消息序列
keyList = [] # 密钥序列
# 输入消息并创建消息序列
massage = (re.sub("[^a-zA-Z]", "", input("Input text:"))).lower()
for i in massage:
massageList.append(i)
# 输入密钥并创建密钥序列
key = (re.sub("[^a-zA-Z]", "", input("Input key:"))).lower()
for i in key:
keyList.append(i)
# 以列表形式返回输入的消息序列和密钥序列
return [massageList, keyList]

if __name__ == "__main__":
while True:
print("1、Encrypt 2、Decrypt 3、Show Form")
choice = int(input("Please choose(Input number):"))
if choice == 1:
ciphertext = encrypt()
print("ciphertext:", ciphertext)
elif choice == 2:
plaintext = decrypt()
print("plaintext:", plaintext)
elif choice == 3:
print("The Form Of This Operation:\n", letterList)
else:
print("Input error!")

输入示例

  • 对以上示例进行验证

输入明文:Block cipher design principles

输入密钥字:Computer

加密结果:dzarevmgjsdsylmxpddxhvmgnse
在这里插入图片描述
再对其解密
在这里插入图片描述
解密结果与输入的明文相同。

  • 验证密钥字的作用

依然对以上明文进行加密:Block cipher design principles

输入不同的密钥字:XiaoheiBryant

加密结果:ytoqrgqqycrqxpqgbwvqotgpyxp
在这里插入图片描述
可见,相同的明文,相同的字母表,但是密钥字不同,加密结果依然不同。

同理,明文相同,密钥字相同,当字母表变换时,加密结果依然不同。

总结

作为多表代换加密,维吉尼亚密码可以很好的抗击频率分析攻击法,再该加密算法下,字母的频率特性会消失。

一般而言,在大量分析后,英文字母的出现频率是与一个高低排序的,因此在单表代换密码(如代换密码)中,可以根据密文中的字母出现频率,来判断其对应的明文字母。
在这里插入图片描述
那么维吉尼亚密码这么好用,为什么还是放弃了呢?

由于维吉尼亚密码依然属于代换密码,而且是凯撒密码的26中移位:
在这里插入图片描述
因此,还是可以根据频率分析的方法进行破解的。

破解

破解维吉尼亚密码的关键在于计算出密钥字的长度

可以采用重复子串偏移量的最大公约数,来求出密钥字的长度

由于其加密是根据密钥字的长度进行分组加密,因此当破解出密钥字的长度后,加密算法就是一个多重凯撒密码,再根据频率分析就能得出对应明文。

  • 震惊

该加密方法是在16世纪(约500年前)提出的!