密码学实验报告
第一次实验
任务1:破解一个Many Time Pad加密
思路:可以看注释~
(运行结果也一并放到代码里了,Shell部分就是)
__reference = 'https://github.com/Morrandir/Crypto001_Week1'
from string import ascii_letters
TARGET = '32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904'
#使用TARGET与每条之前的MSGS进行异或
#原理:c(a)^c(b) == m(a) ^ m(b) ^ k ^ k == m(a) ^ m(b)
#以及空格和ascii中大小写字母异或的结果为反转其大小写
potential_str = ' ' + ascii_letters #有意义的字符
MSGS = (
'315c4eeaa8b5f8aaf9174145bf43e1784b8fa00dc71d885a804e5ee9fa40b16349c146fb778cdf2d3aff021dfff5b403b510d0d0455468aeb98622b137dae857553ccd8883a7bc37520e06e515d22c954eba5025b8cc57ee59418ce7dc6bc41556bdb36bbca3e8774301fbcaa3b83b220809560987815f65286764703de0f3d524400a19b159610b11ef3e',
'234c02ecbbfbafa3ed18510abd11fa724fcda2018a1a8342cf064bbde548b12b07df44ba7191d9606ef4081ffde5ad46a5069d9f7f543bedb9c861bf29c7e205132eda9382b0bc2c5c4b45f919cf3a9f1cb74151f6d551f4480c82b2cb24cc5b028aa76eb7b4ab24171ab3cdadb8356f',
'32510ba9a7b2bba9b8005d43a304b5714cc0bb0c8a34884dd91304b8ad40b62b07df44ba6e9d8a2368e51d04e0e7b207b70b9b8261112bacb6c866a232dfe257527dc29398f5f3251a0d47e503c66e935de81230b59b7afb5f41afa8d661cb',
'32510ba9aab2a8a4fd06414fb517b5605cc0aa0dc91a8908c2064ba8ad5ea06a029056f47a8ad3306ef5021eafe1ac01a81197847a5c68a1b78769a37bc8f4575432c198ccb4ef63590256e305cd3a9544ee4160ead45aef520489e7da7d835402bca670bda8eb775200b8dabbba246b130f040d8ec6447e2c767f3d30ed81ea2e4c1404e1315a1010e7229be6636aaa',
'3f561ba9adb4b6ebec54424ba317b564418fac0dd35f8c08d31a1fe9e24fe56808c213f17c81d9607cee021dafe1e001b21ade877a5e68bea88d61b93ac5ee0d562e8e9582f5ef375f0a4ae20ed86e935de81230b59b73fb4302cd95d770c65b40aaa065f2a5e33a5a0bb5dcaba43722130f042f8ec85b7c2070',
'32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd2061bbde24eb76a19d84aba34d8de287be84d07e7e9a30ee714979c7e1123a8bd9822a33ecaf512472e8e8f8db3f9635c1949e640c621854eba0d79eccf52ff111284b4cc61d11902aebc66f2b2e436434eacc0aba938220b084800c2ca4e693522643573b2c4ce35050b0cf774201f0fe52ac9f26d71b6cf61a711cc229f77ace7aa88a2f19983122b11be87a59c355d25f8e4',
'32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd90f1fa6ea5ba47b01c909ba7696cf606ef40c04afe1ac0aa8148dd066592ded9f8774b529c7ea125d298e8883f5e9305f4b44f915cb2bd05af51373fd9b4af511039fa2d96f83414aaaf261bda2e97b170fb5cce2a53e675c154c0d9681596934777e2275b381ce2e40582afe67650b13e72287ff2270abcf73bb028932836fbdecfecee0a3b894473c1bbeb6b4913a536ce4f9b13f1efff71ea313c8661dd9a4ce',
'315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d943ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e9417df7c95bba410e9aa2ca24c5474da2f276baa3ac325918b2daada43d6712150441c2e04f6565517f317da9d3',
'271946f9bbb2aeadec111841a81abc300ecaa01bd8069d5cc91005e9fe4aad6e04d513e96d99de2569bc5e50eeeca709b50a8a987f4264edb6896fb537d0a716132ddc938fb0f836480e06ed0fcd6e9759f40462f9cf57f4564186a2c1778f1543efa270bda5e933421cbe88a4a52222190f471e9bd15f652b653b7071aec59a2705081ffe72651d08f822c9ed6d76e48b63ab15d0208573a7eef027',
'466d06ece998b7a2fb1d464fed2ced7641ddaa3cc31c9941cf110abbf409ed39598005b3399ccfafb61d0315fca0a314be138a9f32503bedac8067f03adbf3575c3b8edc9ba7f537530541ab0f9f3cd04ff50d66f1d559ba520e89a2cb2a83'
)
CRACK = 'The secret message is: When using a stream cipher, never use the key more than once'
#根据异或结果,我们可以推断出,结果大概为:
#The secuet message is Whtn usinw a stream cipher never use the key more than once
#因为密钥为一串有意义的字符,我们可以推断出,其真正含义大概为:
#The secret message is When using a stream cipher never use the key more than once
def strxor(a, b): # xor two strings of different lengths,因为源代码给的是py2的,这里修改了下改成了py3的
if len(a) > len(b):
return "".join([chr(x ^ y) for (x, y) in zip(a[:len(b)], b)])
else:
return "".join([chr(ord(x) ^ y) for (x, y) in zip(a, b[:len(a)])])
for MSG in MSGS:
result = strxor(bytes.fromhex(MSG), bytes.fromhex(TARGET))
for j in range(0, len(bytes.fromhex(TARGET))):
if result[j] in potential_str:
print(result[j],end='')
else:
print('*',end='')
print('')
key = strxor(CRACK, bytes.fromhex(TARGET))
for MSG in MSGS:
result = strxor(key, bytes.fromhex(MSG))
print(result)
keyint = ''
for i in range(0,len(key)):
keyint+=str(ord(key[i]))
Eighth = strxor(key,bytes.fromhex(MSGS[7]))
print(f"key={keyint}\n8th message={Eighth}")
'''
shell:
**EC**C***T**S***EN**XE*HT******GQ*A****A*O********N**E*A*S*L**EF***O*O**ET***B**CT
***E*E****DM******L*S*N***NT***N*O*****E**E****E*IC****RAU**R*******N*O*******T*NNE
********E*H***S***U*SqE****QU**N*O****R***P******DE**V**NU**I**EAK**TM**EF*********
**********T***S***D***Dw**NAU******N******O*I*****I***E*O******EG******R*I****T***E
*******U*TW***S**EB***Aw******I**RAK***E**O*I*H**U****E*P***A***E*E*NM***A*********
***R*E***TT**S****SI*******T*****H***T**********R*I**V**E*S*E***T*E*A**R*R**A*O**C*
***R*E***TT**S****SI*******O*****Y*****E**A*I*****SN***Rg***R***N*E*OM********EO***
**EC**C*******S***N*SMH***NT**I**I****R***A***H***AN****GU**TT******TM********U***E
*HMP**********ZAG*N**CP**********EAS*****M*C*****ET***IRN***L*H*****C****ET********
t**ES*****S*E*****D**YT****R*SA*W*W*S*****N**P****T*E**RT**EA**EO*EYW****N*H*NRO***
We can factor the number 15 with quantum computers. We can also factor the number 1
Euler would probably enjoy that now his theorem becomes a corner stone of crypto -
The nice thing about Keeyloq is now we cryptographers can drive a lot of fancy cars
The ciphertext produced by a weak encryption algorithm looks as good as ciphertext
You don't want to buy a set of car keys from a guy who specializes in stealing cars
There are two types of cryptography - that which will keep secrets safe from your l
There are two types of cyptography: one that allows the Government to use brute for
We can see the point where the chip is unhappy if a wrong bit is sent and consumes
A (private-key) encryption scheme states 3 algorithms, namely a procedure for gene
The Concise OxfordDictionary (2006) deï¬nes crypto as the art of writing o r sol
key=1025711013720121921620415211653422059914916461752061201701272374016012710720114141197111051765115425248170642615610911214312819210219999254240184972205216232220891169135119519317425223621315667581073813996191782406015497
8th message=We can see the point where the chip is unhappy if a wrong bit is sent and consumes
'''
任务2:破解维吉尼亚密码,和任务3思路很相似
思路:写在代码注释里~(还注释掉了一部分另一种思路的做法)
import string
import re
cipherlist = []
testkeys = []
for i in range(0x00,0xff):
testkeys.append(i)
#加入所有可能密钥
potentialchars = test_chars = string.ascii_letters + string.digits + ',' + '.' + ' '
re__ = '[A-Za-z0-9,. ]'
def findindexkey(arr):
global testchars
global testkeys
anskeys = list(testkeys)#不要使用anskeys =testkeys,python复习了属于是
for i in testkeys:
for s in arr:
if chr(s ^ i) not in potentialchars:
anskeys.remove(i)
break
#if re.search(re__,str(chr(s ^ i))):
# pass
#else:
# anskeys.remove(i)
# break
#使用正则表达式的实现方法
return anskeys
#找出所有可能密钥,通过删除实现
text = 'F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923CAB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84CC931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D963FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47EFD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63CED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A85A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794'
for i in range(0,len(text),2):
cipherlist.append(int(text[i:i+2],16))
for keylenth in range(1,12):
for index in range(0,keylenth):
modedlist = cipherlist[index::keylenth]
anskeys = findindexkey(modedlist)
#print(f"anskeys={anskeys},index is {index},and at this time the len is {keylenth}")
#此时发现只有7的时候有anskeys结果出现,结果为
'''
anskeys=[186],index is 0,and at this time the len is 7
anskeys=[31],index is 1,and at this time the len is 7
anskeys=[145],index is 2,and at this time the len is 7
anskeys=[178],index is 3,and at this time the len is 7
anskeys=[83],index is 4,and at this time the len is 7
anskeys=[205],index is 5,and at this time the len is 7
anskeys=[62],index is 6,and at this time the len is 7
'''
key = [186,31,145,178,83,205,62]
origin = ''
for i in range(0,len(cipherlist)):
origin = origin + chr(cipherlist[i] ^ key[i%7])
print(origin)
运行结果:

