AES CTR模式与Python和Javascript的奇怪问题
Strange issue with AES CTR mode with Python and Javascript
我正在尝试使用PyCrypto解密CryptoJS创建的密文。我使用AES-256-CTR,具有12字节的随机前缀和4字节计数器。到目前为止,我取得了有限的成功。请阅读这篇我第一次尝试的文章。
- 安装CryptoCat扩展
- 运行CryptoCat
- 启动开发者控制台(Chrome/Firefox中的F12)
- 运行以下代码行
key = 'b1df40bc2e4a1d4e31c50574735e1c909aa3c8fda58eca09bf2681ce4d117e11';
msg = 'LwFUZbKzuarvPR6pmXM2AiYVD2iL0/Ww2gs/9OpcMy+MWasvvzA2UEmRM8dq4loB'ndfPaYOe65JqGQMWoLOTWo1TreBd9vmPUZt72nFs=';
iv = 'gpG388l8rT02vBH4';
opts = {mode: CryptoJS.mode.CTR, iv: CryptoJS.enc.Base64.parse(iv), padding: CryptoJS.pad.NoPadding};
CryptoJS.AES.decrypt(msg, CryptoJS.enc.Hex.parse(key), opts).toString(CryptoJS.enc.Utf8);
期望输出:"Hello, world!ImiAq7aVLlmZDM9RfhDQgPp0CrAyZE0lyzJ6HDq4VoUmIiKUg7i2xpTSPs28USU8"
.
这是我用Python写的一个脚本,部分(!)解密密文:
import struct
import base64
import Crypto.Cipher.AES
import Crypto.Util.Counter
def bytestring_to_int(s):
r = 0
for b in s:
r = r * 256 + ord(b)
return r
class IVCounter(object):
def __init__(self, prefix="", start_val=0):
self.prefix = prefix
self.first = True
self.current_val = start_val
def __call__(self):
if self.first:
self.first = False
else:
self.current_val += 1
postfix = struct.pack(">I", self.current_val)
n = base64.b64decode(self.prefix) + postfix
return n
def decrypt_msg(key, msg, iv):
k = base64.b16decode(key.upper())
ctr = IVCounter(prefix=iv)
#ctr = Crypto.Util.Counter.new(32, prefix=base64.b64decode(iv), initial_value=0, little_endian=False)
aes = Crypto.Cipher.AES.new(k, mode=Crypto.Cipher.AES.MODE_CTR, counter=ctr)
plaintext = aes.decrypt(base64.b64decode(msg))
return plaintext
if __name__ == "__main__":
#original:
key = 'b1df40bc2e4a1d4e31c50574735e1c909aa3c8fda58eca09bf2681ce4d117e11'
msg = 'LwFUZbKzuarvPR6pmXM2AiYVD2iL0/Ww2gs/9OpcMy+MWasvvzA2UEmRM8dq4loB'ndfPaYOe65JqGQMWoLOTWo1TreBd9vmPUZt72nFs='
iv = 'gpG388l8rT02vBH4'
decrypted = decrypt_msg(key, msg, iv)
print "Decrypted message:", repr(decrypt_msg(key, msg, iv))
print decrypted
输出为:
'Hello, world!Imi'xfb+'xf47'x04'xa0'xb1'xa1'xea'xc0I'x03'xec'xc7'x13d'xcf'xe25>l'xdc'xbd'x9f'xa2'x98'x9f$'x13a'xbb'xcb'x13
'xd2#'xc9T'xf4|'xd8'xcb aO)'x94'x9aq<'xa7'x7f'x14'x11'xb5'xb0'xb6'xb5GQ'x92'
问题是,只有前16个字节的输出与预期输出的前16个字节匹配!
Hello, world!ImiAq7aVLlmZDM9RfhDQgPp0CrAyZE0lyzJ6HDq4VoUmIiKUg7i2xpTSPs28USU8
当我修改脚本这样做时:
def __init__(self, prefix="", start_val=1):
和
self.current_val += 0 #do not increment
使计数器每次调用时输出相同的值('x00'x00'x00'x01
),明文为:
'xf2?'xaf:='xc0'xfd'xbb'xdf'xf6h^'x9f'xe8'x16I'xfb+'xf47'x04'xa0'xb1'xa1'xea'xc0I'x03'xec'xc7'x13dQgPp0CrAyZE0lyzJ'xa8'xcd!?h'xc9'xa0'x8b'xb6'x8b'xb3_*'x7f'xf6'xe8'x89'xd5'x83H'xf2'xcd''xc5V'x15'x80k]
其中第二个16字节块(dQgPp0CrAyZE0lyzJ)匹配预期输出。
当我将计数器设置为发出'x00'x00'x00'x02
和'x00'x00'x00'x03
时,我得到了类似的结果——随后的16字节块被显示出来。唯一的例外是,对于0,显示前32个字节。
All 0s: reveals first 32 bytes.
'Hello, world!ImiAq7aVLlmZDM9RfhD'xeb='x93&b'xaf'xaf'x8d'xc9'xdeA'n'xd2'xd8'x01j'x12'x97'xe2i:%}G'x06'x0f'xb7e'x94'xde'x8d'xc83'x8f@'x1e'xa0!'xfa't'xe6'x91'x84Q'xe3'
All 1s: reveals next 16 bytes.
"'xf2?'xaf:='xc0'xfd'xbb'xdf'xf6h^'x9f'xe8'x16I'xfb+'xf47'x04'xa0'xb1'xa1'xea'xc0I'x03'xec'xc7'x13dQgPp0CrAyZE0lyzJ'xa8'xcd!?h'xc9'xa0'x8b'xb6'x8b'xb3_*'x7f'xf6'xe8'x89'xd5'x83H'xf2'xcd''xc5V'x15'x80k]"
All 2s: reveals next 16 bytes.
'l'xba'xcata_2e'x044'xb2J'xe0'xf0'xd7'xc8e'xae'x91yX?~'x7f1'x02'x93'x17'x93'xdf'xd2'xe5'xcf'xe25>l'xdc'xbd'x9f'xa2'x98'x9f$'x13a'xbb'xcb6HDq4VoUmIiKUg7i'x17P'xe6'x06'xaeR'xe8'x1b'x8d'xd7Z'x7f"'
All 3s: reveals next 13 bytes.
'I'x92''&'x9c]'xa9L'xb1'xb6'xbb`'xfa'xbet;@'x86'x07+'xa5='xe5V'x84'x80'x9a='x89'x91q'x16'xea'xca'xa3l'x91'xde&'xb6'x17'x1a'x96'x0e't/'x188'x13`'xd2#'xc9T'xf4|'xd8'xcb`aO)'x94'x9a2xpTSPs28USU8'
如果您连接"正确"的块,您将得到预期的明文:
Hello, world!ImiAq7aVLlmZDM9RfhDQgPp0CrAyZE0lyzJ6HDq4VoUmIiKUg7i2xpTSPs28USU8
这真的很奇怪。我肯定在Python端做错了什么,因为东西可以被解密,但不是一次全部解密。如果有人能帮忙,我会非常感激。谢谢你。
这里有几个问题。首先,消息不是块大小的倍数,并且您没有使用填充。其次,也是这个问题最关键的一点是,静脉注射的大小也不合适。应该是16个字节,但你只有12个。可能这两个实现都应该失败并出现异常,在CryptoJS的下一个主要版本中,情况将会是这样。
以下是由于这个错误导致的结果:当计数器第一次增加时,它试图增加未定义的值,因为IV的最后一个字节丢失了。Undefined + 1是NaN, NaN | 0是0。这就是为什么你得到两次0当使用加密模式CryptoJS.mode.CTR
(其中CTR代表计数器)时,初始化向量与计数器一起被加密,然后应用于要加密的数据。这是为您加密的每个数据块执行的。
你解释说,当你对start_val
应用不同的值时,消息的不同部分被正确解密,所以我怀疑计数器只是没有正确地随着块的每次解密而增加。
看看维基百科上的分组密码模式:点击率
注意:请注意,当使用CTR模式时,初始化向量+计数器的组合不应该重复。
修复。我只是让计数器两次以0开始。有人知道这是不是一个漏洞吗?
import struct
import base64
import Crypto.Cipher.AES
import Crypto.Util.Counter
import pdb
def bytestring_to_int(s):
r = 0
for b in s:
r = r * 256 + ord(b)
return r
class IVCounter(object):
def __init__(self, prefix="", start_val=0):
self.prefix = prefix
self.zeroth = True
self.first = False
self.current_val = start_val
def __call__(self):
if self.zeroth:
self.zeroth = False
self.first = True
elif self.first:
self.first = False
else:
self.current_val += 1
postfix = struct.pack(">I", self.current_val)
n = base64.b64decode(self.prefix) + postfix
return n
def decrypt_msg(key, msg, iv):
k = base64.b16decode(key.upper())
ctr = IVCounter(prefix=iv)
#ctr = Crypto.Util.Counter.new(32, prefix=base64.b64decode(iv), initial_value=0, little_endian=False)
aes = Crypto.Cipher.AES.new(k, mode=Crypto.Cipher.AES.MODE_CTR, counter=ctr)
plaintext = aes.decrypt(base64.b64decode(msg))
return plaintext
if __name__ == "__main__":
#original:
key = 'b1df40bc2e4a1d4e31c50574735e1c909aa3c8fda58eca09bf2681ce4d117e11'
msg = 'LwFUZbKzuarvPR6pmXM2AiYVD2iL0/Ww2gs/9OpcMy+MWasvvzA2UEmRM8dq4loB'ndfPaYOe65JqGQMWoLOTWo1TreBd9vmPUZt72nFs='
iv = 'gpG388l8rT02vBH4'
decrypted = decrypt_msg(key, msg, iv)
print "Decrypted message:", repr(decrypt_msg(key, msg, iv))
print decrypted
- 我不知道我的编码有什么问题.(JavaScript)
- 登录后重定向,缓存页面问题-Javascript
- 在我的网站上创建一个在1-10之间不断变化的数字时遇到了问题.Javascript
- 浮点数字问题JavaScript
- 局部全局变量问题 - JavaScript
- 内容滑块问题 javascript
- 多个脚本导致链接问题?Javascript、CSS、HTML、Jquery
- 基本范围问题(javascript和node)
- 表单验证问题(Javascript)
- 表杂乱无章的问题.Javascript HTML5.
- 我的代码有什么问题.JavaScript幻灯片
- 这段代码有什么问题?(JavaScript)
- 浏览器和移动设备的视差背景问题(javascript/jquery)
- Mozilla中的setInterval()问题(Javascript函数)
- While循环问题-JavaScript-jQuery.Clone()
- 检查密码匹配问题(JavaScript)
- 抓取选中单选按钮的问题- JavaScript
- 基于下拉选择禁用文本字段的问题(JavaScript)
- 比较日期问题- javascript
- 浏览器兼容性问题Javascript