vigenere-like cipher
设密文为 c c c,密钥为 k k k,则密文为 c ⊕ k c \oplus k c⊕k。
首先,题干中所说的维吉尼亚-like是指将密钥复制为和明文一样长(多出来的删除掉),然后进行异或运算。
因此,结合one-time pad的思想,可知利用密钥和密文进行异或,便得到明文。
题干中提到“明文包含大写字母、小写字母、标点符号和空格,但不包含数字”,因此可以枚举密钥,然后将密钥和密文进行异或,得到的结果中,如果有数字,则说明该密钥不正确。据此暴力找出密钥。
c = "F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923CAB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84CC931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D963FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47EFD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63CED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A85A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794"
cArr = [int(c[i:i+2], 16) for i in range(0, len(c), 2)] # 16进制转10进制:[249, 109, ...]
print(len(cArr))
correctChar = []
for x in range(32,126):
correctChar.append(x)
for x in range(48, 58): # 不包含数字
correctChar.remove(x)
def findindexkey(subarr): # 该函数可以找出将密文subarr解密成可见字符的所有可能值
test_keys=[]# 用于测试密钥
ans_keys=[]# 用于结果的返回
for x in range(0x00,0xFF):# 枚举密钥里所有的值
test_keys.append(x)
ans_keys.append(x)
for i in test_keys: # 对于0x00~0xFF里的每一个数i和subarr里的每个值s异或
for s in subarr:
if s^i not in correctChar: # 用i解密s,如果解密后明文不是可见字符,说明i不是密钥
ans_keys.remove(i) # 去掉ans_keys里测试失败的密钥
break
return ans_keys
def findKeySpace(maxLen=14):
keySpace = []
for keylen in range(1,maxLen): # 枚举密钥的长度1~14
for index in range(0,keylen): # 对密钥里的第index个进行测试
subarr=cArr[index::keylen] # 每隔keylen长度提取密文的内容,提取出来的内容都被密文的第index个加密
ans_keys=findindexkey(subarr) # 找出密钥中第index个的可能的值
if ans_keys:
print('keylen=',keylen,'index=',index,'keys=',ans_keys)
keySpace.append(ans_keys) # 将所有可能的值存入keySpace
return keySpace
# 得出结论,密钥长度为 7
keySpace = findKeySpace()
cnt = 1
for i in range(len(keySpace)):
print('in index ', i, 'key space size is ', len(keySpace[i]))
cnt *= len(keySpace[i])
print('total key space size is ', cnt)
# 结果是 497664,可以暴力枚举
# 枚举所有可能的密钥,长度为 7
hh = []
def enumKey(keySpace):
for i in keySpace[0]:
for j in keySpace[1]:
for k in keySpace[2]:
for l in keySpace[3]:
for m in keySpace[4]:
for n in keySpace[5]:
for o in keySpace[6]:
key = [i,j,k,l,m,n,o]
hh.append(key)
enumKey(keySpace)
ansSpace = []
for key in hh:
ansSpace.append(''.join([chr(cArr[i]^key[i%7]) for i in range(len(cArr))]))
# 输出为txt文件
with open('ans.txt', 'w') as f:
for ans in ansSpace:
f.write(ans+'\n')
import string
# 不妨缩小一下明文范围:大写字母,小写字母,空格,逗号,句号,问号,感叹号
testString = string.ascii_letters + ' ,.?!'
correctChar = [ord(x) for x in testString]
keySpace = findKeySpace()
# 此时解唯一,密钥为:[186, 31, 145, 178, 83, 205, 62]
# 解密时刻
keyLen = 7
for i in range(len(cArr)):
print(chr(cArr[i] ^ keySpace[i % keyLen][0]), end='')