任务3:给出一串用维吉尼亚密码加密后的密文,要求使用重合指数法获得明文。
http://www.cryptopals.com/sets/1
思路:先求密钥长度,看密钥可能的长度是多少,然后分片分成每个段的[0],[1]…的位置,依次进行试验,使得每个子段进行维吉尼亚解密后的结果处于正常英文的ASCII序号列表里,更好点的还可以通过进行字母出现频率判断来进一步缩小答案范围,最终范围只有一个。
import base64
import binascii
cipherbase64 = b'HUIfTQsPAh9PE048GmllH0kcDk4TAQsHThsBFkU2AB4BSWQgVB0\dQzNTTmVS\
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'
cipher = base64.b64decode(cipherbase64)
cipher64 = binascii.b2a_hex(base64.b64decode(cipherbase64))
cipherlist = []
for i in range(0,len(cipher64),2):
cipherlist.append(int(cipher64[i:i+2],16))
#print(cipherlist)
#cipherlist是一个list,里面每项为字符ASCII的int值
dict1 = {}
def HammingDistance(list1,list2):
string = ''
for i in range(0,len(list1)):
a = bin(list1[i]^list2[i])
string+=a[2:]
return string.count('1')
def hamdistenceaverage(mlist):
count = 0
for j in mlist:
for k in mlist:
if j != k:
count += HammingDistance(j,k) / i
return count
for i in range(2,41):
firstm = cipherlist[0:i]
secondm = cipherlist[i:2*i]
thirdm = cipherlist[2*i:3*i]
fouthm = cipherlist[3*i:4*i]
mlist = [firstm,secondm,thirdm,fouthm]
count = hamdistenceaverage(mlist)
dict1[f'{i}'] = count
#此部分用于找KEYSIZE
print(dict1)
list1= sorted(dict1.items(),key=lambda x:x[1])
print(list1)
OptionalKEYSIZE = [29,5,2,24]
'''
[('29', 32.96551724137932), ('5', 34.8), ('2', 36.0), ('24', 36.25), ('7', 36.85714285714286), ('6', 37.0), ('19', 37.1578947368421), ('20', 37.2),
('3', 37.333333333333336), ('8', 37.5), ('28', 37.85714285714286), ('30', 38.13333333333333), ('34', 38.1764705882353), ('9', 38.22222222222222), ('39', 38.35897435897436), ('10', 38.4), ('17', 38.47058823529411), ('16', 38.5), ('40', 38.6), ('18', 39.0), ('26', 39.0), ('37', 39.027027027027025), ('32', 39.125), ('23', 39.130434782608695), ('38', 39.21052631578947), ('15', 39.33333333333333), ('33', 39.45454545454546), ('21', 39.61904761904762), ('35', 39.65714285714286), ('31', 39.74193548387096), ('25', 39.76), ('13', 39.84615384615385), ('4', 40.0), ('14', 40.142857142857146), ('27', 40.148148148148145), ('22', 40.54545454545455), ('36', 40.611111111111114), ('11', 41.09090909090909), ('12', 41.5)]
'''
def singlekey_xor(cipher):
'''get the most probable key byte
cipher: encrypted by the same byte of key(bytes)
return: the byte of key(bytes)'''
result = -1
for i in range(256):
tempmsg = bytes([i ^ x for x in cipher])
#tempresult = score(tempmsg.decode('latin1'))
tempresult = ChiSquared(tempmsg.decode('latin1') , 1)
if tempresult > result:#比较如果score更大则几率更大
result = tempresult
key = bytes([i])
return key
def break_key_reuse_xor(msg):#显示所有可用的keysize对应密文,词典形式排序
'''msg: bytes
return a dic {keysize : key},keysize is int, key is bytes'''
key_size = OptionalKEYSIZE
key = []
for i in key_size:
tempkey = b''
for j in range(i):
tempkey += singlekey_xor(msg[j::i])
key.append(tempkey)
return key
def ChiSquared(msg , mod):#重要!使用这个函数进行score排序,不然返回的将是一大堆列表,都可以
'''Chi-squared Statistic
msg: str
mod: 1 is simply plus, 0 is multiple'''
standard_letter_frequency = { # From https://en.wikipedia.org/wiki/Letter_frequency.
'e': 0.12702,'t': 0.09056,'a': 0.08167,'o': 0.07507,'i': 0.06966,'n': 0.06749,
's': 0.06327,'h': 0.06094,'r': 0.05987,'d': 0.04253,'l': 0.04025,'c': 0.02782,
'u': 0.02758,'m': 0.02406,'w': 0.02360,'f': 0.02228,'g': 0.02015,'y': 0.01974,
'p': 0.01929,'b': 0.01492,'v': 0.00978,'k': 0.00772,'j': 0.00153,'x': 0.00150,
'q': 0.00095,'z': 0.00074,' ': 0.25000}
score = 0
msg = msg.lower()
if mod:
for i in msg:
if i in standard_letter_frequency:
score += standard_letter_frequency[i]
else:
msg = msg.lower()
for i in standard_letter_frequency:
temp = standard_letter_frequency[i] * len(msg)
score += (msg.count(i)- temp)**2 / temp
return score
#均以bytes类型,防止串类型做不出来
XOR = lambda s1 , s2 : bytes([x1 ^ x2 for x1 , x2 in zip(s1 , s2)])
key = break_key_reuse_xor(cipher)
print(key)#可以看出key[0]有意义其余无意义
key = key[0]*len(cipher)
print(XOR(key , cipher))
运行结果:

任务4:用户输入密码所使用的按键,告诉你密码的长度和密码SHA1之后的值,让你求密码的值。
MTC3 Cracking SHA1- HashedPasswords
https://www.mysterytwisterc3.org/en/challenges/level-2/cracking-sha1-hashed-passwords
思想:就是暴力枚举,看哪个算出来SHA1和目标SHA1一样就行了
#coding:utf-8
import hashlib
import itertools
import datetime
hash1="67ae1a64661ac8b4494666f58c4822408dd0a3e4"
str1="QqWw%58(=0Ii*+nN"
str2=[['Q', 'q'],[ 'W', 'w'],[ '%', '5'], ['8', '('],[ '=', '0'], ['I', 'i'], ['*', '+'], ['n', 'N']]
def sha_encrypt(str):
hash = hashlib.sha1()
hash.update(str.encode('utf-8'))
encrypts = hash.hexdigest()
return encrypts
st3="0"*8
str4=""
str3=list(st3)
starttime = datetime.datetime.now()
for a in range(0,2):
str3[0]=str2[0][a]
for b in range(0,2):
str3[1]=str2[1][b]
for c in range(0,2):
str3[2]=str2[2][c]
for d in range(0,2):
str3[3] = str2[3][d]
for e in range(0,2):
str3[4] = str2[4][e]
for f in range(0,2):
str3[5] = str2[5][f]
for g in range(0,2):
str3[6] = str2[6][g]
for h in range(0,2):
str3[7] = str2[7][h]
newS="".join(str3)
for i in itertools.permutations(newS, 8):
str4 = sha_encrypt("".join(i))
if str4==hash1:
print(("".join(i)))
endtime = datetime.datetime.now()
print(((endtime - starttime).seconds))
运行结果:

第二次实验
任务1:根据错误的Padding获得信息
PA2 option
AES decrypt a challenge cipher text generated using AES in CBC-mode with PKCS#5 padding.
connect(('128.8.130.16', 49101)))
思路:CBC 模式是一种分组链接模式,目的是为了使原本独立的分组密码加密过程形成迭代,使每次加密的结果影响到下一次加密。初始化向量 IV 与第一组明文(分组 1)异或后,再经过运算得到的结果作为新的 IV,用于下一分组(分组 2),不断迭代下去。
而在实际运用中,明文的长度并不会刚好为 16 字节的倍数,这时,分组的最后一块的长度必然小于 16 字节。这时,就需要使用填充(padding)。题目给的是 PKCS #7 填充方式,规则是需要填充 n 个字节,那么这 n 个填充位就填上 n 的 16 进制数。如 缺少 1 位(1 个 Padding), 填充位为: 0x01 缺少 5 位(5 个 Padding), 填充位为: 0x05 0x05 0x05 0x05 0x05
当我们提交 IV+密文到服务端时,服务端便会按照字符串的第一组为 IV,之后为密文进行解密。如此题中: IV: 9F0B13944841A832B2421B9EAF6D9836 C_Block1:813EC9D944A5C8347A7CA69AA34D8DC0 C_Block2:DF70E343C4000A2AE35874CE75E64C31
解密后,若最后的 Padding 不正确(值和数量不一致),则解密程序往往会抛出异常(Padding Error)。在这题里就是返回 0。而利用此回显,我们就可以判断出 Paddig 是否正确

