简单异或 密钥长度未知 如何求解? 2021/09/05

本文介绍了在不知道密钥长度的情况下,如何通过卡希斯基实验和重合指数来推测密钥长度。同时,提出了一种假定明文密钥枚举法,通过找出密文中每个字符可能的密钥字符来辅助确定密钥长度。然而,这种方法可能产生多个解,建议使用Many-Time-Pad方法进行更准确的解密。以一个CTF挑战为例,详细展示了这种方法的实践过程和可能出现的问题。
摘要由CSDN通过智能技术生成

这里讨论的是明文与密钥进行简单异或的加密,但解密时密钥长度不给出的情况下进行解密。

因为之前讨论过已知密钥长度进行解密的情况(Many-Time-Pad),给出了相应的解决方案。所以在密钥长度不知道的情况下,首要任务是想办法求解密钥长度。

为了求出密钥长度len,可以利用卡希斯基实验和重合指数。

参考:[卡希斯基实验和重合指数](密码学原理-篇1:重合指数 - 简书 (jianshu.com))

简单来说就是卡希斯基实验是利用明文中可能会出现相同的单词(或相同的部分连续字母)被同样的密钥字母进行加密,对应密文字符在密文中重复出现,而这个正好可以用来确定密钥的长度。在密文中找某部分字符连续出现的位置,两者相差的距离就是密钥长度本身或倍数,多找几次不同的字符,他们的公因数即是密钥长度。

重合指数则是枚举密钥长度,将密文按密钥长度分组,求出每组的重合指数并取平均值,若近似0.065,则选取的数值为密钥长度。

嗯……貌似懂了,但真的要按上述方法求解可能会有点麻烦,呃,我确实没想出来。

直到我找到了一个好方法:[De1CTF2019_ssrfme_xorz · 大专栏 (dazhuanlan.com)]

我称它为
(突然发现这个方法的使用有点局限:))
假定明文密钥枚举法:

假设情况是这样的,明文和密钥的组成元素均在可打印字符内,假定可能组成明文的字符集合为m_elem,可能组成密钥的字符集合为k_elem。让密文中每个字符与k_elem挨个试探,若密文异或上某个字符,得到的字符在m_elem中,那么该字符极有可能为真正的密钥字符。

于是,我们就得到了密文中每个字符对应的真正的密钥字符的集合。这样我们就可以开始求密钥长度了!!!

枚举密钥长度len,对密文按len进行分组,则共有len列。每列是由相同的密钥字符进行加密的,所以我们只需要将该列字符对应的密钥字符的集合做交集,若有交集,则说明该长度可能为密钥长度。为了让得到的结果更具可能性(满足某列并不能满足全列),我们对第一组字符按列进行上述操作,若有交集的数量大于列数的一半,则记录该数值。

求出了密钥长度后,我们就可以Many-Time-Pad的方法解出密钥以及明文。不过我们也可以使用另一种方法,因为在求解密钥长度的时候,我们已经求出了密文中每个字符的可能情况,之要按列求交集,得出来的按顺序依次排列,就是密钥了。

但是!!!如果你的m_elem和k_elem选的不好的话,密钥长度和密钥是有可能出现多种情况的!!!

所以在求出密钥长度后,求密钥和明文还是使用Many-Time-Pad方法吧。

例题:

这里拿 [De1CTF2019]xorz 做分析:

根据给出的cipher和salt,可以先求出key^m的结果。

根据假定明文密钥枚举法的描述,先求出密文中每个字符对应密钥字符的可能情况

from itertools import *

c = '49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c'
salt="WeAreDe1taTeam"
si=cycle(salt)

list_c = []
for i in range(0,len(c),2):
    list_c.append(hex(int(c[i:i+2],16) ^ ord(next(si)))[2:].zfill(2))

#list_c相当于 m ^ key ,这里假设m,key中的元素均为可打印字符
key_element = ' !#$%&*+-./0123456789<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\^_`abcdefghijklmnopqrstuvwxyz|~'
m_element =  ' !,.0123456789:;?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'

c_xor_k = []
for i in range(len(list_c)):
    temp = []
    for j in key_element:
        if chr(int(list_c[i],16)^ord(j)) in m_element:
            temp.append(j)
    c_xor_k.append(temp)#密文中每个元素对应密钥的可能值

求出来后,按列求交集,满足多列的枚举值记录,作为密钥长度的参考。

#分析密钥长度
#将密文按枚举值分组
#按列依次对字符对应的密钥的可能值做交集,如果交集不为空,则密钥长度可能为该枚举值
Len = []
for length in range(1,38):
    num = 0
    for index in range(length):
        x = set(c_xor_k[index])#变成集合
        for i in range(index+length,len(c_xor_k),length):
            x &= set(c_xor_k[i])
        if len(x) != 0:
            num += 1
    if num/length > 0.5:
        Len.append(length)
