我正在使用Laravel的encryptString方法对网站上的某些数据进行加密。它使用OpenSSL的256位AES-CBC加密,无需任何序列化。我现在正在尝试用Python解密这些数据,但是我一直在收到有关密钥长度的错误,而且似乎无法弄清原因。
Example data to decrypt: eyJpdiI6ImdxY0VcLzFodmpISFV4allSWmJDdEpRPT0iLCJ2YWx1ZSI6IkxXd0ZJaUd2bTUweW5pNm0wUjQwOFM2N1wvWEs5SlYrNB4xNlR7Qkh1U3FvPSIsIm1hYyI6Ijc5ZWM0YTYxYjljZGFiNzgwNjY2NDU1ZmQ5Yjc1ZmJlOGU4NzBkMjQzMzA3MmVhYzE3NzY4ZmU1MWIyMjZlOTQifQ==
Example Key to use for decryption (from laravel .env):
base64:/AZejP0lh3McL/+Vy5yZcADdTcR65qnx5Jqinuw7raK=
我更改了这些值,因此使用这些值进行实际解密将不会提供任何真实数据,只是认为这会很好。然后,我尝试使用以下命令在Python 3.7中解密此数据:
import base64
from Crypto.Cipher import AES
def decrypt(enc, key):
IV = 16 * '\x00'
decobj = AES.new(key, AES.MODE_CBC, IV)
data = decobj.decrypt(base64.b64decode(enc))
print(str(data.decode()))
if __name__ == "__main__":
key = b"/AZejP0lh3McL/+Vy5yZcADdTcR65qnx5Jqinuw7raK="
decrypt("eyJpdiI6ImdxY0VcLzFodmpISFV4allSWmJDdEpRPT0iLCJ2YWx1ZSI6IkxXd0ZJaUd2bTUweW5pNm0wUjQwOFM2N1wvWEs5SlYrNB4xNlR7Qkh1U3FvPSIsIm1hYyI6Ijc5ZWM0YTYxYjljZGFiNzgwNjY2NDU1ZmQ5Yjc1ZmJlOGU4NzBkMjQzMzA3MmVhYzE3NzY4ZmU1MWIyMjZlOTQifQ==", key)
看来这应该可行,但是当我运行它时,出现错误:ValueError: Incorrect AES key length (60 bytes)
,所以我不确定自己在做什么错。我试过填充/取消填充数据/密钥,但这似乎并没有改变任何东西。我想知道我是否从Laravel获取了用于解密的错误密钥,但是根据链接文档中的信息,它应该只是我的.env文件中的APP_KEY。
如果有人可以帮助我或向正确的方向指出,那真是太神奇了!
这个问题是其他类似问题所独有的,因为我主要是想从Laravel获得正确的AES密钥,实际上我并不需要过多的解密帮助,我只是想抓住Laravel提供了错误的密钥。
编辑:看起来可行的新代码:
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
def decrypt(enc, key):
IV = 16 * '\x00'.encode()
decobj = AES.new(key, AES.MODE_CBC, IV)
data = decobj.decrypt(pad(base64.b64decode(enc), 16))
print(base64.b64decode(data))
if __name__ == "__main__":
key = base64.b64decode(b"/AZejP0lh3McL/+Vy5yZcADdTcR65qnx5Jqinuw7raK=")
decrypt("eyJpdiI6ImdxY0VcLzFodmpISFV4allSWmJDdEpRPT0iLCJ2YWx1ZSI6IkxXd0ZJaUd2bTUweW5pNm0wUjQwOFM2N1wvWEs5SlYrNB4xNlR7Qkh1U3FvPSIsIm1hYyI6Ijc5ZWM0YTYxYjljZGFiNzgwNjY2NDU1ZmQ5Yjc1ZmJlOGU4NzBkMjQzMzA3MmVhYzE3NzY4ZmU1MWIyMjZlOTQifQ==", key)
现在,print语句会打印一些字节,但是当我在其上运行.decode()时,出现错误:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfa in position 0: invalid start byte
,似乎无法弄清楚我需要做些什么才能使其能够被打印为字符串。
答案 0 :(得分:2)
问题:...试图在Python中解密该数据,但始终收到有关密钥长度的错误
做完key
之后,我可以在链接答案的代码中使用您的.b64decode(...
。
示例代码.encode(...
和decode(...
可以正常工作。
结论:您的密钥没有错!
key = b"/AZejP0lh3McL/+Vy5yZcADdTcR65qnx5Jqinuw7raK="
key = base64.b64decode(key)
但是有了您的代码,我得到了与
IV
参数有关的 TypeError :expect_byte_string(iv) File "/usr/local/lib/python3.4/dist-packages/Crypto/Util/_raw_api.py", line 172, in expect_byte_string TypeError: Only byte strings can be passed to C code
已修复
IV = 16 * '\x00'.encode()
,导致与<enc
相关的 ValueError :data = decobj.decrypt(base64.b64decode(enc)) File "/usr/local/lib/python3.4/dist-packages/Crypto/Cipher/_mode_cbc.py", line 209, in decrypt ValueError: Error 3 while decrypting in CBC mode
错误3表示“ ERR_NOT_ENOUGH_DATA”
根据链接的GitHub页面,您必须重新阅读文档,有关填充数据,同时编码。
来自GitHub的工作示例:
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
key = b"/AZejP0lh3McL/+Vy5yZcADdTcR65qnx5Jqinuw7raK="
key = base64.b64decode(key)
BLOCK_SIZE = 32
encryption_suite = AES.new(key, AES.MODE_CBC, b'This is an IV...')
cipher_text = encryption_suite.encrypt(pad(b'A really secret message...', BLOCK_SIZE))
decryption_suite = AES.new(key, AES.MODE_CBC, b'This is an IV...')
print(unpad(decryption_suite.decrypt(cipher_text), BLOCK_SIZE).decode())
>>> A really secret message...
使用Python测试:3.4.2
答案 1 :(得分:1)
我注意到这已经有一段时间没有活跃了,但是我正在尝试做同样的事情,并且似乎无法使其工作。
我注意到的是Laravel存储的编码密码字符串是一个以64为基数的json对象,在最初的问题中并未考虑到这一点:
pass_obj = base64.b64decode('eyJpdiI6ImdxY0VcLzFodmpISFV4allSWmJDdEpRPT0iLCJ2YWx1ZSI6IkxXd0ZJaUd2bTUweW5pNm0wUjQwOFM2N1wvWEs5SlYrNB4xNlR7Qkh1U3FvPSIsIm1hYyI6Ijc5ZWM0YTYxYjljZGFiNzgwNjY2NDU1ZmQ5Yjc1ZmJlOGU4NzBkMjQzMzA3MmVhYzE3NzY4ZmU1MWIyMjZlOTQifQ==')
print(pass_obj)
>>> b'{"iv":"gqcE\\/1hvjHHUxjYRZbCtJQ==","value":"LWwFIiGvm50yni6m0R408S67\\/XK9JV+4\x1e16T{BHuSqo=","mac":"79ec4a61b9cdab780666455fd9b75fbe8e870d2433072eac17768fe51b226e94"}'
据此,您可以获得IV和加密值,两者似乎都是base64编码的。但最后还是会收到解码错误,就像;
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfa in position 0: invalid start byte
这是我完整的代码;
password = 'eyJpdiI6ImJGNDNNZjN3YWtpcDQ5VEJVXC9IazF3PT0iLCJ2YWx1ZSI6IkNVRW1VQUY1dXArYlFkU3NlY1pnZUE9PSIsIm1hYyI6ImM3ODk0NWQ0NjgxMzM4YjE0M2JhN2MzZWRmOWEwMWJiMjI2Y2FhYmUxYjFhYzAyYjY4YWZkZGE3N2EyMDYwNWYifQ=='
key = 'some secret key that i can't share'.encode()
p_obj = json.loads(base64.b64decode(password).decode())
decobj = AES.new(key, AES.MODE_CBC, base64.b64decode(p_obj['iv']))
data = decobj.decrypt(base64.b64decode(p_obj['value']))
print(data)
>>> b'l\xee:f\x9eZ\x90rP\x99\xca&@\x1d1\x9f'
data.decode()
>>> Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xee in position 1: invalid continuation byte
@Pecans你有没有发现?
谢谢。
我弄错了,最初我的钥匙有问题。因此,这是完整的代码段,以供将来参考;
import base64
import json
from phpserialize import unserialize
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
key = b'my secret key'
enc_pass = 'eyJpdiI6ImJGNDNNZjN3YWtpcDQ5VEJVXC9IazF3PT0iLCJ2YWx1ZSI6IkNVRW1VQUY1dXArYlFkU3NlY1pnZUE9PSIsIm1hYyI6ImM3ODk0NWQ0NjgxMzM4YjE0M2JhN2MzZWRmOWEwMWJiMjI2Y2FhYmUxYjFhYzAyYjY4YWZkZGE3N2EyMDYwNWYifQ=='
p_obj = json.loads(base64.b64decode(password).decode())
decobj = AES.new(key, AES.MODE_CBC, base64.b64decode(p_obj['iv']))
data = decobj.decrypt(base64.b64decode(p_obj['value']))
dec_pass = unserialize(unpad(data, 16)).decode()
您将在dec_pass中拥有解密的密码。
请注意,有时Laravel在base64中生成密钥。在这种情况下,字符串将类似于base64:sdfsdjfhjsdf32
,那么您必须先解码。
干杯!
答案 2 :(得分:1)
具有MAC验证的完整工作代码
除了Gonzalo的提交内容外,Laravel的加密消息是一个base64 json编码的数组,由以下密钥对组成:
[
'iv' => 'generated initialization vector (iv)',
'value' => 'encrypted, base64ed, signed value',
'mac' => 'message authentication code (mac)'
]
使用消息身份验证代码(MAC)对“值”进行签名,以验证该值在传输过程中没有更改。
从负载中提取的MAC(加密消息)应根据 从“值”中提取的mac(可以使用键iv和值完成此操作)。 Laravel的加密方案可以在GitHub上找到:src/Illuminate/Encryption/Encrypter.php
参考关于Laravel的讨论thread,我在Github上找到了部分解决方案:orian/crypt.py(是fideloper/crypt.py的分支)。
我已经放弃了Orian的代码并修复了输入参数问题。只要加密密钥(作为解密()的输入传递)被解密,并且不包含通常在.env中的APP_KEY环境变量字符串赋值的“ base64:”,该代码即可按预期工作文件。
crypt.py的代码片段如下所示:
import base64
import json
from Crypto.Cipher import AES
from phpserialize import loads
import hashlib
import hmac
def decrypt(payload, key):
"""
Decrypt strings that have been encrypted using Laravel's encrypter (AES-256 encryption).
Plain text is encrypted in Laravel using the following code:
>>> ciphertext = Crypt::encrypt('hello world');
The ciphertext is a base64's json-encoded array consisting of the following keys:
[
'iv' => 'generated initialization vector (iv)',
'value' => 'encrypted, base64ed, signed value',
'mac' => 'message authentication code (mac)'
]
The 'value' is signed using a message authentication code (MAC) so verify that the value has not changed during
transit.
Parameters:
payload (str): Laravel encrypted text.
key (str): Encryption key (base64 decoded). Make sure 'base64:' has been removed from string.
Returns:
str: plaintext
"""
data = json.loads(base64.b64decode(payload))
if not valid_mac(key, data):
return None
value = base64.b64decode(data['value'])
iv = base64.b64decode(data['iv'])
return unserialize(mcrypt_decrypt(value, iv, key)).decode("utf-8")
def mcrypt_decrypt(value, iv, key):
AES.key_size=128
crypt_object=AES.new(key=key,mode=AES.MODE_CBC,IV=iv)
return crypt_object.decrypt(value)
def unserialize(serialized):
return loads(serialized)
def valid_mac(key, data):
dig = hmac.new(key, digestmod=hashlib.sha256)
dig.update(data['iv'].encode('utf8'))
dig.update(data['value'].encode('utf8'))
dig = dig.hexdigest()
return dig==data['mac']