可以看到,padding 为 0x17,却只填充了一位,那么这种情况下,服务端就会返回 0。 接下来要利用选择密文攻击的思想,不断调整,修正 IV。来对 IValue 进行猜测。不断地调整 IV 的值,目的是解密后,最后一个字节的值为正确的 Padding。第一次进行穷举时,是 0×01。因为 IValue 是固定的(未知),所以从 0×00~0xFF 之间,只可能有一个 IV 的值与 IValue 的最后一个字节进行 XOR 后,结果是 0×01。 在 CBC 模式下进行解密时,第一组密文还原成明文时需要先用密钥解密得到一个中间结果(记为 IVALUE),然后将 IVALUE 与 IV 进行异或得到明文 而我们通过穷举 IV,利用服务端检测 padding 是否正确的返回值来判断 IV 是否正确,一旦正确,就立即可以通过 IV 异或 padding 来求得 IVALUE 值。
参考资料:https://www.tr0y.wang/2017/10/06/Crypto1/
友情提示:下面这个是python2,因为python3对于某些字符的识别和python2差别比较大,python3进行识别总是报错,最终无奈选择了python2
# -*- coding: cp936 -*-
from oracle import *
from Crypto.Util import strxor
import re
C = '9F0B13944841A832B2421B9EAF6D9836813EC9D944A5C8347A7CA69AA34D8DC0DF70E343C4000A2AE35874CE75E64C31'
BLOCK = 2
div = len(C) / (BLOCK + 1)
C = re.findall('.{' + str(div) + '}', C)
Oracle_Connect()
M = []
IVALUE = []
for b in range(BLOCK): #对 2 组密文分别求解
print '[*] Detecting Block',b+1
IV = C[b]
Ivalue = []
iv = '00000000000000000000000000000000' #初始化 iv
iv = re.findall('.{2}', iv)[::-1]
padding = 1
for l in range(16):
print " [+] Detecting IVALUE's last", l + 1 , 'block'
for ll in range(l):
iv[ll] = hex(int(Ivalue[ll], 16) ^ padding)[2:].zfill(2) #更新 iv
for n in range(256): #遍历 0x00-0xFF
iv[l] = hex(n)[2:].zfill(2)
data = ''.join(iv[::-1]) + C[b + 1]
ctext = [(int(data[i:i + 2], 16)) for i in range(0, len(data), 2)]
rc = Oracle_Send(ctext, 2)
if str(rc) == '1': #Padding 正确时, 记录 Ivalue, 结束爆破
Ivalue += [hex(n ^ padding)[2:].zfill(2)]
break
print ' [-]', ''.join(iv[::-1])
print ' [-]', ''.join(Ivalue[::-1])
padding += 1
Ivalue = ''.join(Ivalue[::-1])
IVALUE += [Ivalue]
#IV 与 Ivalue 异或求密文
m = re.findall('[0-9a-f]+', str(hex(int(IV, 16) ^ int(''.join(Ivalue), 16))))[1].decode('hex')
M += [m]
print '[#] Detecting Block', b + 1 ,'-- Done!'
print '[#]', 'The IValue' + str(b + 1), 'is:', Ivalue
print '[#]', 'The M' + str(b + 1) , 'is:', m
print '-' * 50
Oracle_Disconnect()
print '[!] The Intermediary Value is:', ''.join(IVALUE)
print '[!] The M is:', ''.join(M)
运行结果:

