代换密码破解

题目:

请解密以下代换密码的密文:

JGRMQOYGHMVBJWRWQFPWHGFFDQGFPFZRKBEEBJIZQQOCIBZKLFAFGQVFZFWWEOGWOPFGFHWOLPHLRLOLFDMFGQWBLWBWQOLKFWBYLBLYLFSFLJGRMQBOLWJVFPFWQVHQWFFPQOQVFPQOCFPOGFWFJIGFQVHLHLROQVFGWJVFPFOLFHGQVQVFILEOGQILHQFQGIQVVOSFAFGBWQVHQWIJVWJVFPFWHGFIWIHZZRQGBABHZQOCGFHX

解析:

    代换密码(Simple Substitution Cipher),顾名思义,就是一种使用substitution来进行加密的算法。代换密码采用一一映射的方式,将明文每个字符逐一唯一地代换成另外的一个字符。对于代换密码的详细说明,可点击这里。在凯撒密码中,每个字母都按照其在字母表中的顺序向后(或向前)移动固定数目(循环移动)作为密文。而代换密码则不是简单的按顺序移位,而是完全混乱的毫无规律地进行映射。在这两种加密方法中,密钥的长度都是26个字符,如密钥“DGLHEIJPQFRMSTNUVWXKOYZBAC”,则在加密的时候,A->D,B->G,以此类推。下面举个例子:

(1)凯撒密码:

明文:ABCDEFGHIJKLMNOPQRSTUVWXYZ

密文:DEFGHIJKLMNOPQRSTUVWXYZABC

(2)代换密码

明文:ABCDEFGHIJKLMNOPQRSTUVWXYZ

密文:DGLHEIJPQFRMSTNUVWXKOYZBAC

攻击:

    观察前面的两种加密方式,可以发现,凯撒就是简单的字母的移位,例子中的偏移量是3,仅仅有25种可能的密钥,暴力穷举法可以瞬间攻破它。而在代换密码中,则没法简单地看出字符的对应关系了,其可能的密钥可达26!个,因此要攻击代换密码的话,就没法采用简单的暴力破解方法了。

    对于代换密码,有多种的常用的攻击方法,其中常见两种为“词频分析法”和“爬山法”。所谓的“词频攻击法”,即把密文中字母使用的相对频率统计出来,与日常英语中英文字母使用的相对频率进行比较,利用频率进行匹配,即密文中频率较高的字母对应于日常英语中使用频率较高的英文字母,反之亦然,从而推导出密钥。由于要使用到数学统计的方法,此攻击方法需要用到较长的密文。这里介绍一个网站,词频攻击法可点击这里进行线上解密。

    下面着重介绍“爬山法”。

“爬山法”

    在这个算法中,我们需要选择性地尝试不同解密密钥,然后给每一个解密出来的明文标记上一个适应度。若解密出来的明文越接近我们的日常用的英语,它的适应度就越高;若解密出来的明文越难读懂或者越不像我们日常用的英语,则其适应度越低。我们使用一个基于quadgram statistics的适应度计算方法。关于quadgram statistics的详细介绍可点击这里

    下面详细描述算法步骤:

(1)随机生成一个key,称为parentkey,用它解密得对应的明文m1,对明文计算适应度d1。

(2)随机交换parentkey中的两个字符得到子密钥child,解密出对应的明文m2并计算适应度d2。

(3)若d1<d2,则child成为新的parentkey

(4)不断循环进行(2)(3)直到最后的1000次循环中不再有更高的适应度生成

(5)回到(1)重新生成parentkey继续迭代寻找,或者由操作者终止程序

    重新执行(1),是为了防止(2)(3)的操作使结果陷入局部最优的困境。对于生成的明文的适应度的比较,其实可以看作是对不同解密密钥的比较,解密出来的明文的适应度越高,对应的密钥就更好。

    下面是我参照官方源码写的代码:


# -*- coding: UTF-8 -*-

"""算法思路:因为整个密文都只是大写英文字母,所以并不需要进行字符转换操作。解密的时候,在生成了key之后,将key放入一个字典变量中,字典中每个变量都是“密文字符:明文字符”的映射对,从而对密文的字符进行一个接着一个地转换。循环嵌套有两层,外层是每次随机生成一个起始的parentkey,里层进行爬山法,每次随机交换parentkey里的两个元素以解密,最后每次外层循环都判断一次是否找到更优的密钥"""

import random

from ngram_score import ngram_score

#[!]参数初始化

ciphertext='JGRMQOYGHMVBJWRWQFPWHGFFDQGFPFZRKBEEBJIZQQOCIBZKLFAFGQVFZFWWEOGWOPFGFHWOLPHLRLOLFDMFGQWBLWBWQOLKFWBYLBLYLFSFLJGRMQBOLWJVFPFWQVHQWFFPQOQVFPQOCFPOGFWFJIGFQVHLHLROQVFGWJVFPFOLFHGQVQVFIEOGQILHQFQGIQVVOSFAFGBWQVHQWIJVWJVFPFWHGFIWIHZZRQGBABHZQOCGFHX'

parentkey = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')

key = {'A':'A'}#只是用来声明key是个字典

fitness = ngram_score('quadgrams.txt') #读取quadgram statistics

parentscore = -99e9

maxscore = -99e9

#[!]

j = 0

print '***********************************start*******************************************'

