Vigenere密码的唯密文攻击暴力破解(python实现)

一、找到最可能的密钥长度

在不知道密钥的情况下破解,首先要猜测密钥长度,这是非常重要的一个环节。

书上写的是kasiski测试法重合指数法

这里使用到的不是课本上的,而是代码实现中用到的一种方法:移位法(暂且称之为移位法)

原理:

我们都知道vigenere密码是多表循环加密实现的,在这里假设密钥长度是d,那么第1,1+d,....,1+k*d都是同一个密钥字母加密的,那么如果第i和第i+d*n个字母相同的话,他们的密文一定是一样的。

所以我们可以利用这个事实,来进行概率推测,我们假设密钥长度是3~15,我们从密钥长度为3开始测试:

从第0个字符开始,对每个字符(i)判断,如果该字符与相隔密钥长度(我们猜想的)的字符(i+3)一样,那么我们可以认为,这两个字符在一定程度上明文也是匹配的,可以把这个匹配个数记下来,进行累加。(这里不用计算重合指数,代码实现起来比较快,正确率也是很高的

重复测试直到密钥长度15,每次都会计算出一个匹配最大值,我们取最后的匹配量最大的密钥长度,很有可能是我们的密钥长度。

下面给出图示,来清晰下这个过程:

下面给出代码实现:

def getKeyLen(cipherText): # 获取密钥长度
    keylength = 1
    maxCount = 0
    for step in range(3,10): # 循环密钥长度
        count = 0
        for i in range(len(cipherText)-step): #range(step,len(cipherText)-step):
            if cipherText[i] == cipherText[i+step]:
                 count += 1
        if count>maxCount:
            maxCount = count
            keylength = step
    return keylength

二、找到最可能的匹配密钥

寻找密钥是使用的是交互重合指数法

在得到密钥长度之后,就要实现对密钥字母的匹配了。

我们知道密钥长度(k)之后,那么将密文分成n个组(每组包含k个字符),那么每个分组中的第i个字符,都是使用同一个密钥字母进行加密的,那么现在所有分组第i个字符就是一个单表加密的实现,故,他的每组中字母频率,只是明文字母频率的一个转换(shift)。

字母频率也会是这样的一个转换,但是我们实际上是不可能得到明文的频率分布的,(不然还破解个?2?)

这里只是清晰下思路。

现在我们可以来计算明文和所有分组用同一密钥字母加密的密文的交互重合指数(两个都随机取一个字母相同的概率)

\sum_{h=0}^{25}P_{h-ki}P_{h-kj}=\sum_{h=0}^{25}P_{h}P_{h+ki-kj}=\sum_{h=0}^{25}P_{h}P_{h-l}    注:l为密文与明文的相对位移

这个式子表示一段文本中第i个字符和另一段文本中第j个字符同为26个字母中第h个字母的概率,由于26个字母的概率我们是知道的,也就是P_{h},式子就变成了:     [已知的字母概率]  *  [密文字母概率](也就是相对位移l后的字母概率)

很显然,我们的密文字母概率是可以统计出来的。

如果我们此时我们存在相对位移的话,也就是说没有找对正确的密钥字母时,这时候交互重合指数的数值是在[0.031,0.045]之间浮动的;当不存在相对位移时,也就是说相对位移为0时,这时候这个数值会比较接近0.065.

但是这个0.065是在大量大量大量英文中找到的一个普遍水平,我们随便找的文本,可以说是不可能达到0.065的水平。

所以我们只需要取最接近0.065的那个相对位移值(0~25),其对应的字母就是我们的密钥第一个字母。

其他密钥字母如是。

下面给出代码实现:

def getKey(text,length): # 获取密钥
    key = [] # 定义空白列表用来存密钥
    #已知的字母出现概率
    alphaRate = [0.082, 0.015,0.028,0.043,0.127,
              0.022,0.02,0.061,0.07,0.002,0.008,
              0.04,0.024,0.067,0.075,0.019,0.001,
              0.06,0.063,0.091,0.028,0.01,0.023,0.001,0.02,0.001]
#更准确的概率,使用一个就ok
    #alphaRate =[0.08167,0.01492,0.02782,0.04253,0.12705,0.02228,0.02015,0.06094,0.06996,0.00153,0.00772,0.04025,0.02406,0.06749,0.07507,0.01929,0.0009,0.05987,0.06327,0.09056,0.02758,0.00978,0.02360,0.0015,0.01974,0.00074]
    matrix =textToList(text,length)   #将明文按照密钥长度分组成二位列表
    for i in range(length):
        w = [row[i] for row in matrix] #获取每组密文中第i位的密文    这都是用同一个字母加密的
        li = countList(w)    #计算里面的字母频率
        powLi = [] #交互重合指数
        for j in range(26):
            Sum = 0.00000
            for k in range(26):
                Sum += alphaRate[k]*li[k]  #   Ic的值Sum[k] += alphaRate[k]*li[k+j] 的话  就不用后面的切片操作
            powLi.append(Sum)
            li = li[1:]+li[:1]#循环移位 向左移一位   
        Abs = 1
        ch = ''
        for j in range(len(powLi)):
             if abs(powLi[j] -0.065546)<Abs: # 找出最接近英文字母重合指数的项
                 Abs = abs(powLi[j] -0.065546) # 保存最接近的距离,作为下次比较的基准
                 ch = chr(j+97)
        key.append(ch)
    return key

#用到的两个子函数
def countList(lis): # 统计字母频率
    li = []
    alphabet = [chr(i) for i in range(97,123)]  //生成小写字母表
    for c in alphabet:   统计26个字母的出现概率
        count = 0
        for ch in lis:
            if ch == c:
                count+=1
        li.append(count/len(lis))
    return li        #返回字母表每个字母的出现概率

def textToList(text,length): # 根据密钥长度将密文分组
    textMatrix = []   #二维表  里面添加分组数个分组  每个分组中都是按照密钥加密的
    row = []     #行  = 分组   每个row都是按照密钥逐一加密的   每个row长度为密钥的元素个数
    index = 0    #从0开始
    for ch in text:
        row.append(ch)
        index += 1
        if index % length ==0:   #一组完成后,加入二维表
            textMatrix.append(row)
            row = []
    return textMatrix

三、密文还原为明文 

因为我们的密钥是重复使用的,所以在还原时,需要不断根据密文字母的位置对密钥长度取余,判断使用密钥的哪个字母。

plainText = ''
index = 0
for ch in cipherText:
    c = chr((ord(ch)-ord(key[index%length])+26)%26+97)  #字母的ascii值与对该位置加密的密钥字母,相对位移
    plainText += c
    index+=1

至此,我们就已经完成了vigenere的 唯密文破解。

 

 

 

前面的代码已经很完善了,如果还有不明白的,这里附上完整源码<传送门>

附:图源水印

加油加油加油啊~

不要辜负大好年华!

维吉尼亚密码是一种多表密码,它使用一个密钥和一些表格来加密明文。具体来说,它采用多个字母表,每个字母表都是通过对原始字母表进行移位得到的。密钥是一个字符串,每个字符表示对应字母表的移位数。明文中的每个字符都在对应的字母表中被替换为相同位置的字符。 破解维吉尼亚密码需要确定密钥。一种常见的方法是使用Kasiski检测来确定密钥长度。Kasiski检测是通过查找重复的密文片段来确定密钥长度的。然后可以使用频率分析来破解每个字母表的移位数。 下面是一个Python示例代码,用于破解维吉尼亚密码: ```python import string def vigenere_decrypt(ciphertext, key): # 初始化字母表 alphabet = string.ascii_uppercase # 将密文和密钥转换为大写字母 ciphertext = ciphertext.upper() key = key.upper() # 计算密钥长度 key_length = len(key) # 初始化解密后的明文 plaintext = "" # 对密文中的每个字符进行解密 for i in range(len(ciphertext)): # 获取密文字符和对应的密钥字符 c = ciphertext[i] k = key[i % key_length] # 获取对应的字母表 shift = alphabet.index(k) table = alphabet[shift:] + alphabet[:shift] # 解密密文字符 if c in alphabet: p = table.index(c) plaintext += alphabet[p] else: plaintext += c return plaintext def kasiski_test(ciphertext): # 查找重复的密文片段 repeats = {} for i in range(len(ciphertext) - 2): substr = ciphertext[i:i+3] if substr in repeats: repeats[substr].append(i) else: repeats[substr] = [i] # 计算重复片段之间的距离 distances = {} for substr, locations in repeats.items(): if len(locations) > 1: distances[substr] = [] for i in range(len(locations) - 1): distance = locations[i+1] - locations[i] distances[substr].append(distance) # 计算可能的密钥长度 factors = [] for substr, dists in distances.items(): for dist in dists: for factor in range(2, dist+1): if dist % factor == 0: factors.append(factor) key_length = max(set(factors), key=factors.count) return key_length # 测试代码 ciphertext = "WMTIOMHXTCGMSYWINRWGMSYWMXKIRLRSWYWGIRHHIWGIRGL" key_length = kasiski_test(ciphertext) print("Possible key length: {}".format(key_length)) key = "" for i in range(key_length): # 对每个字母表进行频率分析 freqs = [0] * 26 for j in range(i, len(ciphertext), key_length): c = ciphertext[j] if c in string.ascii_uppercase: freqs[ord(c) - ord('A')] += 1 shift = freqs.index(max(freqs)) - ord('E') key += chr(shift + ord('A')) print("Possible key: {}".format(key)) plaintext = vigenere_decrypt(ciphertext, key) print("Plaintext: {}".format(plaintext)) ``` 这个示例代码实现了维吉尼亚密码的破解。它首先使用Kasiski检测来确定密钥长度,然后对每个字母表进行频率分析来确定移位数。最后,它使用确定的密钥来解密密文。注意,这个代码只能处理大写字母的维吉尼亚密码。如果需要处理小写字母或其他字符,需要进行一些修改。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kinnisoy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值