任务2:Crptopals Sets2
Question 2 cryptopals
http://www.cryptopals.com/sets/2
-
Implement PKCS#7 padding
-
Implement CBC mode
-
An ECB/CBC detection oracle4. Byte-at-a-time ECB decryption
(Simple) -
ECB cut-and-paste
-
Byte-at-a-time ECB decryption
(Harder) -
PKCS#7 padding validation8. CBC bit flipping attacks
上传6,7,8的代码截图
6.破解ECB,通过在秘密前面加上特定数量的字节,将秘密的第一个未知字节恰好“顶”到一块的最后一个字节,的到一个加密串。另一个则根据已经求出的秘密前缀将暴力猜测的字节“顶”到同样的位置,保证本字节之前的字节和上一个串是一样的。这样通过比较本块的加密结果是否一样就可以暴出这一字节。
from base64 import b64decode
from typing_extensions import ParamSpecKwargs
from S2C10 import aes_ecb_encrypt
from S2C09 import pkcs7_unpad
from random import randint
from Crypto import Random
from S1C08 import count_aes_ecb_repetitions
from S2C12 import find_block_length, ECBOracle
class HarderECBOracle(ECBOracle):
def __init__(self, secret_padding):
super(HarderECBOracle, self).__init__(secret_padding)
self._random_prefix = Random.new().read(randint(0, 255))
def encrypt(self, data):
"""Encrypts with AES-128-ECB mode, after prepending a fixed (randomly-generated) string
and appending a fixed (given) string to every plaintext.
使用AES-128-ECB模式进行加密
"""
return aes_ecb_encrypt(self._random_prefix + data + self._secret_padding, self._key)
def get_next_byte(prefix_length, block_length, curr_decrypted_message, encryption_oracle):
"""Finds the next byte of the mysterious message that the oracle
is appending to our plaintext.
寻找下一块密文并且将预言加到明文上
"""
# Compute the number of characters that we want to use as input to have the first unknown
# character of the mysterious message at the end of a block
length_to_use = (block_length - prefix_length - (1 + len(curr_decrypted_message))) % block_length
my_input = b'A' * length_to_use
# Compute the number of bytes that we will take from the fake and from the real ciphertexts
# to compare them. We will ignore everything is beyond the byte we are trying to discover.
cracking_length = prefix_length + length_to_use + len(curr_decrypted_message) + 1
# Compute the real ciphertext that the oracle would output with my input
real_ciphertext = encryption_oracle.encrypt(my_input)
# For each possible character
for i in range(256):
# Compute our fake ciphertext, trying to obtain the same as the real ciphertext
fake_ciphertext = encryption_oracle.encrypt(my_input + curr_decrypted_message + bytes([i]))
# If we found a character that, used in our fake input, let us obtain the same ciphertext
if fake_ciphertext[:cracking_length] == real_ciphertext[:cracking_length]:
# Return that character as the next byte of the message
return bytes([i])
# If there was no match (most likely due to padding), return empty byte
return b''
def has_equal_block(ciphertext, block_length):
"""Checks if the given ciphertext contains two consecutive equal blocks
查看给予的密文有没有两个相同的块,这里是一串一串一位一位的算"""
for i in range(0, len(ciphertext) - 1, block_length):
if ciphertext[i:i+block_length] == ciphertext[i+block_length:i+2*block_length]:
return True
return False
def find_prefix_length(encryption_oracle, block_length):
"""Finds the length of the randomly generated prefix that the encryption oracle
adds to every plaintext before encrypting. First, the block where the prefix ends
is searched; then the precise index where the prefix ends is searched.
"""
# To find the index of the block where the prefix ends, we use the oracle to encrypt
# an empty message and a 1 character message. Then we use them to find it as explained below.
ciphertext1 = encryption_oracle.encrypt(b'')
ciphertext2 = encryption_oracle.encrypt(b'a')
# The first block where the two ciphertexts differ will be the block where the
# prefix (which was the same for both the inputs) ended.
prefix_length = 0
for i in range(0, len(ciphertext2), block_length):
if ciphertext1[i:i+block_length] != ciphertext2[i:i+block_length]:
prefix_length = i
break
# Now, to find the precise index where the prefix ended, we will encrypt identical bytes,
# in a number equal to two block_lengths, and we will increase this amount by an incremental
# offset to see when those bytes will be shifted to be autonomous blocks (thus encrypted the same way)
for i in range(block_length):
fake_input = bytes([0] * (2 * block_length + i))
ciphertext = encryption_oracle.encrypt(fake_input)
# If the bytes have shifted enough, we can compute the precise index where the prefix ends
# inside its last block, which is going to be equal to block_length - i
if has_equal_block(ciphertext, block_length):
return prefix_length + block_length - i if i != 0 else prefix_length
raise Exception('The oracle is not using ECB')
def byte_at_a_time_ecb_decryption_harder(encryption_oracle):
"""Performs the byte-at-a-time ECB decryption attack to discover the secret padding used by the oracle."""
# Find the block length
block_length = find_block_length(encryption_oracle)
# To detect if the oracle encrypts with ECB mode, we can encrypt a big enough (more
# than three block sizes) plaintext of identical bytes. If the ciphertext presents
# repeated blocks then we can deduct that it is very likely using ECB.
ciphertext = encryption_oracle.encrypt(bytes([0] * 64))
assert count_aes_ecb_repetitions(ciphertext) > 0
# The number of bytes that we have to decrypt by breaking the encryption oracle
# will be equal to the length of the ciphertext when we encrypt an empty message
# subtracted by the length of the prefix (which we have to find).
prefix_length = find_prefix_length(encryption_oracle, block_length)
mysterious_text_length = len(encryption_oracle.encrypt(b'')) - prefix_length
# At this point we have all the information that we need to crack the ECB
# encryption oracle byte by byte.
secret_padding = b''
for i in range(mysterious_text_length):
secret_padding += get_next_byte(prefix_length, block_length, secret_padding, encryption_oracle)
# Return the complete padding as bytes
return secret_padding
def main():
"""Approach:
1) Find the block_length and the encryption mode (as in S2C12)
2) Find the prefix length
3) Decrypt byte-by-byte the mysterious message (similar to S2C12)
方法:我们通过控制AAAAA*n字符串的长度,慢慢更改A的长度,使得密文中每个字符慢慢暴露出来在另一个块
中,这样就能一次一个字符的进行爆破,大大降低了计算量
"""
secret_padding = b64decode("Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGF"
"pciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IH"
"RvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK")
oracle = HarderECBOracle(secret_padding)
discovered_secret_padding = byte_at_a_time_ecb_decryption_harder(oracle)
# Check if the attack works correctly
print(secret_padding)
print(discovered_secret_padding)
print(pkcs7_unpad(discovered_secret_padding))
assert pkcs7_unpad(discovered_secret_padding) == secret_padding
if __name__ == '__main__':
main()

7.一个简单的Padding Check,这个只是一个中间步骤,其实这个题的目的是要后面用这个Padding Check的函数要调用的
def check_padding(s: bytes):
padding = s[-s[-1]:]
for i in padding:
if not i == len(padding):
return False
return True
print(check_padding(b'ICE ICE BABY\x04\x04\x04\x04'))
print(check_padding(b'ICE ICE BABY\x05\x05\x05\x05'))
print(check_padding(b'ICE ICE BABY\x01\x02\x03\x04'))
print(check_padding(b'ICE ICE BABY'))

8.这是CBC的比特翻转攻击,我们可以通过添加很长的data构造出一个纯没有意义的块,然后异或这一块的某个密文字节,这样下一块的明文字节在解密时就会被篡改
from S2C10 import aes_cbc_encrypt, aes_cbc_decrypt
from Crypto import Random
from Crypto.Cipher import AES
class Oracle:
def __init__(self):
self._key = Random.new().read(AES.key_size[0])
self._iv = Random.new().read(AES.block_size)
self._prefix = "comment1=cooking%20MCs;userdata="
self._suffix = ";comment2=%20like%20a%20pound%20of%20bacon"
def encrypt(self, data):
"""Adds the prefix and the suffix specified in the challenge and encrypts the data with AES-128-CBC"""
data = data.replace(';', '').replace('=', '') # Remove special characters to avoid injection
plaintext = (self._prefix + data + self._suffix).encode()
return aes_cbc_encrypt(plaintext, self._key, self._iv)
def decrypt_and_check_admin(self, ciphertext):
"""Decrypts the string and returns whether the characters ";admin=true;" are in the string"""
data = aes_cbc_decrypt(ciphertext, self._key, self._iv)
return b';admin=true;' in data
def find_block_length(encryption_oracle):
"""Returns the length of a block for the block cipher used by the encryption_oracle.
To find the length of a block, we encrypt increasingly longer plaintexts until the size of the
output ciphertext increases too. When this happens, we can then easily compute the length of a block
as the difference between this new length of the ciphertext and its initial one.
"""
my_text = ''
ciphertext = encryption_oracle(my_text)
initial_len = len(ciphertext)
new_len = initial_len
while new_len == initial_len:
my_text += 'A'
ciphertext = encryption_oracle(my_text)
new_len = len(ciphertext)
return new_len - initial_len
def find_prefix_length(encryption_oracle, block_length):
"""Returns the length of the prefix that the encryption oracle prepends to every plaintext."""
# Encrypt two different ciphertexts
ciphertext_a = encryption_oracle('A')
ciphertext_b = encryption_oracle('B')
# Find their common length
common_len = 0
while ciphertext_a[common_len] == ciphertext_b[common_len]:
common_len += 1
# Make sure that the common length is multiple of the block length
common_len = int(common_len / block_length) * block_length
# Try to add an increasing number of common bytes to the plaintext till they until
# the two ciphertexts will have one extra identical block
for i in range(1, block_length + 1):
ciphertext_a = encryption_oracle('A' * i + 'X')
ciphertext_b = encryption_oracle('A' * i + 'Y')
# If there is one more identical block, it will mean that by adding i bytes
# we made the common input (including prefix) to the same length multiple of
# a block size. Then we can easily get the length of the prefix.
if ciphertext_a[common_len:common_len + block_length] == ciphertext_b[common_len:common_len + block_length]:
return common_len + (block_length - i)
def cbc_bit_flip(encryption_oracle):
"""Performs a CBC bit flipping attack to accomplish admin privileges in the decrypted data."""
# Get the length of a block and the length of the prefix
block_length = find_block_length(encryption_oracle.encrypt)
prefix_length = find_prefix_length(encryption_oracle.encrypt, block_length)
# Compute the number of bytes to add to the prefix to make its length a multiple of block_length
additional_prefix_bytes = (block_length - (prefix_length % block_length)) % block_length
total_prefix_length = prefix_length + additional_prefix_bytes
# Compute the number of bytes to add to the plaintext to make its length a multiple of block length
plaintext = "?admin?true"
#此处输入?admin?true绕过plaintext对于=和?的过滤
additional_plaintext_bytes = (block_length - (len(plaintext) % block_length)) % block_length
# Make the plaintext long one block_length and encrypt it
final_plaintext = additional_plaintext_bytes * '?' + plaintext
ciphertext = encryption_oracle.encrypt(additional_prefix_bytes * '?' + final_plaintext)
# Because XORing a byte with itself produces zero, we can produce the byte that we want
# by changing the bytes of the block before the plaintext
semicolon = ciphertext[total_prefix_length - 11] ^ ord('?') ^ ord(';')
equals = ciphertext[total_prefix_length - 5] ^ ord('?') ^ ord('=')
# 因为plainchar就是?,? ^ plainchar ^ ; = ;就把?变成;了
#同理把?变成;
# Put the pieces of our forged ciphertext together to generate the full ciphertext
forced_ciphertext = ciphertext[:total_prefix_length - 11] + bytes([semicolon]) + \
ciphertext[total_prefix_length - 10: total_prefix_length - 5] + \
bytes([equals]) + ciphertext[total_prefix_length - 4:]
return forced_ciphertext
def main():
encryption_oracle = Oracle()
forced_ciphertext = cbc_bit_flip(encryption_oracle)
# Check if the ciphertext was forced properly
assert encryption_oracle.decrypt_and_check_admin(forced_ciphertext)
if __name__ == '__main__':
main()
第三次实验
大作业
尝试第一届全国高校密码学挑战赛,目的为练习RSA算法常见漏洞的利用。
Alice 正在使用一个使用RSA加密的通信程序。她发了21条信息,但其中有些是相同的,还有些参数不是很合适。现在已经拦截了所有21条信息,告诉你密文的格式是各64bit的n、e、c拼接,明文的格式是64bit的标识符、32bit的序列号、若干个0和64bit的明文,总共521个bit。要求你尽可能多地破解Alice的信息。
- 共模攻击
如果一对消息的模数和明文相同,而指数互质,则可以找到有x,y使得

,则有

如果两个用户的公钥分别为e1和e2,而且两者互质。铭文消息为m,密文分别为:

则当攻击者截获c1和c2后,就可以恢复出明文,使用扩展欧几里得算法求出 的两个整数r和s,由此可得:

对应的代码如下:
import gmpy2
import binascii
import math
# 欧几里得算法
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
if __name__ == "__main__":
N = []
C = []
E = []
for i in range(21):
with open("./RSA大礼包/frame_set/Frame" + str(i), "r") as f:
tmp = f.read()
N.append(tmp[0:256])
E.append(tmp[256:512])
C.append(tmp[512:768])
# 使用公共模数攻击的方法还原出Frame0和Frame4
# Frame0: My secre
# Frame4: My secre
i1 = 0
i2 = 0
for i in range(21):
for j in range(i + 1, 21):
if N[i] == N[j]:
print("寻找到公共模数:" + str((N[i], N[j])))
i1, i2 = i, j
e1 = int(E[i1], 16)
e2 = int(E[i2], 16)
n = int(N[i1], 16)
c1 = int(C[i1], 16)
c2 = int(C[i2], 16)
s = egcd(e1, e2)
s1 = s[1]
s2 = s[2]
# 求模反元素
if s1 < 0:
s1 = -s1
c1 = gmpy2.invert(c1, n)
elif s2 < 0:
s2 = -s2
c2 = gmpy2.invert(c2, n)
m = pow(c1, s1, n) * pow(c2, s2, n) % n
print(m)
print(binascii.a2b_hex(hex(m)[2:]))
plaintext0_and_4 = binascii.a2b_hex(hex(m)[2:])
print(plaintext0_and_4)
根据实验发现此攻击针对Frame0和Frame4有效。
得到的结果为:
Frame0: My secre
Frame4: My secre
2.低加密指数攻击
攻击条件:e特别小,比如e为3
攻击原理:如果若干个消息明文和指数相同,指数和明文个数相同,而且模数互质,则有方程组

