前言
第一次接觸加密算法,經過Crytopals來學習。python
參考文章:算法
Set 1:Basics
1.第一題
從題中能夠了解,須要將文章中16進制轉換爲base64編碼,那麼首先了解一下base64。
Base64:是網絡上最多見的用於傳輸8Bit字節碼的編碼方式之一,是一種基於64個可打印字符來表示二進制數據的方法。
安全
(1) Base64是網絡上最多見的用於傳輸8Bit字節碼的可讀性編碼算法之一。
(2) 可讀性編碼算法不是爲了保護數據的安全性,而是爲了可讀性。
(3) 可讀性編碼不改變信息內容,只改變信息內容的表現形式
網絡
在一些網絡傳送渠道,有時候有些的字節字符不能被支持.好比圖片的二進制流的每一個字節不可能所有是可見字符,這種狀況下傳送不了.而Base64的機制就能很好解決這種問題,它不改變原來的協議,在原來的基礎作一種擴展,基於64個可打印字符來表示二進制.這樣不會改變原來的圖片,同理還有郵件等須要加密的文件。app
首先,瞭解下base64是如何編碼的:
首先咱們規定一個字符:abc。
1.將字符經過ASCII碼編碼。
2.將編好的ASCII碼變爲二進制(8位),並以6位爲一組,分紅四組。
3.將這四組的高位各補兩個0,轉爲十進制數。
4.查表,獲得對應的字符,就是對應的Base64轉化的字符。
學習
若是最後剩下兩個輸入數據,在編碼結果後加1個「=」;若是最後剩下一個輸入數據,編碼結果後加2個「=」;若是沒有剩下任何數據,就什麼都不要加。或分紅6位一組後,最後一組沒有到6位時須要填充一個=或者兩個=。編碼
代碼實現(python):加密
from enum import Enum b64_encoding_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk" \ "lmnopqrstuvwxyz0123456789+/" class Status(Enum): START_NEX = 0 TAKE_2 = 1 TAKE_4 = 2 def hex_to_base64(hexdata): b64data = "" sixbits = 0 status = Status.START_NEX for hexchar in hexdata: dec = int(hexchar, 16) if status == Status.START_NEX: sixbits = dec status = Status.TAKE_2 elif status == Status.TAKE_2: sixbits = (sixbits << 2) | (dec >> 2) b64data += b64_encoding_table[sixbits] sixbits = (dec & 0x3) status = Status.TAKE_4 elif status == Status.TAKE_4: sixbits = (sixbits << 4) | dec b64data += b64_encoding_table[sixbits] status = Status.START_NEX if status == Status.TAKE_2: sixbits <<= 2 b64data += b64_encoding_table[sixbits] b64data += "=" elif status == Status.TAKE_4: sixbits <<= 4 b64data += b64_encoding_table[sixbits] b64data += "==" return b64data def main(): print(hex_to_base64("49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d")) if __name__ == '__main__': main()
結果爲
spa
2.第二題
由題可知,這題是考察XOR異或。
須要對兩個16進制代碼異或,生成下面的代碼。
使用python實現:
.net
def hex_xor (hex1, hex2): dec1 = int(hex1, 16) dec2 = int(hex2, 16) xor = dec1 ^ dec2 return hex(xor) def main(): a = hex_xor("1c0111001f010100061a024b53535009181c", "686974207468652062756c6c277320657965") print(a) if __name__ == '__main__': main()
結果爲
可是,結果中含有[0x]這個前綴,查閱資料發現,只要在return處後面加[2:] 就能夠解決。
return hex(xor)[2:]
結果與題目中所要結果相同。
異或:異或運算符」∧」,它的規則是若參加運算的兩個二進位同號,則結果爲0(假);異號則爲1(真)。即 0∧0=0,0∧1=1, 1^0=1,1∧1=0。
運算 | 說明 |
---|---|
0^1=1, 0^0=0 | 0異或任何數,其結果=任何數 |
1^0=1, 1^1=0 | 1異或任何數,其結果=任何數取反 |
x^x=0 | 任何數異或本身,等於把本身置0 |
異或的運算:先轉化爲二進制,再對照位來進行運算,相同爲0,不一樣爲1。
經過按位異或運算,能夠實現兩個值的交換,而沒必要使用臨時變量。例如交換兩個整數a=3,b=4的值,可經過下列語句實現:
a=a∧b;
b=b∧a;
a=a∧b;
3.第三題
在第三題裏,只給了一個通過異或的密文,而咱們知道是由單個字符所異或,這裏起初想用暴力破解。
字符頻率是一個很好的指標,我沒有考慮到字符頻率的問題(不在目前知識範圍內)而忽略了這個提示,查閱百度發現,字符頻率就是平常生活該字符的使用頻率。若是爲正常的英文文本,那麼它的字符頻率應該儘量的大。因此,只要能計算出哪一個字符異或後字符頻率打,這就是這道題的答案。
好比這裏有兩個解密後的字符串:
I am a student.
sd evbgac rgbq.
經過計算
第一個:0.04+0.19+0.08+0.02+0.19+0.08+0.19+0.06+0.09+0.02+0.04+0.12+0.06+0.09=1.27
同理第二個:
0.82
由此可知。第一個爲正確結果
首先準備一個字符頻率表:
而後再計算分值
def get_score(input_bytes): score = 0 for byte in input_bytes: score += CHARACTER_FREQ.get(chr(byte).lower(), 0) return score
對字符串中每一個字符與key進行異或
def singlechar_xor(input_bytes, key_value): output = b'' for char in input_bytes: output += bytes([char ^ key_value]) return output
用每一個可能的字節對密碼異或解密,計算得出的明文分數。所用的key就是題目要找出來的key
def singlechar_xor_brute_force(ciphertext): candidates = [] for key_candidate in range(256): plaintext_candidate = singlechar_xor(ciphertext, key_candidate) candidates_score = get_score(plaintext_candidate) result = { 'key': key_candidate, 'score': candidates_score, 'plaintext': plaintext_candidate, } candidates.append(result) return sorted(candidates, key=lambda c: c['score'], reverse=True)[0]
打印明文和key
def pretty_print_result(result): print(result['plaintext'].decode().rstrip(), "\tKey:", chr(result['key']))
最後的結果爲
由此可知加密前的明文和key。
參考文章: