我正在尝试使用PyCrypto库解密一些AES-CTR-256数据。密文是由Cryptocat多方聊天Javascript代码生成的,它依赖于CryptoJS库。 IV方案如Cryptocat Multiparty Protocol Specification中所述:
初始化向量(IV)由16个字节组成:12个字节 随机生成,4个字节作为计数器,递增 每个街区一次。
(12个随机字节位于4个计数器字节之前。)
这是我的Python代码:
import struct
import base64
import Crypto.Cipher.AES
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="", iv="\x00\x00\x00\x00"):
self.prefix = prefix
self.initial_value = iv
def increment(self, b):
if b == "\xff\xff\xff\xff":
raise ValueError("Reached the counter limit")
return struct.pack(">I", bytestring_to_int(b)+1)
def __call__(self):
self.initial_value = self.increment(self.initial_value)
n = base64.b64decode(self.prefix) + self.initial_value
return n
def decrypt_msg(key, msg, iv):
k = base64.b16decode(key.upper())
ctr = IVCounter(prefix=iv)
aes = Crypto.Cipher.AES.new(k, Crypto.Cipher.AES.MODE_CTR, counter=ctr)
plaintext = aes.decrypt(msg)
return plaintext
if __name__ == "__main__":
key = 'b1df40bc2e4a1d4e31c50574735e1c909aa3c8fda58eca09bf2681ce4d117e11'
msg = 'LwFUZbKzuarvPR6pmXM2AiYVD2iL0/Ww2gs/9OpcMy+MWasvvzA2UEmRM8dq4loB\ndfPaYOe65JqGQMWoLOTWo1TreBd9vmPUZt72nFs='
iv = 'gpG388l8rT02vBH4'
plaintext = decrypt_msg(key, msg, iv)
print plaintext
这就是如何在Javascript中做同样的事情:
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"
。正如所料,这适用于Javascript。
然而,Python代码输出乱码。 repr(明文)给出:
'\x91I\xbd\n\xd5\x11\x0fkE\xaa\x04\x81V\xc9\x16;.\xe3\xd3#\x92\x85\xd2\x99\xaf;\xc5\xafI\xac\xb6\xbdT\xf4{l\x17\xa1`\x85\x13\xf2\x8e\x844\xac1OS\xad\x9eZ<\xea\xbb6\x9dS\xd5\xbc\xfd\xc4\r\xf94Y~\xaf\xf3\xe0I\xad\xa6.\xfa\x7f\xf8U\x16\x0e\x85\x82\x8c\x8e\x04\xcb,X\x8b\xf7\xef\xb2\xc2\xe3~\xf1\x80\x08L\x8b \x9f\xaf\x0e\x0b'
我不确定为什么会这样。我很确定我的IVCounter实现与JS代码使用的方案相匹配。可能是没有Python等效的CryptoJS NoPadding选项吗?我很难过。
提前感谢您的帮助!
答案 0 :(得分:0)
我想你想看看How come I can't decrypted my AES encrypted message on someone elses AES decryptor?
您使用python解密的方式需要根据该问题的答案进行修改。
答案 1 :(得分:0)
以下是更正有效的Python脚本!
编辑:不是真的 - 只显示明文的前16个字节。我将继续努力。
import struct
import base64
import Crypto.Cipher.AES
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="", iv="\x00\x00\x00\x00"):
self.prefix = prefix
self.initial_value = iv
self.first = True
def increment(self, b):
if b == "\xff\xff\xff\xff":
raise ValueError("Reached the counter limit")
if self.first:
return struct.pack(">I", bytestring_to_int(b))
else:
self.first = False
return struct.pack(">I", bytestring_to_int(b)+1)
def __call__(self):
self.initial_value = self.increment(self.initial_value)
n = base64.b64decode(self.prefix) + self.initial_value
return n
def decrypt_msg(key, msg, iv):
k = base64.b16decode(key.upper())
ctr = IVCounter(prefix=iv)
aes = Crypto.Cipher.AES.new(k, Crypto.Cipher.AES.MODE_CTR, counter=ctr)
plaintext = aes.decrypt(base64.b64decode(msg))
return plaintext
if __name__ == "__main__":
key = 'b1df40bc2e4a1d4e31c50574735e1c909aa3c8fda58eca09bf2681ce4d117e11'
msg = 'LwFUZbKzuarvPR6pmXM2AiYVD2iL0/Ww2gs/9OpcMy+MWasvvzA2UEmRM8dq4loB\ndfPaYOe65JqGQMWoLOTWo1TreBd9vmPUZt72nFs='
iv = 'gpG388l8rT02vBH4'
print decrypt_msg(key, msg, iv)
print "Decrypted message:", repr(decrypt_msg(key, msg, iv))
答案 2 :(得分:0)
我增加计数器的方式有问题。显然,正确的计数器值应用于错误的位置。这是一个解决问题的后续问题。 Strange issue with AES CTR mode with Python and Javascript