成立。可以利用中国剩余定理解出m^e<n1·n2·…·ne,从而可以直接解出明文。
对应的代码如下:
import gmpy2
import math
import binascii
# 欧几里得算法
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def CRT(items):
N = 1
for a, n in items:
N *= n
result = 0
for a, n in items:
m = N // n
d, r, s = egcd(n, m)
if d != 1:
N = N // n
continue
result += a * s * m
return result % N, N
# 低加密指数e == 3
def Be_3():
Brange = [7, 11, 15]
for i in range(3):
c = int(C[Brange[i]], 16)
n = int(NN[Brange[i]], 16)
print("This is frame" + str(i))
for j in range(20):
plain = gmpy2.iroot(gmpy2.mpz(c + j * n), 3)
print("This is test" + str(j))
print(binascii.a2b_hex(hex(plain[0])[2:]))
def low_e_3():
ss = [
{"c": int(C[7], 16), "n": int(NN[7], 16)},
{"c": int(C[11], 16), "n": int(NN[11], 16)},
{"c": int(C[15], 16), "n": int(NN[15], 16)},
]
data = []
for session in ss:
data = data + [(session["c"], session["n"])]
x, y = CRT(data)
#开三次方根
plaintext7_11_15 = gmpy2.iroot(gmpy2.mpz(x), 3)
return binascii.a2b_hex(hex(plaintext7_11_15[0])[2:])
def low_e_5():
ss = [
{"c": int(C[3], 16), "n": int(NN[3], 16)},
{"c": int(C[8], 16), "n": int(NN[8], 16)},
{"c": int(C[12], 16), "n": int(NN[12], 16)},
{"c": int(C[16], 16), "n": int(NN[16], 16)},
{"c": int(C[20], 16), "n": int(NN[20], 16)},
]
data = []
for session in ss:
data = data + [(session["c"], session["n"])]
x, y = CRT(data)
# 直接开五次方根
plaintext3_8_12_16_20 = gmpy2.iroot(gmpy2.mpz(x), 5)
return binascii.a2b_hex(hex(plaintext3_8_12_16_20[0])[2:])
if __name__ == "__main__":
NN = []
C = []
E = []
for i in range(21):
with open("./RSA大礼包/frame_set/Frame" + str(i), "r") as f:
tmp = f.read()
NN.append(tmp[0:256])
E.append(tmp[256:512])
C.append(tmp[512:768])
plaintext3_8_12_16_20 = low_e_5()
print(plaintext3_8_12_16_20)
plaintext7_11_15 = low_e_3()
print(plaintext7_11_15) # low_e_3的是无法破解的
经过分析可以发现:
Frame7, Frame11, Frame15和Frame3, Frame8, Frame12, Frame16, Frame20这两组数分别符合低加密指数攻击的条件。
Frame7、11、15可以直接从上述方式攻击,发现Frame7、11、15并不是由相同明文加密得到的。
对于Frame3、Frame8、Frame2、Frame16、Frame20,由上面的经验,只需要三块就可以获得对应的密文。
但是运行时会发现,7/11/15块是不能使用该方法进行破解的。
但是对于3/8/12/16/20,是可以使用该方法进行破解的。
3.公因数攻击
如果两个不同的模数n1=p·q1,n2=p·q2,其中q相同,那么可以直接求出p=gcd(n1,n2)。毕竟,求gcd的时间复杂度是远小于大数分解的,导致可以分解出模数的因子,进而求得密钥。
Frame1和Frame18由大于1的最大公因数。所以我们可以通过除法运算求的N1和N18的另外一个因数,即求出素数p和q,破译明文。
代码如下:
import gmpy2
import binascii
import math
if __name__ == "__main__":
N = []
C = []
E = []
for i in range(21):
with open("./RSA大礼包/frame_set/Frame" + str(i), "r") as f:
tmp = f.read()
N.append(tmp[0:256])
E.append(tmp[256:512])
C.append(tmp[512:768])
plaintext = []
index = []
for i in range(21):
for j in range(i + 1, 21):
if int(N[i], 16) == int(N[j], 16):
continue
prime = gmpy2.gcd(int(N[i], 16), int(N[j], 16))
if prime != 1:
print((N[i], N[j]))
print((i, j))
index.append(i)
index.append(j)
Pframe = prime
Qframe1 = int(N[index[0]], 16) // Pframe # 通过碰撞因数求得p、q
Qframe18 = int(N[index[1]], 16) // Pframe
print(Pframe)
print(Qframe1, Qframe18)
φframe1 = (Pframe - 1) * (Qframe1 - 1)
φframe18 = (Pframe - 1) * (Qframe18 - 1)
d_of_frame1 = gmpy2.invert(int(E[index[0]], 16), φframe1)
d_of_frame18 = gmpy2.invert(int(E[index[1]], 16), φframe18)
Pframe1 = gmpy2.powmod(
int(C[index[0]], 16), d_of_frame1, int(N[index[0]], 16)
)
Pframe18 = gmpy2.powmod(
int(C[index[1]], 16), d_of_frame18, int(N[index[1]], 16)
)
PLAIN1 = binascii.a2b_hex(hex(Pframe1)[2:])
PLAIN18 = binascii.a2b_hex(hex(Pframe18)[2:])
plaintext.append(PLAIN1)
plaintext.append(PLAIN18)
print(plaintext)
可以得到结果:
Frame1: .Imagin
Frame18: m A to B
4.费马分解法
要求:|p-q|较小
首先:

费马分解法基于:若N=pq,其中p<=q而且都是奇数,然后令x=(1/2)(p+q),y=(1/2)(q-p),则可以找到N=x2-y2=(x+y)(x-y)或者y2=x2-N。
设N=pq,其中Δ=p-q则当Δ<N1/4,费马分解法就可以有效地分解N。
爆破后可以发现之后只有Frame10中的模数在短时间内可以使用此方法进行分解。
代码如下:
import gmpy2
import binascii
import math
import time
def pq(n):
B = math.factorial(2 ** 14)
u = 0
v = 0
i = 0
u0 = gmpy2.iroot(n, 2)[0] + 1
timeStart = time.time()
while i <= (B - 1):
u = (u0 + i) * (u0 + i) - n
if gmpy2.is_square(u):
v = gmpy2.isqrt(u)
break
i = i + 1
timeEnd = time.time()
if timeEnd-timeStart>1:
return 0 #超时
p = u0 + i + v
return p
def fermat_resolve():
ilist = []
for i in range(10, 16):
timeStart = time.time()
N = int(NN[i], 16)
p = pq(N)
timeEnd = time.time()
print(f"i={i},p={p},time={timeEnd-timeStart}")
if p != 0:
ilist.append(i)
print(f"Fermat攻击尝试结束。{ilist}是可以进行攻击的。")
if __name__ == "__main__":
NN = []
C = []
E = []
for i in range(21):
with open("./RSA大礼包/frame_set/Frame" + str(i), "r") as f:
tmp = f.read()
NN.append(tmp[0:256])
E.append(tmp[256:512])
C.append(tmp[512:768])
fermat_resolve()
#底下的代码是根据上面解出的结果再写的
p = 9686924917554805418937638872796017160525664579857640590160320300805115443578184985934338583303180178582009591634321755204008394655858254980766008932978699
n = int(NN[10], 16)
c = int(C[10], 16)
e = int(E[10], 16)
q = n // p
φframe10 = (p - 1) * (q - 1)
d = gmpy2.invert(e, φframe10)
m = gmpy2.powmod(c, d, n)
Plain = binascii.a2b_hex(hex(m)[2:])
print(Plain)
5.Pollard p-1分解法
原理:RSA 加密算法中,素数 p 和 q 应该满足:p 1 和 q 1 至少有一个素因子大于 1020,否则,要分解的整数 N 有一个素因数 p ,且(p-1)有小的素因数时就可以 利用 Pollardp1 分解法有效分解模数 N ,从而得到 p 。
选取一个数B,使得k=B!,如果B足够大,那么就有(p-1)|k,则由于2^(p-1) ≡ 1 (mod p)可知p|(2^k - 1),又有p|n,所以p|a,a ≡ 2^k - 1 mod n。将次数与n取gcd即可分解模数。
根据以上原理构造Pollardp1 分解函数:
分解:
import math
import gmpy2
import binascii
def pollardPMinus1(n):
B = 2 ** 20
a = 2
for i in range(2*2, (B + 1)*2 , 2):
a = pow(a, i, n)
d = gmpy2.gcd(a - 1, n)
if (d >= 2) and (d <= (n - 1)):
q = n // d
n = q * d
return d
if __name__ == "__main__":
NN = []
C = []
E = []
for i in range(21):
with open("./RSA大礼包/frame_set/Frame" + str(i), "r") as f:
tmp = f.read()
NN.append(tmp[0:256])
E.append(tmp[256:512])
C.append(tmp[512:768])
index_list = [2, 6, 19]
plainTextList = []
for i in range(3):
N = int(NN[index_list[i]], 16)
c = int(C[index_list[i]], 16)
e = int(E[index_list[i]], 16)
p = pollardPMinus1(N)
print("p of " + str(index_list[i]) + " is : " + str(p))
q = N // p
φofFrame = (p - 1) * (q - 1)
d = gmpy2.invert(e, φofFrame)
m = gmpy2.powmod(c, d, N)
plainTextList.append(binascii.a2b_hex(hex(m)[2:]))
print(plainTextList)
print("Pollard finished!")
可以发现Frame2、6、19可以使用该方法进行分解。
处理结果如下:
Frame2: That is
Frame6: “Logic
Frame19: instein.
现在已经破解的部分:

根据以上得信息可以大概得到明文为My secret is a f ---------------- instein. That is "Logic will get --------- m A to B. Imagin -------
其中,m A to B这样的句式并不多见,考虑到to,我们可以猜测为from A to B。
通过搜集资料,可以找到爱因斯坦的一句名言Logic will get you from A to B. Imagination will take you everywhere. 这句名言很明显符合要求。
通过进行加密验证和暴力破解遍历,验证成功。我们可以得到5、9、12、13、14、15块得内容。考虑到instein.这样的单词很为少见,同时爱因斯坦名字叫做"Albert Einstein"自然可以考虑4的明文为"Albert E",通过验证也成功。
现在已知的明文为:My secret is a f --------- Albert Einstein. That is "Logic will get you from A to B. Imagination will take you everywhere."
根据已知的明文,我们可以大胆猜测,这个f可能是 “famous” 或者 “from”,如果是"famous"则符合语境的短语有:“famous quotes”, “famous saying”, “famous nots"等,通过继续用加密密钥进行猜测明文攻击,验证了分块3为"amous sa”,所以4很明显为"ying"
My secret is a famous saying --- Albert Einstein. That is "Logic will get you from A to B. Imagination will take you everywhere."
根据这样的一个语法结构和剩余位置只有4个(还必须包括两个空格),可以继续猜测剩下的单词为of,进行加密验证,猜想正确。
所以全部明文为My secret is a famous saying of Albert Einstein. That is "Logic will get you from A to B. Imagination will take you everywhere."
这样一想,还真是"Imagination will take you everywhere."啊。
这篇博客详细记录了密码学实验的过程,包括Many Time Pad、维吉尼亚密码的破解,以及两次实验中的各种密码攻击策略。在第三次实验中,作者深入探讨了RSA算法的共模攻击、低加密指数攻击、公因数攻击和费马分解法,成功破解了多个信息片段,并结合已知明文推测出完整的明文,展示了密码学攻击的实践应用。
2604

被折叠的 条评论
为什么被折叠?



