【问题标题】:Bytes to string in AES encryption and decryption in Python 3Python 3中AES加密和解密中的字节到字符串
【发布时间】:2018-11-02 00:42:54
【问题描述】:

我想对字符串进行 AES 加密和解密。但是密钥和消息必须以字节为单位,所以我通过这样做将消息转换为字节:

b"string"

这是我的 AES 代码:

# Encryption
encryption_suite = AES.new(b'1234567812345678', AES.MODE_OCB)
cipher_text = encryption_suite.encrypt(b"A really secret message. Not for prying eyes.")

# Decryption
decryption_suite = AES.new(b'1234567812345678', AES.MODE_OCB)
plaintext = decryption_suite.decrypt(cipher_text)

但是我需要将解密的纯文本转回字符串才能读取。 目前明文如下所示:

b'x\x85\x92\x9d\xe6\x0bJ\xfe\x9b(\x10G\x8e\x05\xc5\xf4\xcdA9\xc18\xb8_\xf9vbmK\x16\xf8\xa3\xb6'

我尝试过使用

plaintext.decode(encoding='windows-1252')

plaintext.decode("utf-8").strip('\x00')

但我得到的只是这个:

UnicodeDecodeError:“charmap”编解码器无法解码位置 3 的字节 0x9d:字符映射到

或者这个:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb1 in position 1: invalid start byte

我需要将这些字节转换回可读字符串。如果您能提供帮助,将不胜感激。

【问题讨论】:

  • 这个问题很有帮助,但我不明白如何将它应用到我的案例中。还有很多答案是 python 2 stackoverflow.com/questions/1220751/…
  • 嗨,如果您从上面的代码中取回纯文本,那么您的脚本中有问题。明文应该看起来与您在开始时用作输入的疼痛文本相同......所以您应该得到
  • @ZaphoOxx 如您所见,加密和解密方法是预定义的,因此我确信输出是正确的。问题是输入必须看起来像那些字节才能使函数工作,所以自然输出看起来也是这样。
  • 您使用哪个模块进行加密?它看起来像 pycrypto,但我有 pycrypto 2.6.1,它不支持 AES.MODE_OCB。
  • 说真的,如果初始输入和解密输出不匹配,你怎么能认为它有效?想想看。 b“我的消息”已加密。一旦你解密了密码,你应该得到完全相同的结果。否则整个加密没有意义。是吗?

标签: python python-3.x encryption type-conversion


【解决方案1】:

您的代码的主要问题是您没有向AES.new() 提供随机数。 OCB 需要一个随机数;如果您不提供一个随机数,则每次创建新的 AES 对象时都会创建一个随机数,因此解密将失败。

来自docs

nonce(字节串):一个不可重复的值,长度在 1 到 15 个字节之间。如果不存在,将创建推荐长度(15 个字节)的随机 nonce。

您有两个选择,要么创建一个 nonce 并将其传递给 AES.new()(在加密和解密中),要么使用 AES 在加密期间创建的随机 nonce。

接下来,OCB 是一种经过身份验证的加密算法,但您似乎没有检查 MAC。这很重要,因为 MAC 验证密文的完整性。

AES的加解密方法接受和返回字节。您可以使用.decode() 将纯文本(如果是文本)转换为字符串。如果要将密文转换为字符串,则必须首先对其进行 base64 编码,以便将原始字节编码为 ASCII 字符(请记住在解密之前进行解码)。 b64encode() 也返回字节,但可以轻松转换为字符串。

一个例子,

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from base64 import b64encode

key = get_random_bytes(16) # A 16 byte key for AES-128
nonce = get_random_bytes(15)
message = "A really secret message. Not for prying eyes.".encode()

cipher = AES.new(key, AES.MODE_OCB, nonce=nonce)
ciphertext, mac = cipher.encrypt_and_digest(message)

cipher = AES.new(key, AES.MODE_OCB, nonce=nonce)
plaintext = cipher.decrypt_and_verify(ciphertext, mac)

print(b64encode(ciphertext).decode())
#CSwHy3ir3MZ7yvZ4CzHbgYOsKgzhMqjq6wEuutU7vJJTJ0c38ExWkAY1QkLO
print(plaintext.decode())
#A really secret message. Not for prying eyes.

请注意,如果.decrypt_and_verify() 无法验证 MAC,则会引发ValueError 异常,因此您可能需要使用 try-except 块。 noncemac 值不是秘密的,将它们存储在密文旁边是安全的。

最后,如果您计划从密码短语派生密钥,则应使用基于密码的 KDF。 KDFs 创建强密钥,使用盐和迭代,并且它们对 fruteforce 攻击具有很强的抵抗力。您将在Crypto.Protocol.KDF 中找到 KDF 函数。

【讨论】:

  • 谢谢你的工作!如果你能帮忙,我还有另一个问题:你知道我怎样才能摆脱 "b' ' " 的字符串吗?我试过 string[2: ] 但它只是删除了部分字符串本身而不是 b
  • b'' 表示您的字符串是二进制的,它不是实际字符串的一部分。如果要将解密后的明文转换为 unicode 字符串,请使用 decode。请参阅更新的代码。如果要将密文转换为字符串,则必须先对其进行base64编码。
  • 嗨@t.m.adam,希望你一切都好。如果您愿意提供任何解决方案,我会很高兴this post。谢谢。
【解决方案2】:

您不能将二进制数据转换为字符串,因为它本质上不是字符串。是二进制数据。您的加密输出只是偶然发生正确格式的 UTF8 字符串的可能性很小。

您应该改用 base64。它会使数据膨胀(3 个字节到 4 个字符),但更适合您的用例。

编辑:。我的错误,我误解了你的问题。您的输出不正确,第一个字节不是 UTF8 中的“1”。这可能是加密/解密的问题。

【讨论】:

  • 感谢您的回复.. 我不是要转换密文,只转换解密后的纯文本。
  • 我试过base64,但它的输出是这样的:ZGF0YSB0byBiZSBlbmNvZGVk。 aes不接受的
【解决方案3】:

我认为您的编码是“ISO-8859-1”。所以你可以这样做:

plaintext.decode("ISO-8859-1")

【讨论】:

    猜你喜欢
    • 2016-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多