It is said that Vigenere cipher does not achieve the perfect secrecy actually :-)Tips:1.The encode pragram is given;2.Do u no index of coincidence ?3.The key is last 6 words of the plain text(with “nctf{}” when submitted, also without any interpunction)
#include<stdio.h> #define KEY_LENGTH 2 // Can be anything from 1 to 13
main(){ unsignedchar ch; FILE *fpIn, *fpOut; int i; unsignedchar key[KEY_LENGTH] = {0x00, 0x00}; /* of course, I did not use the all-0s key to encrypt */
i=0; while (fscanf(fpIn, "%c", &ch) != EOF) { /* avoid encrypting newline characters */ /* In a "real-world" implementation of the Vigenere cipher, every ASCII character in the plaintext would be encrypted. However, I want to avoid encrypting newlines here because it makes recovering the plaintext slightly more difficult... */ /* ...and my goal is not to create "production-quality" code =) */ if (ch!='\\n') { fprintf(fpOut, "%02X", ch ^ key[i % KEY_LENGTH]); // ^ is logical XOR i++; } } fclose(fpIn); fclose(fpOut); return; }
怎样才能求出密钥的长度?这里引入十分重要的一点,也是求解维吉尼亚密码的关键,即重合指数(index of coincidence)。重合指数是指,在一串字母中,任意取出两个字母,这两个字母相同的概率。例如,随机给出一串混乱的字母,它的重合指数总会在0.45附近,而一串有意义的英语段落,因为字母使用频率的影响,重合指数总是会在0.65附近,并且段落越长越接近这两个值。
defcontrolFlow(): keyLengthRange = range(1,15) cipher = readCipher('c.txt') for i in keyLengthRange: keyGroup = getKeyRange(i, cipher) for a in keyGroup: if0 == len(a): break; #密钥每一位一定有值 else: #没有空集就通过字频求具体值 ...... #省略的代码
defgetKeyRange(keyLength, cipher): ''' 测试每一位密钥可能的取值,若生成范围之外的明文,就抛弃这个值 否则,就将这个值加入结果集之中 ''' cipherGroup = getCipherGroup(keyLength, cipher) keyGroup = [[] for a inrange(keyLength)]
count=0 for perCipherGroup in cipherGroup: for keyTest inrange(1,255): for perCipher in perCipherGroup: plainChar = perCipher^keyTest if plainChar notinrange(32,127): break else: keyGroup[count].append(keyTest) count+=1#下一组密文
count=0 for perKey in keyGroup: maxFreq = 0 tempKey = 0 for a in perKey: freq = getLetterFrequency(a, cipherGroup[count]) if freq>maxFreq: #正确的密钥,算出来的综合字频应该尽可能大 maxFreq = freq tempKey = a key.append(tempKey) count += 1 return key
最后解密出原文:
1 2 3 4 5 6 7
defcipherDecrypt(key, cipher): plainText = '' index = 0 for a in cipher: plainText += chr(key[index%len(key)]^a) index += 1 return plainText
最终得出的结果为:
A possible key is: [186, 31, 145, 178, 83, 205, 62]A possible plainText is: Cryptography is the practice and study of techniques for, among other things, secure communication in the presence of attackers. Cryptography has been used for hundreds, if not thousands, of years, but traditional cryptosystems were designed and evaluated in a fairly ad hoc manner. For example, the Vigenere encryption scheme was thought to be secure for decades after it was invented, but we now know, and this exercise demonstrates, that it can be broken very easily.
defreadCipher(filename): file = open(filename, 'r') strCipher = file.read() cipher = [] index = 0 while index < len(strCipher): cipher.append(int(strCipher[index:index+2], 16)) index += 2 return cipher
defgetCipherGroup(keyLength, cipher): cipherGroup = [[] for a inrange(keyLength)] count = 0 while count < len(cipher): cipherGroup[(count) % keyLength] += [cipher[count]] count += 1 return cipherGroup
defgetKeyRange(keyLength, cipher): cipherGroup = getCipherGroup(keyLength, cipher) keyGroup = [[] for a inrange(keyLength)]
count=0 for perCipherGroup in cipherGroup: for keyTest inrange(1,255): for perCipher in perCipherGroup: plainChar = perCipher^keyTest if plainChar notinrange(32,127): break else: keyGroup[count].append(keyTest) count+=1
count=0 for perKey in keyGroup: maxFreq = 0 tempKey = 0 for a in perKey: freq = getLetterFrequency(a, cipherGroup[count]) if freq>maxFreq: maxFreq = freq tempKey = a key.append(tempKey) count += 1 return key
defcipherDecrypt(key, cipher): plainText = '' index = 0 for a in cipher: plainText += chr(key[index%len(key)]^a) index += 1 return plainText
defcontrolFlow(): keyLengthRange = range(1,15) cipher = readCipher('c.txt') for i in keyLengthRange: keyGroup = getKeyRange(i, cipher) for a in keyGroup: if0 == len(a): break; else: #print('A possible key group is:', keyGroup,'\\n') key = getKeyConfirmValue(keyGroup, cipher) print('A possible key is:', key,'\\n') plainText = cipherDecrypt(key, cipher) print('A possible plainText is:', plainText,'\\n')
deffindindexkey(subarr):#该函数可以找出将密文subarr解密成可见字符的所有可能值 visiable_chars=[]#可见字符 for x inrange(32,126): visiable_chars.append(chr(x)) #print(vi) test_keys=[]#用于测试密钥 ans_keys=[]#用于结果的返回 for x inrange(0x00,0xFF):# 枚举密钥里所有的值 test_keys.append(x) ans_keys.append(x) for i in test_keys:#对于0x00~0xFF里的每一个数i和subarr里的每个值s异或 for s in subarr: ifchr(s^i) notin visiable_chars:#用i解密s,如果解密后明文不是可见字符,说明i不是密钥 ans_keys.remove(i)#去掉ans_keys里测试失败的密钥 break #print(ans_keys) return ans_keys strmi='F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923C\\ AB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF\\ 5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84C\\ C931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D96\\ 3FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47E\\ FD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5\\ 923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA\\ 36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63C\\ ED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A8\\ 5A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250\\ C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794' arr=[]#密文,每个元素为字符的ascii码 for x inrange(0,len(strmi),2): arr.append(int(strmi[x:2+x],16)) for keylen inrange(1,14):#枚举密钥的长度1~14 for index inrange(0,keylen):#对密钥里的第index个进行测试 subarr=arr[index::keylen]#每隔keylen长度提取密文的内容,提取出来的内容都被密文的第index个加密 ans_keys=findindexkey(subarr)#找出密钥中第index个的可能的值 print('keylen=',keylen,'index=',index,'keys=',ans_keys) if ans_keys:#如果密钥第index个有可能存在,尝试用密钥的index个去解密文 ch=[] for x in ans_keys: ch.append(chr(x^subarr[0])) print(ch) #运行到这里,观察输出可以发现,密钥长度为7时有解 print('###############') import string deffindindexkey2(subarr):#再造一个函数筛选密钥 test_chars=string.ascii_letters+string.digits+','+'.'+' '#将检查的字符改为英文+数字+逗号+句号+空格 #print(test_chars) test_keys=[]#用于测试密钥 ans_keys=[]#用于结果的返回 for x inrange(0x00,0xFF):# 枚举密钥里所有的值 test_keys.append(x) ans_keys.append(x) for i in test_keys:#对于0x00~0xFF里的每一个数i和substr里的每个值s异或 for s in subarr: ifchr(s^i) notin test_chars:#用i解密s,如果解密后不是英文、数字、逗号、句号、空格,说明i不是密钥 ans_keys.remove(i)#去掉ans_keys里测试失败的密钥 break #print(ans_keys) return ans_keys vigenerekeys=[]#维基尼尔密码的密钥 for index inrange(0,7):#已经知道密钥长度是7 subarr=arr[index::7] vigenerekeys.append(findindexkey2(subarr)) print(vigenerekeys)#输出的是[[186], [31], [145], [178], [83], [205], [62]]. print("#########") ming='' for i inrange(0,len(arr)): ming=ming+chr(arr[i]^vigenerekeys[i%7][0]) print(ming)
deffindindexkey(subarr):#该函数可以找出将密文subarr解密成可见字符的所有可能值 visiable_chars=[]#可见字符 for x inrange(32,126): visiable_chars.append(chr(x)) #print(vi) test_keys=[]#用于测试密钥 ans_keys=[]#用于结果的返回 for x inrange(0x00,0xFF):# 枚举密钥里所有的值 test_keys.append(x) ans_keys.append(x) for i in test_keys:#对于0x00~0xFF里的每一个数i和subarr里的每个值s异或 for s in subarr: ifchr(s^i) notin visiable_chars:#用i解密s,如果解密后明文不是可见字符,说明i不是密钥 ans_keys.remove(i)#去掉ans_keys里测试失败的密钥 break #print(ans_keys) return ans_keys strmi='F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923C\ AB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF\ 5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84C\ C931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D96\ 3FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47E\ FD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5\ 923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA\ 36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63C\ ED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A8\ 5A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250\ C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794' arr=[]#密文,每个元素为字符的ascii码 for x inrange(0,len(strmi),2): arr.append(int(strmi[x:2+x],16)) for keylen inrange(1,14):#枚举密钥的长度1~14 for index inrange(0,keylen):#对密钥里的第index个进行测试 subarr=arr[index::keylen]#每隔keylen长度提取密文的内容,提取出来的内容都被密文的第index个加密 ans_keys=findindexkey(subarr)#找出密钥中第index个的可能的值 print('keylen=',keylen,'index=',index,'keys=',ans_keys) if ans_keys:#如果密钥第index个有可能存在,尝试用密钥的index个去解密文 ch=[] for x in ans_keys: ch.append(chr(x^subarr[0])) print(ch) #运行到这里,观察输出可以发现,密钥长度为7时有解 print('###############') import string deffindindexkey2(subarr):#再造一个函数筛选密钥 test_chars=string.ascii_letters+string.digits+','+'.'+' '#将检查的字符改为英文+数字+逗号+句号+空格 #print(test_chars) test_keys=[]#用于测试密钥 ans_keys=[]#用于结果的返回 for x inrange(0x00,0xFF):# 枚举密钥里所有的值 test_keys.append(x) ans_keys.append(x) for i in test_keys:#对于0x00~0xFF里的每一个数i和substr里的每个值s异或 for s in subarr: ifchr(s^i) notin test_chars:#用i解密s,如果解密后不是英文、数字、逗号、句号、空格,说明i不是密钥 ans_keys.remove(i)#去掉ans_keys里测试失败的密钥 break #print(ans_keys) return ans_keys vigenerekeys=[]#维基尼尔密码的密钥 for index inrange(0,7):#已经知道密钥长度是7 subarr=arr[index::7] vigenerekeys.append(findindexkey2(subarr)) print(vigenerekeys)#输出的是[[186], [31], [145], [178], [83], [205], [62]]. print("#########") ming='' for i inrange(0,len(arr)): ming=ming+chr(arr[i]^vigenerekeys[i%7][0]) print(ming)