while 1:

    j = j+1

    random.shuffle(parentkey)#随机打乱key中的元素,以达到生成新的起始密钥来进行尝试的目的

    #将密钥做成字典,以日后进行一一映射来解密

    for i in range(len(parentkey)):

        key[parentkey[i]] = chr(ord('A')+i)

    #用字典一个一个字符地进行一一映射地代换回去以解密

    decipher = ciphertext

    for i in range(len(decipher)):

        decipher = decipher[:i] + key[decipher[i]] + decipher[i+1:]

    parentscore = fitness.score(decipher)#计算适应度

    #[!]在当前密钥下随机交换两个密钥的元素从而寻找是否有更优的解

    count = 0

    while count < 1000:

        a = random.randint(0,25)

        b = random.randint(0,25)

        #随机交换父密钥中的两个元素生成子密钥,并用其进行解密

        child = parentkey[:]

        child[a],child[b] = child[b],child[a]

        childkey = {'A':'A'}

        for i in range(len(child)):

            childkey[child[i]] = chr(ord('A')+i)

        decipher = ciphertext

        for i in range(len(decipher)):

            decipher = decipher[:i] + childkey[decipher[i]] + decipher[i+1:]

        score = fitness.score(decipher)


        #若用子密钥所生成的明文更加贴近英文,也就是此明文的适应度更高,则用此子密钥代替其对应的父密钥

        if score > parentscore:

            parentscore = score

            parentkey = child[:]

            count = 0

        count = count+1

    #[!]

    #我认为已经找到了最优的key,然后输出该key和明文

    if parentscore > maxscore:

        maxscore = parentscore

        maxkey = parentkey[:]

        for i in range(len(maxkey)):

            key[maxkey[i]] = chr(ord('A')+i)

        decipher = ciphertext

        for i in range(len(decipher)):

            decipher = decipher[:i] + key[decipher[i]] + decipher[i+1:]

        print 'Currrent key: '+''.join(maxkey)

        print 'Iteration total:', j

        print 'Plaintext: ', decipher


备注:quadgrams.txt和ngram_score.py可点击这里在文章最下方获取。

算法结果:

    可以看出,此算法效率还是比较高的,平均十次迭代以内可以得出比较令人满意的明文。

    不过此算法还是有点可改进的地方,就是最后还得手动在单词之间和密文换行的时候添加空格,才可得出结果:CRYPTOGRAPHIC SYSTEMS ARE EXTREMELY DIFFICULT TO BUILD NEVERTHELESS FOR SOME REASON MANY NON EXPERTS INSIST ON DESIGNING NEW ENCRYPTION SCHEMES THAT SEEM TO THEM TO BE MORE SECURE THAN ANY OTHER SCHEME ON EARTH THE UNFORTUNATE TRUTH HOWEVER IS THAT SUCH SCHEMES ARE USUALLY TRIVIAL TO BREAK

    另外还有其他可以改进的地方,对于大写字符以外的字符的处理不可简单地使用上面的代码,但可以使用库pycipher,将官方示例代码的稍微改了一下可得以下代码:

from pycipher import SimpleSubstitution as SimpleSub

import random

import re

from ngram_score import ngram_score

fitness = ngram_score('quadgrams.txt') # load our quadgram statistics

ctext='JGRMQOYGHMVBJWRWQFPWHGFFDQGFPFZRKBEEBJIZQQOCIBZKLFAFGQVFZFWWEOGWOPFGFHWOLPHLRLOLFDMFGQWBLWBWQOLKFWBYLBLYLFSFLJGRMQBOLWJVFPFWQVHQWFFPQOQVFPQOCFPOGFWFJIGFQVHLHLROQVFGWJVFPFOLFHGQVQVFIEOGQILHQFQGIQVVOSFAFGBWQVHQWIJVWJVFPFWHGFIWIHZZRQGBABHZQOCGFHX'

ctext = re.sub('[^A-Z]','',ctext.upper())

maxkey = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')

maxscore = -99e9

parentscore,parentkey = maxscore,maxkey[:]

print "Substitution Cipher solver, you may have to wait several iterations"

print "for the correct result. Press ctrl+c to exit program."

# keep going until we are killed by the user

i = 0

while 1:

    i = i+1

    random.shuffle(parentkey)

    deciphered = SimpleSub(parentkey).decipher(ctext)

    parentscore = fitness.score(deciphered)

    count = 0

    while count < 1000:

        a = random.randint(0,25)

        b = random.randint(0,25)

        child = parentkey[:]

        # swap two characters in the child

        child[a],child[b] = child[b],child[a]

        deciphered = SimpleSub(child).decipher(ctext)

        score = fitness.score(deciphered)

        # if the child was better, replace the parent with it

        if score > parentscore:

            parentscore = score

            parentkey = child[:]

            count = 0

        count = count+1

    # keep track of best score seen so far

    if parentscore>maxscore:

        maxscore,maxkey = parentscore,parentkey[:]

        print '\nbest score so far:',maxscore,'on iteration',i

        ss = SimpleSub(maxkey)

        print '    best key: '+''.join(maxkey)

        print '    plaintext: '+ss.decipher(ctext)

    以上算法同样算法效率很高。

     “爬山法”的具体描述可点击这里

    有什么错误或者不足还请大家多多指教~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容

  • 0x01 目录 常见编码: ASCII编码 Base64/32/16编码 shellcode编码 Quoted-p...
    H0f_9阅读 12,780评论 2 17
  • CTF中那些脑洞大开的编码和加密 0x00 前言 正文开始之前先闲扯几句吧,玩CTF的小伙伴也许会遇到类似这样的问...
    查无此人asdasd阅读 6,008评论 0 19
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,631评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,656评论 18 139
  • 再见你时, 你已经过上了归园田居的生活, 面朝大山,果香四溢。 不喂马,你喂鱼, 关心起了粮食和蔬菜。 你像侍弄盆...
    铅华眉深阅读 459评论 0 3