0x00 前言
Cryptopals这个网站有许多较为有趣的密码学问题,此系列用于记录一些密码学问题的解决分析过程.
题目链接地址: Cryptopals-Set1
0x01 储备函数
Set1部分功能的实现需要依赖一些储备函数,进行字符串、字节数组与十六进制数之间的转换.相关代码如下:
1 | # 十六进制字符串转化为字节数组 |
0x02 Challenge1 Convert hex to base64
题目要求
实现一个函数,将十六进制字符串转化为base64编码的字符串.
Python编解码
计算机中的文本数据以Unicode万国码的形式存储.在写入与输出过程中会经历将字符串解码为Unicode后存储在本地以及将本地Unicode字符串进行指定类型的编码这两个过程.
编码的过程是将Unicode字符按照编码规则(如utf-8)编成字节序列.但是在使用print语句打印编码后文本是乱码或对应中文,而不是字节序列.这是因为print语句默认进行了隐式编码,为的是让人类看见友好的字符数据.对于背后的十六进制数,需要调用魔术方法__repr__()
. 解码过程是将字节序列按照编码规则(如utf-8)解释成Unicode形式.
Python2/3中的str与bytes
Python3中,文本字符串类型(Unicode编码数据)被命名为str,字节字符串类型被命名为bytes.一般来说实例化一个字符串会得到一个str对象.所以Python3默认为Unicode,如果需要得到bytes类型数据,需要在文本之前加上前缀b或者进行encode.
1 | '测试' a = |
显然,str对象拥有encode方法,而bytes对象拥有decode方法.
Python2中情况就大相径庭.Python3中的str对象在Python2中为unicode,而bytes对象在Python2中叫做str.如果想要得到一个文本字符串,则需要在字符串之前加上前缀u或者进行decode.值得注意的是,str对象的encode方法与unicode对象的decode方法是为报错而生的,所以尽量不要使用该类方法.
题目分析
首先将十六进制字符串使用decode方法解码为unicode编码,然后再将字符串编码为base64.代码如下:
1 | # -*- coding:utf-8 -*- |
0x03 Challenge2 Fixed XOR
题目要求
实现一个函数,输入为两个等长的字符串,输出为这两个字符串的异或结果.
题目分析
储备函数中已经实现了两个字节数组异或的函数.若要实现两个等长字符串的异或,先使用储备函数中的str_to_bytelist()
方法将字符串转化为字节数组,然后调用xor方法进行异或操作,最后再调用bytelist_to_str()
方法将字节数组转化为十六进制字符串.代码如下:
1 | # -*- coding:utf -*- |
0x04 Challenge3 Single-byte XOR cipher
题目要求
给定一个十六进制字符串,该字符串是明文与一个单字符key进行异或加密之后得到的结果.现要求在已知cipher_hex_string的情况下还原plaintext.
题目分析
该题目使用的核心思想为爆破.已知密文是明文与同一单字符进行异或生成,那么我们可以循环遍历所有可能的单字符,与密文进行异或从而得到所有可能的明文.根据不同英文字母出现的统计频率,可以计算所有明文对应的频率分数.频率分数越高则该明文为语义通顺的英文文本可能性越高.取最高频率分数对应的结果为明文,代码如下:
1 | # -*- coding:utf-8 -*- |
运行结果如下图:
0x05 Challenge4 Detect Single-character XOR
题目要求
给出challenge4.txt文件,文件中的一个字符串是由单字符异或方法加密的十六进制编码串,找到并解密这个字符串.
题目分析
结合Challenge3中的函数,对所给文件的每个字符串调用ergodic_found()
方法进行遍历和评估,对于文件中每个字符串返回的所有得分最高对象,选取其中分数最高的作为最终结果,代码如下:
1 | # -*- coding:utf-8 -*- |
运行结果如下图:
0x06 Challenge5 Implement repeating-key XOR
题目要求
实现一个函数,给定待加密的字符串和密钥,该函数通过使用重复密钥异或的方法加密一段英文文本,返回加密后的结果.
题目分析
将待加密的字符串按照keysize的大小进行分块,将每块分别与密钥key进行逐字符异或加密,最后将每块的加密结果合并到一起.代码如下:
1 | from Set0 import * |
0x07 Challenge6 Break repeating-key XOR
题目要求
该挑战中提供challenge6.txt文件,文件使用重复密钥异或方法加密后,再经过bas64编码后得到的文本,我们需要找到密钥,对其进行解密.
题目分析
对密文的攻击采用了流密码加密密钥重用对应的攻击方法.大体破解方法如下:首先从2到40猜测密钥长度,对于每个KEYSIZE,求得解密块两两之间的汉明距离,对KEYSIZE取平均值.选取2-3个最小汉明距离对应的KEYSIZE作为最终的密钥长度.获得密钥长度后,按照密钥长度对密文进行分块,即将使用同一单字符密钥加密后的密文分到一组.使用破解单字符异或的方法来逐字符破解密钥.对不同KEYSIZE解密后的明文进行评估,选取得分最高的一组作为最终的明文.代码如下:
1 | from Set0 import * |
解密结果如下图:
0x08 Challenge7 AES in ECB mode
题目要求
该挑战提供了一个文件challenge7.txt,文件的内容是经过AES-128 ECB模式加密后的base64编码的文本,已知密钥key,进行解密.
题目分析
直接使用from Crypto.Cipher import AES
调用AES模块,构造AES-ECB生成器,调用其encrypt和decrypt方法进行加解密.解密后明文末尾包含填充部分,可以遵循PKCS7填充标准进行填充内容剔除.代码如下:
1 | from Crypto.Cipher import AES |
0x09 Challenge8 Detect AES in ECB mode
题目要求
该挑战提供一个文件,文件内容是十六进制编码的若干加密字符串,其中一个字符串是通过AES-ECB模式加密的,key未知.我们需要通过密文找到这个AES ECB加密的字符串.
题目分析
由于ECB模式加密是静态和确定的,相同的16字节明文块总是生成相同16字节密文块.所以我们可以通过查找加密字符串中是否有相同的密文块,来判断是否为ECB模式.如果存在,则为ECB模式.此方法只可以用于判断所给密文字节数较多的情况,即在基数较大情况下出现相同的16字节明文块的统计概率较高.对于所给密文字节数较少的情况,不存在对应统计规律,无法使用该方法判断.代码如下:
1 | # -*- coding:utf-8 -*- |