AES-CTR加密模式介绍 & 例题

CTR(Counter mode,CTR)计数器模式

原理: CTR将块密码变为流密码。它通过递增一个加密计数器以产生连续的密钥流,其中,计数器可以是任意保证长时间不产生重复输出的函数。AES是一种分组加密模式,其中CTR(计数模式)是其中一种工作模式。

加密原理:用密钥对输入的计数器加密,然后同明文异或得到密文。
解密原理:用密钥对输入计数器加密,然后同密文异或得到明文。
⋆ ⋆ ⋆ 从图中可以看出对计数器的加密 ( 即从 C o u n t e r ⇒ K 1 的过程 ) 采用的是 A E S − E C B 加密模式 \star\star\star 从图中可以看出对计数器的加密(即从Counter\Rightarrow K1的过程)采用的是AES-ECB加密模式 从图中可以看出对计数器的加密(即从CounterK1的过程)采用的是AESECB加密模式

♠ C T R 模式不需要 p a d d i n g ,故其加密后的密文长度是可知的 \spadesuit CTR模式不需要padding,故其加密后的密文长度是可知的 CTR模式不需要padding,故其加密后的密文长度是可知的
♠ 从图中可以看出对计数器的加密 ( 即从 C o u n t e r ⇒ K 1 的过程 ) 采用的是 A E S − E C B 加密模式 \spadesuit 从图中可以看出对计数器的加密(即从Counter\Rightarrow K1的过程)采用的是AES-ECB加密模式 从图中可以看出对计数器的加密(即从CounterK1的过程)采用的是AESECB加密模式
在这里插入图片描述
举个生成的例子来理解

ctr = Counter.new(AES.block_size * 8, initial_value=initial_value)
aes = AES.new(key, counter=ctr, mode=AES.MODE_CTR)
enc = aes.encrypt(msg)
  • AES.block_size 用于表示 AES 算法的分组大小(以字节为单位)的常量值。
  • AES 算法支持三种分组大小:16 字节(128 bits)、24 字节(192 bits)和 32 字节(bits 位)。分组大小的选择取决于所使用的 AES 密钥长度
  • 无指定情况下AES.block_size 的值为 16,表示使用 128 bits的分组大小
  • initial_value用于表示计数器的初始值,每使用一次,计数器值加1,确保每次不一样,从而不易被攻击,例如:

initial_value:b’\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08’
Counter value1: b’\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08’
Counter value2: b’\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\t’
Counter value3: b’\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\n’
Counter value4: b’\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x0b’
Counter value5: b’\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x0c’

以此类推,每次加1(转为long型加1,再转为bytes型)

题目一

题目描述:

from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Util import Counter
from hashlib import sha256
from secret import flag

import os


def padding(msg):
    return msg + os.urandom(16 - len(msg) % 16)


msg = b"where is the flag? Key in my Heart/Counter!!!!"
key = b"I w0nder how????"

assert len(msg) == 46
assert len(key) == 16

enc_key = os.urandom(16)
initial_value = bytes_to_long(enc_key)
hash = sha256(str(initial_value).encode()).hexdigest()

aes = AES.new(enc_key, AES.MODE_ECB)
enc_flag = aes.encrypt(padding(flag))

ctr = Counter.new(AES.block_size * 8, initial_value=initial_value)
aes = AES.new(key, counter=ctr, mode=AES.MODE_CTR)
enc = aes.encrypt(msg)

print("enc = {}".format(enc[-16:]))
print("enc_flag = {}".format(enc_flag))
print("hash = {}".format(hash))

'''
enc = b'\xbe\x9bd\xc6\xd4=\x8c\xe4\x95bi\xbc\xe01\x0e\xb8'
enc_flag = b'\xb2\x97\x83\x1dB\x13\x9b\xc2\x97\x9a\xa6+M\x19\xd74\xd2-\xc0\xb6\xba\xe8ZE\x0b:\x14\xed\xec!\xa1\x92\xdfZ\xb0\xbd\xb4M\xb1\x14\xea\xd8\xee\xbf\x83\x16g\xfa'
hash = efb07225b3f1993113e104757210261083c79de50f577b3f0564368ee7b25eeb
'''

题目分析:

在这里插入图片描述
由流程可知,明文msg和密文enc_flag已知,那么两者异或就能得到K3(分三组:16 16 14,不需填充)
但此K3非彼K3,此K3是14字节,实际上K3是16字节,所以要得到真正的K3得进行填充
注意这里是往后填充,不是往前填充
(从流程图确实很容易想到是往前填充。但此处是CRT库里面的自己实现的,类似于规定一样,测试出来的数据确实也表明是往后填充,我想这应该也就是所说的 C T R 模式不需要 p a d d i n g CTR模式不需要padding CTR模式不需要padding的原因吧)
在末尾爆破两字节得到真正的K3,然后-2即可得到初始Counter,即initial_value
initial_value出来了,那么flag差不多也就出来了

from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Util import Counter
from hashlib import sha256

msg = b"where is the flag? Key in my Heart/Counter!!!!"
key = b"I w0nder how????"
enc = b'\xbe\x9bd\xc6\xd4=\x8c\xe4\x95bi\xbc\xe01\x0e\xb8'
enc_flag = b'\xb2\x97\x83\x1dB\x13\x9b\xc2\x97\x9a\xa6+M\x19\xd74\xd2-\xc0\xb6\xba\xe8ZE\x0b:\x14\xed\xec!\xa1\x92\xdfZ\xb0\xbd\xb4M\xb1\x14\xea\xd8\xee\xbf\x83\x16g\xfa'
hash = 'efb07225b3f1993113e104757210261083c79de50f577b3f0564368ee7b25eeb'

a = msg[32:]
b = enc[2:]
enc_Counter1 = bytes(a[i] ^ b[i] for i in range(14))
for i in range(256):
    for j in range(256):
        enc_Counter2 = enc_Counter1 + bytes([i]) + bytes([j])
        aes = AES.new(key,AES.MODE_ECB)
        Counter = aes.decrypt(enc_Counter2)
        initial_value = bytes_to_long(Counter) - 2
        if hash == sha256(str(initial_value).encode()).hexdigest():
            enc_key = long_to_bytes(initial_value)
            aes = AES.new(enc_key,AES.MODE_ECB)
            flag = aes.decrypt(enc_flag)
            print(flag)
            break
# flag{9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d}

浅记一下

学到了AES-CTR加密模式,往后填充哪里确实需要我们记住
还有,当时看了很多资料,发现大家都是这样说的(引用一下):

计数器的生成方法

每次加密时都会生成一个不同的值(nonce)来作为计数器的初始值。当分组长度为128比特(16字节)时,计数器的初始值可能是像下面这样的形式
在这里插入图片描述
其中前8个字节为nonce(随机数),这个值在每次加密时必须都是不同的,后8个字节为分组序号,这个部分是会逐次累加的。在加密的过程中,计数器的值会产生如下变化:在这里插入图片描述

这应该是初始值只有8字节的时候吧
如果初始值就是16字节,那Counter value1的值就是初始值,之后的Counter value便是每次在初始值后加1,也就没有所谓的后8字节为分组序号组这一说法,就像上面讲原理是所介绍的那个例子。我理解的大致是这样,因为遇到的题目确实是按这个想法才能解。
学艺不精,还有待提升,继续加油!

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值