前言
第一次接触加密算法,经过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。
参考文章: