分组密码不仅仅是一个加密算法,也是用于实现多种不同密码学编制机制的万能元件。本文先介绍分组密码的设计原则,再介绍五种主要的分组工作模式,包括了它们的流程、特点以及攻击方法,最后列出几道CTF中的相关赛题。
在今天我们所使用的加密法中,分组加密是最常见的类型。分组加密法每次作用于固定大小的分组(与流密码不同,流密码每次加密数据流的一位或一个字节),一个分组的比特数就称为分组长度,典型的分组大小是64位或128位。与流密码一样, 两个用户要共享一个对称加密密钥。使用某些工作模式,分组密码可以获得与流密码相同的效果。一般来说,分组密码的应用范围比流密码要广泛,且绝大部分基于网络的对称密码应用使用的是分组密码。
分组密码是许多系统安全的一个重要组成部分。可用于构造:
分组密码实质上是字长为 n n n的数字序列的代换密码,如下图所示:
分组密码的设计问题在于找到一种算法,能在密钥控制下从一个足够大且足够好的置换子集中,简单而迅速地选出一个置换,用来对当前输入的明文的数字组进行加密变换。
分组密码算法应满足的要求:
混淆和扩散是由Shannon提出的设计密码系统的两个基本方法,目的是抗击攻击者对密码系统的统计分析。
混淆原则(Confusion):
扩散原则(Diffusion):
为什么需要工作模式:
电码本(ECB)模式一次处理一组明文分块,每次使用相同的密钥加密。使用电码本这个词是因为对于给定的密钥,任何b位的明文组只有唯一密文与之对应,所以可以想象存在一个很厚的密码本,根据任意b位明文都可以查到相应的密文。
明文若长于b位,则可简单将其分成b位一-组的块,有必要则可对最后一块进行填充。解密也是一-执行一块,且使用相同的密钥。明文(必要的时候进行填充)由一串b位的块组成,记为P, P2, … Pv,相应的密文分组依次是C,C2, … CN。我们可以如下定义ECB 模式。
特点和不足:
对ECB模式的攻击:
每个明文分组都是各自独立地进行加密和解密,但这其实是一个很大的弱点。假如存在一个攻击者,当他作为中间人截获两方的通信时,他能够改变密文的分组顺序,当接收者对密文进行解密时,由于密文分组的顺序被改变了,因此相应的明文分组的顺序也被改变了,那么接收者实际上是解密出了一段被篡改后的密文。
例:假设银行A和银行B之间的资金转帐系统所使用报文模式如下:
敌手C通过截收从A到B的加密消息,只要将第5至第12分组替换为自己的姓名和帐号相对应的密文,即可将别人的存款存入自己的帐号。
为了克服ECB的弱点,我们需要将重复的明文分组加密成不同的密文分组。满足这一要求的简单方法是使用CBC模式。这种模式下加密算法的输人是当前的明文组和
上一个密文组的异或,而使用的密钥是相同的,相当于将所有的明文组链接起来。
加解密方式:
第一块明文可以和一个初始向量( I V IV IV)异或后再加密,以产生第一一个明文分组。解密时将第一块密文解密的结果与 I V IV IV异或而恢复出第一块明文。 I V IV IV是和密文具有相同长度的数据分组。
对CBC模式的攻击:
对于AES, DES或任何的分组密码,加密是对一一个b位的分组进行加密。对于DES的例子,b=64,而在AES时,b= 128。但是密文反馈模式(CFB)和输出反馈模式(OFB)及计数器(CTR)模式,亦可将分组密码当成流密码使用。流密码不需要将明文填充到长度是分组长度的整数倍,且可以实时操作。所以,待发送的字符流中任何一个字符都可以用面向字符的流密码加密后立即发送。
适用范围:
对CFB模式的攻击:
OFB模式在结构上类似于CFB模式,但反馈的内容是DES的输出而不是密文,进入移位寄存器的数据和被加密明文无关,只和初始向量相关。
利用固定密钥k对自然数序列1,2.3…,n, …加密,将得到的密文分组序列看作密钥流序列,按加法密码的方式与明文分组逐位异或的一种方式。可以利用它产生伪随机数序列,其伪随机特性远比计算机产生的随机数的性质好。
ECB模式简单、高速,但最弱,易受重放和替换攻击,一般用于加密长度小于等于分组长度的消息。
CBC, CFB,OFB模式的选用取决于实际的特殊需求。
12.25DASCTF.Crypto.asa
#encoding:utf-8
from Crypto.Util.number import bytes_to_long, long_to_bytes, getPrime
import gmpy2, os
from Crypto.Cipher import AES
from flag import flag
assert flag.startswith("DASCTF{") and flag.endswith("}")
def padding(s,blocklen):
length = (blocklen-len(s)) % blocklen
return s + chr(length) * length
key = os.urandom(16)
iv = os.urandom(16)
m = padding(flag,16)
c = AES.new(key, AES.MODE_CBC, iv).encrypt(m)
print c.encode("hex")
p = getPrime(512)
q = getPrime(512)
n = p*q
e = 65537
m = bytes_to_long(key)
c = pow(m,e,n)
print hex(n).strip("L")
print hex(c).strip("L")
q = getPrime(512)
n = p*q
m = bytes_to_long(iv)
c = pow(m,e,n)
print hex(n).strip("L")
print hex(c).strip("L")
'''
f8559d671b720cd336f2d8518ad6eac8c405585158dfde74ced376ba42d9fe984d519dc185030ddec7b4dc240fd90fa8
0x661d752110bcc6ee5ca33edaf244716cccce6400dfdbfd84ce6ae2d8fbbeb2f61584da7668768403b6135e7810eae9d4d8e044935f8680de5324c3fc0f9bffb01812f9d2ac9055ee8dbd17b90c5a60cb7595a82f24a075d951db3b7f913b8543ecd52b8c8464ce348c3970d511ae911e814f9ca33b8412db2730e61820f5de47
0xd7931796fa39cfa37c0b621c01175904206dff1d74a28369dcd6517957ed76c5eb7d4934cbeb902119f9215f9ae7926debe3abe856244b45dbb4caaa2b93dbb79a3ca1a9813e1466c49fe3c03e5462811afbf3f40ff79927f9fe3681b7f3cef34466b9a736512f4931b5026eefacbae9be6e408085a7a636c514574c3b22ffe
0x9f159326c907441326c88d17eae1c6e8aaea23922c5e628a585294e379e9245644f9c249c57f54a2b83921b4adc988fecc90c00feb6936d9be1f3a5ffae951b74ffbc6fc7aa11743e4ca179a937392dacf931e820d1d83016562ff608e8c59ef7310654a09bbba4a0129f71dcb61bd9bef073bbb93bfcac4a7a2e81156dbb32d
0x6240740d41a539a88634726cf0a791a87e02419c3c3e00dff62eba59e81a93fd04a59109e57f64fc375b9a321583b6fa133317eb5c4e6eb1e6f6d9a0b4ae6ff0c54423718811f7956cd63b7bf9c7f8e29f48dad8f05b63b71d6c5112d91864adba0d6bb342c67aee39ccd5e2a6928a8e4ab2248d29a0c990bae821b31b39b1f3
'''
本题涉及到CBC加密模式和RSA的相关知识。
可以发现参数生成过程中两次使用了相同的p,所以先求是两个n的最大公约数得到p,然后进行RSA基础解密得到key和iv,再进行AES的CBC模式解密即可。
from gmpy2 import*
from Crypto.Util.number import *
from Crypto.Cipher import AES
n1=0x661d752110bcc6ee5ca33edaf244716cccce6400dfdbfd84ce6ae2d8fbbeb2f61584da7668768403b6135e7810eae9d4d8e044935f8680de5324c3fc0f9bffb01812f9d2ac9055ee8dbd17b90c5a60cb7595a82f24a075d951db3b7f913b8543ecd52b8c8464ce348c3970d511ae911e814f9ca33b8412db2730e61820f5de47
n2=0x9f159326c907441326c88d17eae1c6e8aaea23922c5e628a585294e379e9245644f9c249c57f54a2b83921b4adc988fecc90c00feb6936d9be1f3a5ffae951b74ffbc6fc7aa11743e4ca179a937392dacf931e820d1d83016562ff608e8c59ef7310654a09bbba4a0129f71dcb61bd9bef073bbb93bfcac4a7a2e81156dbb32d
p=gcd(n1,n2)
print(p)
p=9540203717217880059997385799331301649727503984010337568404427747385824530958536656147747848448822264268428226235860927158082497191830274046098671199542207
q1=n1//p
q2=n2//p
q1=7516363572069282672026207933559652178219500346572833541898291914709599489663099294873864325874446529790989623154562433650903688205637808143655002021347193
q2=11709684761787285321169271535544195271858094227802343600689046124468724036061594373710783769953414491363505203702886296365059403955548433247809736064179219
c1=0xd7931796fa39cfa37c0b621c01175904206dff1d74a28369dcd6517957ed76c5eb7d4934cbeb902119f9215f9ae7926debe3abe856244b45dbb4caaa2b93dbb79a3ca1a9813e1466c49fe3c03e5462811afbf3f40ff79927f9fe3681b7f3cef34466b9a736512f4931b5026eefacbae9be6e408085a7a636c514574c3b22ffe
c2=0x6240740d41a539a88634726cf0a791a87e02419c3c3e00dff62eba59e81a93fd04a59109e57f64fc375b9a321583b6fa133317eb5c4e6eb1e6f6d9a0b4ae6ff0c54423718811f7956cd63b7bf9c7f8e29f48dad8f05b63b71d6c5112d91864adba0d6bb342c67aee39ccd5e2a6928a8e4ab2248d29a0c990bae821b31b39b1f3
e=65537
d1=inverse(e,(p - 1) * (q1 - 1))
key=pow(c1,d1,n1)
d2=inverse(e,(p - 1) * (q2 - 1))
iv=pow(c2,d2,n2)
c=0xf8559d671b720cd336f2d8518ad6eac8c405585158dfde74ced376ba42d9fe984d519dc185030ddec7b4dc240fd90fa8
key=0x8929ef12a17f145bc9b48afa2c88a2ef
iv=0xf5f6a88b0855ed47e0de771caf3cb23e
key=long_to_bytes(key)
iv=long_to_bytes(iv)
c=long_to_bytes(c)
aes=AES.new(key,AES.MODE_CBC,iv)
print(aes.decrypt(c))
#b'DASCTF{e4f6c51dc2fe722173e41b47533879bc}\x08\x08\x08\x08\x08\x08\x08\x08'
题目链接buu平台
本题用到的是AES的CBC模式。
from Cryptodome.Cipher import AES
import os
import gmpy2
from flag import FLAG
from Cryptodome.Util.number import *
def main():
key=os.urandom(2)*16
iv=os.urandom(16)
print(bytes_to_long(key)^bytes_to_long(iv))
aes=AES.new(key,AES.MODE_CBC,iv)
enc_flag = aes.encrypt(FLAG)
print(enc_flag)
if __name__=="__main__":
main()
key是产生的两个字节的随机比特流重复16次得到的,iv是一个长度为16字节的随机比特流。由key和iv异或的输出结果可得,key的低16字节与iv的16字节异或,高16字节被保留。
key和iv异或的输出结果转为16进制值可以发现重复的字节是b’\xc9\x81’,然后计算iv之后进行AES的CBC模式解密即可。
from Crypto.Util.number import *
from Crypto.Cipher import AES
xor=91144196586662942563895769614300232343026691029427747065707381728622849079757
c=b'\x8c-\xcd\xde\xa7\xe9\x7f.b\x8aKs\xf1\xba\xc75\xc4d\x13\x07\xac\xa4&\xd6\x91\xfe\xf3\x14\x10|\xf8p'
print(hex(xor))
# xor=0xc981c981c981c981c981c981c981c9814eed98e380b1356763849930c850b9cd
key=b'\xc9\x81'*16
iv=(long_to_bytes(xor^bytes_to_long(key)))
aes=AES.new(key,AES.MODE_CBC,iv)
flag=aes.decrypt(c)
print(flag)
#b'actf{W0W_y0u_can_so1v3_AES_now!}'