密码学大作业
第一次大作业(0930)
题目1
题目内容
题目大致意思就是给你多个使用同一密钥进行流密码加密的密文,解密出来给定密文的信息。
思路
由ASCII码的性质可以知道,空格与字母异或会使字母大小写翻转,字母异或得到的非字母。故将密文分别两两异或,通过结果判断不同明文中可能存在空格的位置,将对应位置上的密文和空格异或,得到对应位置密钥,并最终对目标密文进行解密。
先前已经解出了最后一组明文,只需要将明文与对应的密文16进制异或,得到密钥再与给定的第八组明文进行异或即可。
源码
plaintext = "The secret message is: When using a stream cipher, never use the key more than once"
ciphertext = "32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904"
ciphertext8 = "315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d943ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e9417df7c95bba410e9aa2ca24c5474da2f276baa3ac325918b2daada43d6712150441c2e04f6565517f317da9d3"
def charListToHexList(charList):
plainHexList = []
for i in charList:
plainHexList.append(hex(ord(i)))
return plainHexList
if __name__ == '__main__':
keyList = []
plainList8 = []
plaintext8 = ""
plainList = list(plaintext)
plainHexList = (charListToHexList(plainList))
cipherHex = bytes.fromhex(ciphertext)
cipherHex8 = bytes.fromhex(ciphertext8)
for i in range(0, len(plainHexList)):
keyList.append(cipherHex[i] ^ int(plainHexList[i], 16))
plainList8.append(keyList[i] ^ cipherHex8[i])
for i in range(0, len(plainList8)):
plaintext8 += chr(plainList8[i])
print("密钥(10进制ASCII码表示):", keyList)
print("第八组明文:" + plaintext8)
结果
密钥(10进制ASCII码表示): [102, 57, 110, 137, 201, 219, 216, 204, 152, 116, 53, 42, 205, 99, 149, 16, 46, 175, 206, 120, 170, 127, 237, 40, 160, 127, 107, 201, 141, 41, 197, 11, 105, 176, 51, 154, 25, 248, 170, 64, 26, 156, 109, 112, 143, 128, 192, 102, 199, 99, 254, 240, 18, 49, 72, 205, 216, 232, 2, 208, 91, 169, 135, 119, 51, 93, 174, 252, 236, 213, 156, 67, 58, 107, 38, 139, 96, 191, 78, 240, 60, 154, 97]
第八组明文:We can see the point where the chip is unhappy if a wrong bit is sent and consumes
题目2
题目内容
给定类维吉尼亚密码的流密码加密密文(不是相加模26,而是异或),解密出明文信息。
思路
设密钥的长度为x,密文为c,则c[i]、c[i+x]、c[i+2x]……都是明文与同一字符异或得到,所以我们可以猜测密钥长度,遍历密钥的每一个字符的每一个ASCII码可能值,直到对应的明文都是字母或标点为止,这样就能得到密钥和明文。
源码
import string
# text
ciphertext = "F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923CAB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84CC931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D963FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47EFD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63CED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A85A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794"
# 将16进制长串转为10进制ASCII码
def ciphertextToList(ciphertext):
cipherList = []
for i in range(0, len(ciphertext), 2):
cipherList.append(int(ciphertext[i:2 + i], 16))
return cipherList
# 选取密钥
def keySelect(subCipher):
charList = string.ascii_letters + " " + "," + "." # 这仨不能用string.punctuation,问就是乱码
KeyTest = []
KeyFin = []
for i in range(0x00, 0xFF):
KeyTest.append(i)
KeyFin.append(i)
for i in KeyTest:
for j in subCipher:
if chr(i ^ j) not in charList:
KeyFin.remove(i)
break
return KeyFin
# main
def Decryption(cipherList):
KeyFin = []
KeyLenFin = 0
for keyLen in range(1, 14):
alternateKey = []
for i in range(0, keyLen):
subArray = cipherList[i::keyLen]
keyAlter = keySelect(subArray)
if not keyAlter:
break
else:
alternateKey.insert(i, keyAlter)
if alternateKey:
KeyFin = alternateKey
KeyLenFin = keyLen
print("长度:", KeyLenFin)
print("密钥(十进制ASCII码):", KeyFin)
plaintext = ''
for i in range(0, len(cipherList)):
plaintext = plaintext + chr(cipherList[i] ^ KeyFin[i % len(KeyFin)][0])
print('明文:', plaintext)
return 0
cipherList = ciphertextToList(ciphertext)
Decryption(cipherList)
结果
长度: 7
密钥(十进制ASCII码): [[186], [31], [145], [178], [83], [205], [62]]
明文: 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.
题目3
题目内容
Break repeating-key XOR - The Cryptopals Crypto Challenges
思路
- 猜测密钥长度KeySize,如从2-30
- 计算两个字符串之间的汉明距离
- 每次尝试KeySize时,从文件开头截取四个块,两两组合计算其汉明距离
- 选取3个最小的汉明距离所对应的KeySize作为密钥长度备选
- 知道KeySize大小后,将密文按KeySize大小切片,每片取第一个、第二个……组成新的块
- 对新的块而言,解密思路类似作业第2题
- 将每个单字符异或的Key拼接起来即为所要的最终的Key,将明文拼接即为完整明文
源码
import base64
from functools import partial
import itertools
letterFreqs = \
{'a': 0.0651738, 'b': 0.0124248, 'c': 0.0217339,
'd': 0.0349835, 'e': 0.1041442, 'f': 0.0197881,
'g': 0.0158610, 'h': 0.0492888, 'i': 0.0558094,
'j': 0.0009033, 'k': 0.0050529, 'l': 0.0331490,
'm': 0.0202124, 'n': 0.0564513, 'o': 0.0596302,
'p': 0.0137645, 'q': 0.0008606, 'r': 0.0497563,
's': 0.0515760, 't': 0.0729357, 'u': 0.0225134,
'v': 0.0082903, 'w': 0.0171272, 'x': 0.0013692,
'y': 0.0145984, 'z': 0.0007836, ' ': 0.1918182} # 字母频率
def GetScore(ib): # 计算输入文本的分值
score = 0
for b in ib:
score += letterFreqs.get(chr(b).lower(), 0)
return score
def SingleXOR(ib, KeyValue): # 对每个字符和Key异或
output = b""
for ch in ib:
output += bytes([ch ^ KeyValue])
return output
def SingleXorBreak(ciphertext): # 破解单字符异或
choices = []
for KeyChoice in range(256):
PlaintextChoice = SingleXOR(ciphertext, KeyChoice)
ScoreChoice = GetScore(PlaintextChoice)
result = {
"key": KeyChoice,
"score": ScoreChoice,
"plaintext": PlaintextChoice
}
choices.append(result)
return sorted(choices, key=lambda c: c['score'], reverse=True)[0]
def HammingDistance(bstr1, bstr2): # 汉明距离
distance = 0
for b1, b2 in zip(bstr1, bstr2):
dis = b1 ^ b2
distance += sum([1 for bit in bin(dis) if bit == "1"])
return distance
def KeyXOR(plaintext, Key):
ciphertext = b""
i = 0
for b in plaintext:
ciphertext += bytes([b ^ Key[i]])
i = i + 1 if i < len(Key) - 1 else 0
return ciphertext
def KeyBreak(text):
NorDistances = {}
for KeySize in range(2, 30):
Slices = [text[i:i + KeySize] for i in range(0, len(text), KeySize)][:4]
TempDistance = 0
SlicePair = itertools.combinations(Slices, 2)
for (a, b) in SlicePair:
TempDistance += HammingDistance(a, b)
TempDistance /= 6
NorDistance = TempDistance / KeySize # 对汉明距离进行归一化
NorDistances[KeySize] = NorDistance
PossibleKeySize = sorted(NorDistances, key=NorDistances.get)[:3] # 取最小的3个汉明距离所对应的KeySize
PossiblePlaintext = []
for i in PossibleKeySize:
TempKey = b""
for j in range(i):
block = b""
for k in range(j, len(text), i):
block += bytes([text[k]])
TempKey += bytes([SingleXorBreak(block)["key"]])
PossiblePlaintext.append((KeyXOR(text, TempKey), TempKey))
return max(PossiblePlaintext, key=lambda k: GetScore(k[0])) # 选取分数最高,即最有可能是正确明文的那一组
if __name__ == '__main__':
with open("H3.txt") as fp:
text = base64.b64decode(fp.read())
result = KeyBreak(text)
print("Key=", result[1].decode())
print("----------result----------")
print(result[0].decode().rstrip())
结果
Key= Terminator X: Bring the noise
----------result----------
I'm back and I'm ringin' the bell
A rockin' on the mike while the fly girls yell
In ecstasy in the back of me
Well that's my DJ Deshay cuttin' all them Z's
Hittin' hard and the girlies goin' crazy
Vanilla's on the mike, man I'm not lazy.
I'm lettin' my drug kick in
It controls my mouth and I begin
To just let it flow, let my concepts go
My posse's to the side yellin', Go Vanilla Go!
Smooth 'cause that's the way I will be
And if you don't give a damn, then
Why you starin' at me
So get off 'cause I control the stage
There's no dissin' allowed
I'm in my own phase
The girlies sa y they love me and that is ok
And I can dance better than any kid n' play
Stage 2 -- Yea the one ya' wanna listen to
It's off my head so let the beat play through
So I can funk it up and make it sound good
1-2-3 Yo -- Knock on some wood
For good luck, I like my rhymes atrocious
Supercalafragilisticexpialidocious
I'm an effect and that you can bet
I can take a fly girl and make her wet.
I'm like Samson -- Samson to Delilah
There's no denyin', You can try to hang
But you'll keep tryin' to get my style
Over and over, practice makes perfect
But not if you're a loafer.
You'll get nowhere, no place, no time, no girls
Soon -- Oh my God, homebody, you probably eat
Spaghetti with a spoon! Come on and say it!
VIP. Vanilla Ice yep, yep, I'm comin' hard like a rhino
Intoxicating so you stagger like a wino
So punks stop trying and girl stop cryin'
Vanilla Ice is sellin' and you people are buyin'
'Cause why the freaks are jockin' like Crazy Glue
Movin' and groovin' trying to sing along
All through the ghetto groovin' this here song
Now you're amazed by the VIP posse.
Steppin' so hard like a German Nazi
Startled by the bases hittin' ground
There's no trippin' on mine, I'm just gettin' down
Sparkamatic, I'm hangin' tight like a fanatic
You trapped me once and I thought that
You might have it
So step down and lend me your ear
'89 in my time! You, '90 is my year.
You're weakenin' fast, YO! and I can tell it
Your body's gettin' hot, so, so I can smell it
So don't be mad and don't be sad
'Cause the lyrics belong to ICE, You can call me Dad
You're pitchin' a fit, so step back and endure
Let the witch doctor, Ice, do the dance to cure
So come up close and don't be square
You wanna battle me -- Anytime, anywhere
You thought that I was weak, Boy, you're dead wrong
So come on, everybody and sing this song
Say -- Play that funky music Say, go white boy, go white boy go
play that funky music Go white boy, go white boy, go
Lay down and boogie and play that funky music till you die.
Play that funky music Come on, Come on, let me hear
Play that funky music white boy you say it, say it
Play that funky music A little louder now
Play that funky music, white boy Come on, Come on, Come on
Play that funky music
进程已结束,退出代码0
题目4
题目内容
Cracking SHA1-Hashed Passwords
源码
import hashlib
import itertools
import datetime
starttime = datetime.datetime.now()
hash1 = "67ae1a64661ac8b4494666f58c4822408dd0a3e4"
searchList = [['Q', 'q'], ['W', 'w'], ['I', 'i'], ['N', 'n'], ['5', '%'], ['8', '('], ['0', '='], ['*', '+']]
def encryptSha(str):
sha = hashlib.sha1(str.encode("utf-8"))
encrypts = sha.hexdigest()
return encrypts
if __name__ == '__main__':
zeroStr = "00000000"
str4 = ""
zeroList = list(zeroStr)
for a in range(0, 2):
zeroList[0] = searchList[0][a]
for b in range(0, 2):
zeroList[1] = searchList[1][b]
for c in range(0, 2):
zeroList[2] = searchList[2][c]
for d in range(0, 2):
zeroList[3] = searchList[3][d]
for e in range(0, 2):
zeroList[4] = searchList[4][e]
for f in range(0, 2):
zeroList[5] = searchList[5][f]
for g in range(0, 2):
zeroList[6] = searchList[6][g]
for h in range(0, 2):
zeroList[7] = searchList[7][h]
finStr = "".join(zeroList)
for i in itertools.permutations(finStr, 8):
str4 = encryptSha("".join(i))
if str4 == hash1:
print("".join(i))
endtime = datetime.datetime.now()
print("time: " + str(endtime - starttime))
结果
(Q=win*5
time: 0:00:06.105986
第二次大作业(1024)
题目1
题目内容
大致意思就是说解密CBC模式下使用PKCS #5填充的密文,通过与服务器进行交互来得到是否没有错误的信息。
思路
源码
# -*- coding: cp936 -*-
from oracle import *
from Crypto.Util import strxor
import re
def main():
CipherText = "9F0B13944841A832B2421B9EAF6D9836813EC9D944A5C8347A7CA69AA34D8DC0" \
"DF70E343C4000A2AE35874CE75E64C31"
BlockCount = 2
Division = len(CipherText) / (BlockCount + 1)
CipherTextList = re.findall(".{" + str(Division) + "}", CipherText)
Oracle_Connect()
PlainText = []
IVALUE = []
for bc in range(0, BlockCount): # 对两组密文分别求解
print "-" * 60
print "Now Detecting Block " + str(bc + 1) + "."
IV = CipherTextList[bc]
ivalue = [] # 将解密后的ciphertext初始化
TempIV = "0" * 32 # 初始化 IV
TempIV = re.findall('.{2}', TempIV)[::-1]
padding = 1
for l in range(16):
print "-" * 60
print "Now Detecting Last " + str(l + 1) + ' Byte In Block.'
for ll in range(l):
TempIV[ll] = hex(int(ivalue[ll], 16) ^ padding)[2:].zfill(2) # 更新 TempIV
for n in range(0, 256): # 从0x00-0xFF遍历寻找符合条件的字节
TempIV[l] = hex(n)[2:].zfill(2)
data = "".join(TempIV[::-1]) + CipherTextList[bc + 1]
print data
ctext = [(int(data[i:i + 2], 16)) for i in range(0, len(data), 2)]
Feedback = Oracle_Send(ctext, 2)
if str(Feedback) == "1":
ivalue += [hex(n ^ padding)[2:].zfill(2)]
break
print "-" * 60
print "Now The Value of IV is: ", "".join(TempIV[::-1])
print "The Temporary Ciphertext after Decryption is: ", "".join(ivalue[::-1])
padding += 1
ivalue = "".join(ivalue[::-1])
IVALUE += [ivalue]
# IV与CADe异或求明文
plaintext = re.findall("[0-9a-f]+", str(hex(int(IV, 16) ^ int("".join(ivalue), 16))))[1].decode("hex")
PlainText += [plaintext]
print "Detecting Block", bc + 1, "Finished."
print "The ivalue" + str(bc + 1) + " is:", ivalue
print "The Plaintext" + str(bc + 1) + "is:", plaintext
print "-" * 60
Oracle_Disconnect()
print "The Ciphertext after Decryption is: ", "".join(IVALUE)
print "The Plaintext is:", "".join(PlainText)
if __name__ == '__main__':
main()
结果
题目2
题目内容
Set2 - The Cryptopals Crypto Challenges
源码
c7.py
from Crypto.Cipher import AES
from c9 import *
def decrypt_aes_128_ecb(data, key):
cipher = AES.new(key, AES.MODE_ECB)
return pkcs7_unpad(cipher.decrypt(data))
c8.py
from Crypto.Cipher.AES import block_size
def count_aes_ecb_repetitions(ciphertext):
chunks = [ciphertext[i:i + block_size] for i in range(0, len(ciphertext), block_size)]
number_of_duplicates = len(chunks) - len(set(chunks))
return number_of_duplicates
def detect_ecb_encrypted_ciphertext(ciphertexts):
best = (-1, 0)
for i in range(len(ciphertexts)):
repetitions = count_aes_ecb_repetitions(ciphertexts[i])
best = max(best, (i, repetitions), key = lambda t: t[1])
return best
def main():
ciphertexts = [bytes.fromhex(line.strip()) for line in open("8.txt")]
result = detect_ecb_encrypted_ciphertext(ciphertexts)
print("The ",result[0])
print("the",result[1])
print(result[0])
assert result[0] == 132
if __name__ == '__main__':
main()
c9.py
def pkcs7_padding(message,block_size):
if len(message) == block_size:
return message
padding_thing = block_size - (len(message) % block_size) # 填充内容、长度计算
return message + bytes([padding_thing] * padding_thing)
def is_pkcs7_padded(bin_data):
padding = bin_data[-bin_data[-1]:]
return all(padding[b] == len(padding) for b in range(0, len(padding)))
def pkcs7_unpad(padded_message):
if len(padded_message) == 0:
raise Exception("the input must contain at least one byte!")
if not is_pkcs7_padded(padded_message):
return padded_message
padded_len = padded_message[len(padded_message) - 1]
return padded_message[:-padded_len]
def c9_main():
message=b"YELLOW SUBMARINE"
padded_message = pkcs7_padding(message, 20)
print(padded_message)
assert pkcs7_unpad(padded_message) == message
if __name__ == '__main__':
c9_main()
c10.py
from Crypto.Cipher.AES import block_size
from base64 import b64decode
from c7 import *
from c9 import *
def encrypt_aes_128_ecb(message, key):
cipher_message = AES.new(key, AES.MODE_ECB)
return cipher_message.encrypt(pkcs7_padding(message, AES.block_size))
def ez_xor(bin_data1, bin_data2):
return bytes([b1 ^ b2 for b1, b2 in zip(bin_data1, bin_data2)])
def encrypt_aes_128_cbc(message, key, iv):
ciphertext = b""
p = iv
for i in range(0, len(message), AES.block_size):
temp_plaintext_block = pkcs7_padding(message[i: i + AES.block_size], AES.block_size)
block_ciphertext_in = ez_xor(temp_plaintext_block, p)
encrypted_block = encrypt_aes_128_ecb(block_ciphertext_in, key)
ciphertext += encrypted_block
p = encrypted_block
return ciphertext
def decrypt_aes_128_cbc(data, key, iv, unpad=True):
plaintext = b""
p = iv
for i in range(0, len(data), AES.block_size):
temp_ciphertext_block = data[i:i + AES.block_size]
decrypted_blcok = decrypt_aes_128_ecb(temp_ciphertext_block, key)
plaintext += ez_xor(p, decrypted_blcok)
p = temp_ciphertext_block
return pkcs7_unpad(plaintext) if unpad else plaintext
def main():
iv = b'\x00' * AES.block_size
key = b"YELLOW SUBMARINE"
with open("10.txt") as f:
bin_data = b64decode(f.read())
print(decrypt_aes_128_cbc(bin_data, key, iv).decode().rstrip())
if __name__ == '__main__':
main()
c12.py
from Crypto import Random
from c10 import *
class ECBOracle:
def __init__(self, secret_padding):
self._key = Random.new().read(AES.key_size[0])
self._secret_padding = secret_padding
def encrypt(self, data):
return encrypt_aes_128_ecb(data + self._secret_padding, self._key)
def find_block_length(encryption_oracle):
my_text = b""
ciphertext = encryption_oracle.encrypt(my_text)
initial_len = len(ciphertext)
new_len = initial_len
while new_len == initial_len:
my_text += b"A"
ciphertext = encryption_oracle.encrypt(my_text)
new_len = len(ciphertext)
return new_len - initial_len
c14.py(第6题)
from random import randint
from c12 import *
from c8 import *
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):
return encrypt_aes_128_ecb(self._random_prefix + data + self._secret_padding, self._key)
def get_next_byte(prefix_length, block_length, curr_decrypted_message, encryption_oracle):
length_to_use = (block_length - prefix_length - (1 + len(curr_decrypted_message))) % block_length
my_input = b"A" * length_to_use
cracking_length = prefix_length + length_to_use + len(curr_decrypted_message) + 1
real_ciphertext = encryption_oracle.encrypt(my_input)
for i in range(256):
fake_ciphertext = encryption_oracle.encrypt(my_input + curr_decrypted_message + bytes([i]))
if fake_ciphertext[:cracking_length] == real_ciphertext[:cracking_length]:
return bytes([i])
return b""
def has_equal_block(ciphertext, block_length):
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):
ciphertext1 = encryption_oracle.encrypt(b"")
ciphertext2 = encryption_oracle.encrypt(b"a")
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
for i in range(block_length):
fake_input = bytes([0] * (2 * block_length + i))
ciphertext = encryption_oracle.encrypt(fake_input)
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_harder_decryption(encryption_oracle):
block_length = find_block_length(encryption_oracle)
ciphertext = encryption_oracle.encrypt(bytes([0] * 64))
assert count_aes_ecb_repetitions(ciphertext) > 0
prefix_length = find_prefix_length(encryption_oracle, block_length)
mysterious_text_length = len(encryption_oracle.encrypt(b"")) - prefix_length
secret_padding = b""
for i in range(mysterious_text_length):
secret_padding += get_next_byte(prefix_length, block_length, secret_padding, encryption_oracle)
return secret_padding
def main():
secret_padding = b64decode("Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg"
"aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq"
"dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg"
"YnkK")
oracle = HarderECBOracle(secret_padding)
discovered_secret_padding = byte_at_a_time_ecb_harder_decryption(oracle)
print(pkcs7_unpad(discovered_secret_padding))
if __name__ == '__main__':
main()
c15.py(第7题)
def is_pkcs7_padded(bin_data):
padding = bin_data[-bin_data[-1]:]
return all(padding[b] == len(padding) for b in range(0, len(padding)))
c16.py(第8题)
from Crypto.Cipher import AES
from Crypto import Random
from c10 import *
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):
data = data.replace(";", "").replace("=", "")
plaintext = (self._prefix + data + self._suffix).encode()
return encrypt_aes_128_cbc(plaintext, self._key, self._iv)
def decrypt_and_check_admin(self, ciphertext):
data = decrypt_aes_128_cbc(ciphertext, self._key, self._iv)
return b";admin=true" in data
def find_block_length(encryption_oracle):
text = ""
ciphertext = encryption_oracle(text)
ordi_len = len(ciphertext)
new_len = ordi_len
while new_len == ordi_len:
text += "A"
ciphertext = encryption_oracle(text)
new_len = len(ciphertext)
return new_len - ordi_len
def find_prefix_length(encryption_oracle, block_length):
ciphertext_a = encryption_oracle("A")
ciphertext_b = encryption_oracle("B")
common_len = 0
while ciphertext_a[common_len] == ciphertext_b[common_len]:
common_len += 1
common_len = int(common_len / block_length) * block_length
for i in range(1, block_length + 1):
ciphertext_a = encryption_oracle("A" * i + "X")
ciphertext_b = encryption_oracle("A" * i + "Y")
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_atk(encryption_oracle):
block_length = find_block_length(encryption_oracle.encrypt)
prefix_length = find_prefix_length(encryption_oracle.encrypt, block_length)
addition_prefix_bytes = (block_length - (prefix_length % block_length)) % block_length
total_prefix_length = prefix_length + addition_prefix_bytes
plaintext = "?admin?true"
additional_plaintext_bytes = (block_length - (len(plaintext) % block_length)) % block_length
final_palintext = additional_plaintext_bytes * "?" + plaintext
ciphertext = encryption_oracle.encrypt(addition_prefix_bytes * "?" + final_palintext)
semicolon = ciphertext[total_prefix_length - 11] ^ ord("?") ^ ord(";")
equals = ciphertext[total_prefix_length - 5] ^ ord("?") ^ ord("=")
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_atk(encryption_oracle)
print(encryption_oracle.decrypt_and_check_admin(forced_ciphertext))
if __name__ == '__main__':
main()
结果
c14(第6题)
b"Rollin' in my 5.0\nWith my rag-top down so my hair can blow\nThe girlies on standby waving just to say hi\nDid you stop? No, I just drove by\n"
c16(第8题)
True
题目3
题目内容
思路
- 根据参考资料,16.1-16.1.5,可以算出?的值
- 根据文件得到kseed
- 把kseed和c相连,计算d的sha1,前十六位是ka,后十六位是kb
- Ka和kb奇偶校验生成key
- 把密文以base64解码,用key解密
源码
# coding=utf-8
import base64
import re
from Crypto.Cipher import AES
from Crypto.Hash import SHA
def odd_even_verify(ka): # 调整奇偶校验位
k = []
for i in ka:
if bin(int(i, 16) >> 1).count('1') % 2 == 0: # 若1的个数为偶数
k += [hex(1 + (int(i, 16) >> 1 << 1))[2:].zfill(2)] # 调整为奇数
else:
k += [hex((int(i, 16) >> 1 << 1))[2:].zfill(2)] # 不动
return ''.join(k)
def get_sha1(d):
h = SHA.new()
h.update(d) # 计算d的sha1
return h.hexdigest()[:32] # 获取sha1的前32位
def main():
c = '9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMazJuApwd6' \
'+jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI ' # C由文档中获取
c = base64.b64decode(c)
code = '12345678<8<<<1110182<1111167<<<<<<<<<<<<<<<4'
code_no = code[:9]
code_verify = code[9]
nation = code[10:13]
birth = code[13:19]
birth_verify = code[19]
sex = code[20]
date_end = code[21:27]
date_end_verify = code[27]
other = code[28:]
# 按照文件提取code中机读区对应的信息
info = code_no + code_verify + birth + birth_verify + date_end + date_end_verify # 得到机读区信息
k_seed = get_sha1(info) # 获取k_seed
d = (k_seed + '0' * 7 + '1').decode('hex') # 连接k_seed和c
key = get_sha1(d) # 获取key的sha1
k1 = odd_even_verify(re.findall('.{2}', key[:16])) # 对前半后半分别进行奇偶校验位的调整
k2 = odd_even_verify(re.findall('.{2}', key[16:]))
key = k1 + k2 # 再将密钥key拼接起来
print 'The key is:', key
ciphertext = AES.new(key.decode('hex'), AES.MODE_CBC, ('0' * 32).decode('hex'))
print 'The M is:', ciphertext.decrypt(c)
if __name__ == '__main__':
main()
结果
The key is: ea8645d97ff725a898942aa280c43179
The M is: Herzlichen Glueckwunsch. Sie haben die Nuss geknackt. Das Codewort lautet: Kryptographie!
第三次大作业(RSA大礼包)
题目
题目内容
这次的题目是2016年密码挑战赛的第三题,大意上是说现在有一个RSA的加解密软件,并且有一些明密文对和对应参数,同时截获了一些密文。现在利用多种针对RSA的攻击,对明密文对和对应的参数,以及截获的密文进行分析和挖掘。从加密数据中,恢复通关密语和RSA的体制参数,以及对应的明文。
思路
利用多种针对RSA的攻击,如RSA共模攻击、低指数攻击、p-1分解法、公因数攻击、Fermat攻击等,尽可能多得在所截获的数据中,挖掘出明文和参数。
源码
共模攻击
如果一对消息的模数和明文相同,而指数互质,那么可以找到
x
,
y
x,y
x,y 使
x
e
1
+
y
e
2
=
1
xe_1+ye_2=1
xe1+ye2=1 那么有
c
1
x
+
c
2
y
≡
m
m
o
d
n
c_1^x+c_2^y≡m ~mod~n
c1x+c2y≡m mod n
Frame0、Frame4采用该种攻击方法
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 sameMod():
index1 = 0
index2 = 0
for i in range(21):
for j in range(i + 1, 21):
if F[i] == F[j]:
index1, index2 = i, j
n = int(F[index1], 16)
e1 = int(T[index1], 16)
e2 = int(T[index2], 16)
c1 = int(S[index1], 16)
c2 = int(S[index2], 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
result = binascii.a2b_hex(hex(m)[-16:]).decode('ascii')
return result
公因数攻击
Frame1、Frame18采用该种攻击方法
def sameFactor():
index = []
for i in range(21):
for j in range(i + 1, 21):
if int(F[i], 16) == int(F[j], 16):
continue
prime = gmpy2.gcd(int(F[i], 16), int(F[j], 16))
if prime != 1:
index.append(i)
index.append(j)
frameP = prime
frameQ1 = int(F[index[0]], 16) // frameP
frameQ18 = int(F[index[1]], 16) // frameP
framePhi1 = (frameP - 1) * (frameQ1 - 1)
framePhi18 = (frameP - 1) * (frameQ18 - 1)
frameD1 = gmpy2.invert(int(T[index[0]], 16), framePhi1)
frameD18 = gmpy2.invert(int(T[index[1]], 16), framePhi18)
plaintext1 = gmpy2.powmod(int(S[index[0]], 16), frameD1, int(F[index[0]], 16))
plaintext18 = gmpy2.powmod(int(S[index[1]], 16), frameD18, int(F[index[1]], 16))
fin1 = binascii.a2b_hex(hex(plaintext1)[-16:]).decode('ascii')
fin18 = binascii.a2b_hex(hex(plaintext18)[-16:]).decode('ascii')
print('Frame', index[0], ':', fin1, sep='')
print('Frame', index[1], ':', fin18, sep='')
return 0
低指数攻击
若多个消息明文和指数相同,指数和明文个数相同,而且模数互质,那么可以列出方程组
{
m
e
≡
c
1
m
o
d
n
1
m
e
≡
c
2
m
o
d
n
2
m
e
≡
c
3
m
o
d
n
3
.
.
.
m
e
≡
c
e
m
o
d
n
e
\left\{\begin{matrix} m^e≡c_1~mod~n_1\\m^e≡c_2~mod~n_2\\m^e≡c_3~mod~n_3\\...\\m^e≡c_ e~mod~n_e \end{matrix}\right.
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧me≡c1 mod n1me≡c2 mod n2me≡c3 mod n3...me≡ce mod ne
利用中国剩余定理可以解出
m
e
<
n
1
n
2
n
3
.
.
.
n
e
m^e<n_1n_2n_3...n_e
me<n1n2n3...ne ,从而直接开根解出明文
Frame3、Frame8、Frame12、Frame16、Frame20采用该种攻击方法
def lowE():
sessions = [{"c": int(S[3], 16), "n": int(F[3], 16)},
{"c": int(S[8], 16), "n": int(F[8], 16)},
{"c": int(S[12], 16), "n": int(F[12], 16)},
{"c": int(S[16], 16), "n": int(F[16], 16)},
{"c": int(S[20], 16), "n": int(F[20], 16)}]
data = []
for session in sessions:
data += [(session['c'], session['n'])]
x, y = ChineseRemainder(data)
pt = gmpy2.iroot(gmpy2.mpz(x), 5)
print(binascii.a2b_hex(hex(pt[0])[-16:]).decode('ascii'))
return binascii.a2b_hex(hex(pt[0])[2:])
Pollard p-1分解法
选取一个数
B
B
B,并令
k
=
B
!
k=B!
k=B!,如果
B
B
B足够大,则有
p
−
1
∣
k
p-1|k
p−1∣k。由
2
p
−
1
≡
1
m
o
d
n
2^{p-1}≡1~mod~n
2p−1≡1 mod n,有
p
∣
2
k
−
1
p|2^k-1
p∣2k−1。又有
p
∣
n
p|n
p∣n,所以有
p
∣
a
p|a
p∣a,
a
≡
2
k
−
1
m
o
d
n
。
a≡2^k-1~mod~n。
a≡2k−1 mod n。将幂与
n
n
n取最大公因数即可分解模数。
Frame2、Frame6、Frame19均采用该种攻击方法
def p1(n):
B = pow(2,20)
a = 2
for i in range(2, B + 1):
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
def pollardResolve():
index_list = [2, 6, 19]
plaintext = []
for i in range(3):
N = int(F[index_list[i]], 16)
c = int(S[index_list[i]], 16)
e = int(T[index_list[i]], 16)
p = p1(N)
q = N // p
framePhi = (p - 1) * (q - 1)
d = gmpy2.invert(e, framePhi)
m = gmpy2.powmod(c, d, N)
plaintext.append(binascii.a2b_hex(hex(m)[2:]))
print(f"Frame{index_list[i]}:{(binascii.a2b_hex(hex(m)[-16:])).decode('ascii')}")
return plaintext
费马分解法
p、q比较接近时,可以使用这种攻击方法
Frame10采用该种攻击方法
def fermat(n):
B = math.factorial(2 ** 14)
v = 0
i = 0
u0 = gmpy2.iroot(n, 2)[0] + 1
while i <= (B - 1):
u = (u0 + i) * (u0 + i) - n
if gmpy2.is_square(u):
v = gmpy2.isqrt(u)
break
i += 1
p = u0 + i + v
return p
def fermatResolve():
for i in range(10, 14):
N = int(F[i], 16)
p = fermat(N)
结果
共模攻击
My secre
公因数攻击
Frame1:. Imagin
Frame18:m A to B
低指数攻击
t is a f
Pollard p-1 分解法
Frame2: That is
Frame6: "Logic
Frame19:instein.
费马分解法
Frame10:will get
通过上边的攻击我们可以破解出Frame0、1、2、3、4、6、8、10、12、16、18、19、20,再通过某些科学手段(百度)得到以下结果
"My secret is a famous saying of Albert Einstein. That is "Logic will get you from A to B. Imagination will take you everywhere.""