学习笔记——cryptopals解密(一)

目录

  • 前言
  • Set 1:Basics
    • 1.第一题
    • 2.第二题
    • 3.第三题


前言

第一次接触加密算法,通过Crytopals来学习。

参考文章:

  1. cryptopals解密之旅 (一)
  2. 浅谈常见的七种加密算法及实现

Set 1:Basics

1.第一题

学习笔记——cryptopals解密(一)_第1张图片
学习笔记——cryptopals解密(一)_第2张图片
从题中可以了解,需要将文章中16进制转换为base64编码,那么首先了解一下base64。
Base64:是网络上最常见的用于传输8Bit字节码的编码方式之一,是一种基于64个可打印字符来表示二进制数据的方法。

(1) Base64是网络上最常见的用于传输8Bit字节码的可读性编码算法之一。
(2) 可读性编码算法不是为了保护数据的安全性,而是为了可读性。
(3) 可读性编码不改变信息内容,只改变信息内容的表现形式

在一些网络传送渠道,有时候有些的字节字符不能被支持.比如图片的二进制流的每个字节不可能全部是可见字符,这种情况下传送不了.而Base64的机制就能很好解决这种问题,它不改变原来的协议,在原来的基础做一种扩展,基于64个可打印字符来表示二进制.这样不会改变原来的图片,同理还有邮件等需要加密的文件。

首先,了解下base64是如何编码的:
首先我们规定一个字符:abc
1.将字符通过ASCII码编码。
2.将编好的ASCII码变为二进制(8位),并以6位为一组,分成四组。
3.将这四组的高位各补两个0,转为十进制数。
4.查表,得到对应的字符,就是对应的Base64转化的字符。
学习笔记——cryptopals解密(一)_第3张图片

如果最后剩下两个输入数据,在编码结果后加1个“=”;如果最后剩下一个输入数据,编码结果后加2个“=”;如果没有剩下任何数据,就什么都不要加。或分成6位一组后,最后一组没有到6位时需要填充一个=或者两个=。

学习笔记——cryptopals解密(一)_第4张图片

代码实现(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()

结果为
学习笔记——cryptopals解密(一)_第5张图片

2.第二题

学习笔记——cryptopals解密(一)_第6张图片
由题可知,这题是考察XOR异或。
需要对两个16进制代码异或,生成下面的代码。
使用python实现:

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()

结果为
学习笔记——cryptopals解密(一)_第7张图片
但是,结果中含有[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.第三题

学习笔记——cryptopals解密(一)_第8张图片
在第三题里,只给了一个经过异或的密文,而我们知道是由单个字符所异或,这里起初想用暴力破解。
字符频率是一个很好的指标,我没有考虑到字符频率的问题(不在目前知识范围内)而忽略了这个提示,查阅百度发现,字符频率就是日常生活该字符的使用频率。如果为正常的英文文本,那么它的字符频率应该尽可能的大。所以,只要能计算出哪个字符异或后字符频率打,这就是这道题的答案。
比如这里有两个解密后的字符串:
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。

参考文章:

  1. 什么是Base64?
  2. 异或

你可能感兴趣的:(学习笔记——cryptopals解密(一))