python实现维吉尼亚秘钥破解

关于维吉尼亚密码,百度百科中有着较为详细的描述:
维吉尼亚密码——百度百科
维吉尼亚密码的原理与凯撒密码类似,其实是凯撒的一种强化和变形,通过使加密相同明文的秘钥不同,来掩盖字符的频率。
举个栗子:
秘钥为"hello",明文为"today when people ......",通过秘钥加密后结果如下:

加密

可以看到相同的明文e,经过不同的字符加密之后变成了不同的密文,掩盖了明文字符e的字符频率。
但也不是找不到字符频率,我们可以发现,将用"h"字符加密的明文取出之后,就变成了普通的凯撒加密,这是可以通过字符频率分析来破解的。
这就为维吉尼亚秘钥的破解带来了思路。

思路与过程

1.破解秘钥长度N。
2.将密文分成N组,逐个破解秘钥。

用到的数学公式:重合指数

重合指数

其中fi为每个字符在英文当中的频率。fi^2则表示连续取出两个相连的字符,它们相同的概率。英文中对26种情况求和的统计结果约为0.065。
Ni/N为密文中某个字符占密文的比例,假设秘钥长度为key_len,如果key_len组密文中的重合指数IC1也都与0.065接近,那么就可以推测key_len是秘钥长度了。

当秘钥长度key_len知道以后,我们将密文分成key_len个组,计算每个分组的IC2。
举个栗子:如果第一个分组都是用b字符进行加密,那么a字符的频率会转移到b字符上,c字符的频率会转移到d字符上......我们也做这种相应的转移,让b字符在密文的频率(N1/L)和a字符在英文的频率f0相乘,当然这只是其中一种猜测。我们将这26种字符可能都列出来,最接近IC的一定是用b字符加密的那一组。

读者如果想知道详细的资料,可以去网上查找关于重合指数的详细内容,这里也给出一个博客。
密码学原理-篇1:重合指数

步骤

1.破解秘钥长度

首先要有需要破解的密文,密文长度要足够,否则无法破解,笔者给大家一个栗子。

cipher ='Asolm dlpy dlsaws aewv oisfe Flh Ncczw Zcuhrtkoamzy, hoij dvhop evlmc sshhd lbk hzyh avfdh altd cyklywgeetcu. Tpzdsi cpojx qzf px zcwnmylhlh qcct emzia jzff filcg hkz, lh alle hpqp, l upvw dvvapo cmj spf syifff my evl tfmzpg xprpe, dss aswo dlsaws alle vlv qlhoic hoz e xpaiic zt alp Csk Gczgz Scroumklhpsy. Xcyi lyr tscp dlsaws rrph vlv, essf xszinle evlc hpfl gspoaio mm alp zfneytnhxtzb, alp xcuij evlc ozbhxpo khw yzh bwpo wu xsp fpkse khc. Ess prntrlre soz e rcshx ypuhxtgs prqwilrnp cu xsp Flh Ncczw Zcuhrtkoamzy, dlsaws kse efbwe th hrj xcyi, essf ecp bvx htzsmyr hv hzyoai esspv xzblc. Ld tvv xp, W dmww bvx ozbhxp xcuij ec alp zfneytnhxtzb, P gszczi ez upzp xcuij ec alp asywzy kos td wu rppr vj spzw, wz evl qzysf azyh ii elylr mj calpcg, tevp gbvp evl tpcgvr rph alp cshp xzblc.'

字符频率大家在网上也都可以搜得到。

'''
'a': 0.0651738, 'b': 0.0124248, 'c': 0.0217339,
'd': 0.0349835, 'e': 0.1041442, 'f': 0.0197881,
'g': 0.0158610, 'h': 0.0492888, 'i': 0.0558094,
'j': 0.0009033, 'k': 0.0050529, 'l': 0.0331490,
'm': 0.0202124, 'n': 0.0564513, 'o': 0.0596302,
'p': 0.0137645, 'q': 0.0008606, 'r': 0.0497563,
's': 0.0515760, 't': 0.0729357, 'u': 0.0225134,
'v': 0.0082903, 'w': 0.0171272, 'x': 0.0013692,
'y': 0.0145984, 'z': 0.0007836, ' ': 0.1918182
'''

接下来就是破解的python代码

#coding=utf-8
#-*- coding:utf-8 –*-
def c_alpha(cipher):   # 去掉非字母后的密文
    cipher_alpha = ''
    for i in range(len(cipher)):
        if (cipher[i].isalpha()):
            cipher_alpha += cipher[i]
    return cipher_alpha

# 计算cipher的重合指数
def count_CI(cipher):
    N = [0.0 for i in range(26)]
    cipher = c_alpha(cipher)
    L = len(cipher)
    if cipher == '':
        return 0
    else:
        for i in range(L):     #计算所有字母的频数,存在数组N当中
            if (cipher[i].islower()):
                 N[ord(cipher[i]) - ord('a')] += 1
            else:
                 N[ord(cipher[i]) - ord('A')] += 1
    CI_1 = 0
    for i in range(26):
        CI_1 += ((N[i] / L) * ((N[i]-1) / (L-1)))
    return CI_1