print(Len)

因为我对key_element和m_element做了一些修改,所以得到的值只有30,故密钥长度为30.

求出密钥长度后,如果继续按该方法求密钥,则可能会有多种情况,也可能会出现得出的密钥字符个数不为密钥长度(因为做交集的时候,只是满足的一半的字符,剩下的为空集,也就是找不到对应的密钥字符,而在满足的那部分里,交集里可能并不只有一个字符)

key = []
for i in range(30):
    x = set(c_xor_k[i])
    for j in range(i+30,len(c_xor_k),30):
        x &= set(c_xor_k[j])
    if len(x) != 0:
        key.append(list(x))
print(key)
结果:[['W'], ['2', '3'], ['l'], ['c'], ['0'], ['l'], ['3'], ['t'], ['O', 'P', 'p', 's', 'N'], ['j'], ['o'], ['1', '0'], ['n'], ['u'], ['5', '9'], ['5'], ['u'], ['n'], ['1'], ['o'], ['j'], ['O'], ['t'], ['3'], ['m'], ['1', '&'], ['c'], ['m'], ['3'], ['W']]

求出的与实际密钥不同!!!淦

但是当你选用的

key_element='1234567890qwertyuiopasdfghjklxcvbnmQWERTYUIOPSDFGHJKLZXCVBNM'

m_element='1234567890qwertyuiopasdfghjklxcvbnmQWERTYUIOPSDFGHJKLZXCVBNM` ;,.'

求出来的是唯一且正确的:

[['W'], ['3'], ['l'], ['c'], ['0'], ['m'], ['3'], ['t'], ['O'], ['j'], ['o'], ['1'], ['n'], ['u'], ['5', '9'], ['5'], ['u'], ['n'], ['1'], ['o'], ['j'], ['O'], ['t'], ['3'], ['m'], ['0'], ['c'], ['l'], ['3'], ['W']]

所以推荐使用Many-Time-Pad

from itertools import *
import Crypto.Util.strxor as xo
import codecs
import numpy as np
from Crypto.Util.number import *

def ischr(x):
    #因为从字节串中去出的是对应的ascii码值,所以进行数字比较
    if x>=ord('a') and x<=ord('z'):
        return True
    if x>=ord('A') and x<=ord('Z'):
        return True
    else:
        return False

def trans(x,y):
    if msg[x,y] != 0:
        return
    msg[x,y] = ord(' ')
    for index in range(len(c)):
        if index != x:
            msg[index,y] = xo.strxor(c[x], c[index])[y] ^ ord(' ')

c = '49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c'
salt="WeAreDe1taTeam"
si=cycle(salt)

string_c = ''
for i in range(0,len(c),2):
    string_c += hex(int(c[i:i+2],16) ^ ord(next(si)))[2:].zfill(2)

c = [long_to_bytes(eval('0x'+string_c[i:i+60])) for i in range(0,len(string_c),60)]#两个十六进制数对应一个字符,共30个字符

number = []
for i in range(len(c)):
    xor_c = [xo.strxor(c[i],c[j]) for j in range(len(c)) if j != i]#strxor:两个字节串进行异或
    for pos in range(len(c[i])):
       num = len([ci for ci in xor_c if ischr(ci[pos])])
       number.append([num,i,pos])#[个数,行,列]
   
number = sorted(number,reverse = True)

msg = np.zeros([len(c), len(c[0])], dtype=int)#创建一个全为零的数组,用于填充明文

for i in number:
    trans(i[1],i[2])

mingwen = ''
for i in msg:
    for j in i:
        mingwen += chr(j)
    mingwen += '\n'
print(mingwen)

ming = 't makes me sin awards me pain.'.encode()
mi = b'#\x13\x01\x02[\x08@T"\x0fOB\x07\x1b\x15T\x02\x0fC\x0b\x19o\x19VM@\x02\x05]y'
flag = xo.strxor(ming,mi)
print(flag)

得到

In faith I do not love thee wi
th mine eyes,For they in thee 
a thousand errors note;But `ti
s my heart that loves what the
y despise,Who in despite of vi
ew is pleased to dote.Nor are 
mine ears with thy tongue`s tu
ne delighted;Nor tender feelin
g to base touches prone,Nor ta
ste, nor smell, desire to be i
nvitedTo any sensual feast wit
h thee alone.But my five wits,
 nor my five senses canDissuad
e one foolish heart from servi
ng thee,Who leaves unswayed th
e likeness of a man,Thy proud 
heart`s slave and vassal wretc
h to be.Only my plague thus fa
r I count my gain,That she tha
t makes me sin awards me pain.

b'W3lc0m3tOjo1nu55un1ojOt3m0cl3W'

总结:

Many-Time-Pad没过几天就差不多忘掉了,淦!!!得把这两种方法多敲几遍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值