密码学—Vigenere破解Python程序

文章详细阐述了如何利用Kasiski测试法和重合指数法来破解Vigenere密码,包括识别密钥长度、计算重合指数、确定相对位移等步骤,并介绍了编程实现中的关键函数和流程。文章强调了统计常见字母频率在破解过程中的重要性,并提到对于非正常文本的局限性。

概要

破解Vigenere需要Kasiski测试法与重合指数法的理论基础

具体知识点细节看下面这两篇文章

预备知识点学习

下面两个是结合起来使用猜测密钥长度的,只有确认了密钥长度之后才可以进行破解。

❀点击学习Kasiski测试法❀
❀点击学习重合指数法❀

整体流程

破解的方法就是不同的凯撒表中每个字母相乘得到的交互重合指数是否接近0.065,并且每两个表都要通过将凯撒表位移得到最接近0.065的那个偏移量,然后就可以得知这两个表相对位移多少的时候交互重合指数最接近0.065,以此类推将所有单表能够一起的可能组合都算一遍指数,最后观察每个组合MIC然后找出接近0.065且算出偏移量将所有未知数列出公式计算可得出26份密钥(难点在于如何计算出26份密钥!!!为此本座真的头秃了一大片)

流程如下
按照猜测的密钥长度分组计算单个凯撒表的每一个字母出现的频率

所有凯撒表两两组合

计算俩表之间的偏移量为g的时候,对应的交互重合指数为MIC
这里的重合指数计算是通过两个表的下标一致来进行相乘,只是偏移量不同
然后会出现26次偏移,因为凯撒密钥空间为26个字母
因此俩凯撒表组合就会产生26份重合指数。如图所示为任意两组的MIC数据
在这里插入图片描述

得到了任意组合的26份交互重合指数后我们要找出最接近0.065的,并且算出他的相对位移(偏移量),就会得到类似的公式。如何在程序中实现,这个让我头秃了(后面附上代码)


i=0,j=1,相对位移s=?时,MIc=最接近0.065



i=4,j=5,相对位移s=?时,MIc=最接近0.065
则有:
k0-k2=2
k0-k3=9



k4-k5=4
由此求得密钥字间的相对位移

因为已知k0 ~ k5运算得到的公式,然后我们可以假定k0为a ~ z的字母,得到一个字母之后其他未知数也就能够计算了,因此会产生26份密钥出来


有26份密钥之后,需要找出最真密钥,其他都是伪密钥。
需要用到统计的26个字母常用的频率,这个可能就是Vigenere的缺陷了,一旦是一个非正常文本就几乎破解不了。因为用的就是常用词频统计的出来的概率,同样这个表不用我们统计,网上一查一堆。这里将其称为AZ表

'''a-z的频率,这是不是指你加密的词频,
而是大家阅读各式各样的文章中出现次数
最多的也可以理解为大数据统计出来的'''
[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]

由于已知密钥了,所以只需要将其密钥的单个字母对单个凯撒解密之后,将其解密的凯撒表与AZ表对应的字母概率相乘,最后将其所有结果加起来是否接近0.065,如果接近则可以考虑将其纳入真实密钥的列表名单。

最后缩小范围得到所有最接近密钥之后,我们可以选择采用一个一个的将密文解密,解出来之后看是否读的通顺即为密钥。(我到了这一步就用肉眼观察了)

技术名词解释

  • Ic重合指数:指的是单个凯撒表的每一个字母概率的平方,最后26个字母得出的概率相加
  • MIc交互重合指数:不同表之间对应字母概率相乘,最后26个字母得出的概率相加
  • 相对位移:不同表之间虽然用不同密钥加密,但是不同密钥之间又有一个密钥距离,比如1表用a加密,2表用c加密,那么这两个表的相对位移就是2,但是在破解的时候不知道是偏移多少,所以需要循环将其偏移,然后计算MIc计算出最接近0.065的就是可能的真实偏移量。
    具体为何要偏移26,是因为密钥空间26,所以你必须位移26次。

技术细节

程序中实现的分工必须要很明确,否则乱成一坨。

函数分工
shiftCaserCode:将密文成不同的凯撒表
caltMIC:计算重合指数
caltResult:找出所有可能的密钥
findEndKey:通过与AZ表概率交互重合得出最接近0.065的密钥

小结

破解Vigenere十分艰辛,但是收获满满,不仅对破解有了一定了解,很大一部分影响是编程技巧在此得到了提升,对自己的编程能力更加有信心。
比如如何在Kasiski测试法中如何找到片段出现次数最多且要考虑不同的长度片段的统计还要计算出相同长度出现次数最高的那一个。
在计算重合指数的时候对字典有了一个更深的了解,字典可以按需使用,我这里就采用数组作为键,大大提高了编程效率,还认识到了在3.6版本之后的Python已经支持按顺序打印输出了,不再是无序的字典,这也帮我解决了很大一部分繁琐的操作。

代码

由于在我的程序中是用类和pyqt5结合使用,所以下面的函数都是在类中定义的,主要运行功能在outputKeys函数中进入


class CrackVigenere(ModuleCode):
    name = 'CrackVigenere'
    # 正常文本统计出来的概率
    NormalFrequ = [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]

    def __init__(self, appInit):
        super().__init__(appInit, self.name)

    def newMe(self, appInit):
        return CrackVigenere(appInit)

    def InitUI(self):
        super(CrackVigenere, self).InitUI()

    def bindCapabilities(self):
        self.enterbtn.clicked.connect(lambda: self.outputKeys())
        self.save_file_btn.clicked.connect(lambda: self.saveFileText())
        self.choice_file_btn.clicked.connect(lambda: self.choiceFIleText())

    # ===============================================找出所有可能的密钥==================================================
    def shiftCaserCode(self, mess, keylen):
        newdit = {
   
   }
        for i in range(keylen):
            newdit[i] = []
        c = self.messGroupByN(mess, keylen)  # 将密文分组
        for i in range(len(c)):  # 将分好组的c密文抽出每一行对应一个加密字母,变成一行的单行凯撒加密
            for j in range(len(c[i])):
                if j < len(c[i]):
                    newdit[j].append(ord(c[i][j]) - ord('A'))
        return newdit

    def messGroupByN(self, mess, n):  # 根据密钥长度分组
        sunlist = []
        temp = [''] * n
        # print(math.ceil(len(mess) / n
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

竹等寒

谢过道友支持,在下就却之不恭了

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

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

打赏作者

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

抵扣说明:

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

余额充值