# 计算秘钥长度为 key_len 的重合指数
def count_key_len_CI(cipher,key_len):        
    un_cip = ['' for i in range(key_len)]    # un_cip 是分组 
    aver_CI = 0.0
    count = 0
    for i in range(len(cipher_alpha)):
        z = i % key_len
        un_cip[z] += cipher_alpha[i]
    for i in range(key_len):
        un_cip[i]= count_CI(un_cip[i])
        aver_CI += un_cip[i]
    aver_CI = aver_CI/len(un_cip)
    return aver_CI

## 找出最可能的前十个秘钥长度
def pre_10(cipher):
    M = [(1,count_CI(cipher))]+[(0,0.0) for i in range(49)]
    for i in range(2,50):
        M[i] = (i,abs(0.065 - count_key_len_CI(cipher,i)))
    M = sorted(M,key = lambda x:x[1])   #按照数组第二个元素排序
    for i in range(1,10):
        print (M[i])

F = [
0.0651738, 0.0124248, 0.0217339,
0.0349835, 0.1041442, 0.0197881,
0.0158610, 0.0492888, 0.0558094,
0.0009033, 0.0050529, 0.0331490,
0.0202124, 0.0564513, 0.0596302,
0.0137645, 0.0008606, 0.0497563,
0.0515760, 0.0729357, 0.0225134,
0.0082903, 0.0171272, 0.0013692,
0.0145984, 0.0007836
]       # 英文字符频率。
cipher = 'Asolm dlpy dlsaws aewv oisfe Flh Ncczw Zcuhrtkoamzy, hoij dvhop evlmc sshhd lbk hzyh avfdh altd cyklywgeetcu. Tpzdsi cpojx qzf px zcwnmylhlh qcct emzia jzff filcg hkz, lh alle hpqp, l upvw dvvapo cmj spf syifff my evl tfmzpg xprpe, dss aswo dlsaws alle vlv qlhoic hoz e xpaiic zt alp Csk Gczgz Scroumklhpsy. Xcyi lyr tscp dlsaws rrph vlv, essf xszinle evlc hpfl gspoaio mm alp zfneytnhxtzb, alp xcuij evlc ozbhxpo khw yzh bwpo wu xsp fpkse khc. Ess prntrlre soz e rcshx ypuhxtgs prqwilrnp cu xsp Flh Ncczw Zcuhrtkoamzy, dlsaws kse efbwe th hrj xcyi, essf ecp bvx htzsmyr hv hzyoai esspv xzblc. Ld tvv xp, W dmww bvx ozbhxp xcuij ec alp zfneytnhxtzb, P gszczi ez upzp xcuij ec alp asywzy kos td wu rppr vj spzw, wz evl qzysf azyh ii elylr mj calpcg, tevp gbvp evl tpcgvr rph alp cshp xzblc.'

cipher_alpha = c_alpha(cipher)
print u"秘钥长度为:"
pre_10(cipher)

得出的结果排名靠前的都是5的倍数(栗子是'hello'),我们可以猜测秘钥长度为5

秘钥长度
2.破解单个秘钥
# 猜测单个秘钥得到的重合指数
def count_CI2(cipher,n):     # n 代表我们猜测的秘钥,也即偏移量
    N = [0.0 for i in range(26)]
    cipher = c_alpha(cipher)
    L = len(cipher)
    for i in range(L):     #计算所有字母的频数,存在数组N当中
        if (cipher[i].islower()):
            N[(ord(cipher[i]) - ord('a') - n)%26] += 1
        else:
            N[(ord(cipher[i]) - ord('A') - n)%26] += 1  
    CI_2 = 0
    for i in range(26):
        CI_2 += ((N[i] / L) * F[i])
    return CI_2

def one_key(cipher,key_len):
    un_cip = ['' for i in range(key_len)]   
    cipher_alpha = c_alpha(cipher)
    for i in range(len(cipher_alpha)):     # 完成分组工作
        z = i % key_len
        un_cip[z] += cipher_alpha[i]
    for i in range(key_len):
        print (i)
        pre_5_key(un_cip[i])     ####这里应该将5个分组的秘钥猜测全部打印出来

## 找出前5个最可能的单个秘钥
def pre_5_key(cipher):
    M = [(0,0.0) for i in range(26)]
    for i in range(26):
        M[i] = (chr(ord('a')+i),abs(0.065 - count_CI2(cipher,i)))
    M = sorted(M,key = lambda x:x[1])   #按照数组第二个元素排序

    for i in range(10):
        print (M[i])

key_len = 5   #输入猜测的秘钥长度
one_key(cipher,key_len)

得出的秘钥会按照可能性进行排序,排在第一位的字符取出得到'hello'。(图片太长了,所以只截取一部分)


单个秘钥

以上就是维吉尼亚秘钥的破解了。

你可能感兴趣的:(python实现维吉尼亚秘钥破解)