【问题标题】:How do I decode utf-8 from a file如何从文件中解码 utf-8
【发布时间】:2019-05-10 11:14:15
【问题描述】:

我有一个程序尝试使用 aes 加密消息。当我必须加密消息并得到TypeError: Object type <class 'str'> cannot be passed to C code 时,就会出现问题。我发现如果我将它编码为 utf-8 它可以工作,但是当我尝试解密它时它并没有摆脱 b'...' 并且 base64 解密失败,使我的 iv 不是 16 个字节。每当我尝试使用 aes.decrypt(file.readline().decode("utf-8")) 解码文件的第一行时,它都会说我不能在 str 上使用解码。

from Crypto.Cipher import AES
from Crypto import Random

def pad(s):
    pad = s + (16 - len(s) % 16) * chr(16 - len(s) % 16)
    return str(pad)

def unpad(s):
    unpad = s[:-ord(s[len(s)-1:])]
    return str(unpad)


class AESCipher:
    def __init__( self, key ):
    self.key = key

    def encrypt( self, s ):
        raw = pad(s)
        iv = Random.new().read( AES.block_size )
        cipher = AES.new( self.key, AES.MODE_CBC, iv )
        return base64.b64encode( iv + cipher.encrypt( raw.encode("utf-8") ) )

    def decrypt( self, enc ):
        enc = base64.b64decode(enc)
        iv = enc[:16]
        cipher = AES.new(self.key, AES.MODE_CBC, iv )
        return unpad(cipher.decrypt( enc[16:] ))

我是加密新手,所以我真的不知道以前是否已经回答过这个问题,我只是不知道如何措辞,但我已经四处寻找了几个小时并没有找到任何事物。 谢谢你。 再次抱歉,如果措辞不当。

【问题讨论】:

  • 在加密/解密数据时应始终使用字节。然后您可以解码为字符串(如果是纯文本),或 base-46 编码(如果是密文)。 b'' 表示一个字节字符串,它不是实际字符串的一部分,它只是字符串的表示形式。如果您解码为字符串 (b''.decode()),您可以“摆脱它”。请记住,'s'.encode() -> 字节,b's'.decode() -> 字符串。
  • padunpad 定义在哪里?始终在示例代码中包含非标准(或非显而易见)函数的定义。
  • def pad(s): pad = s + (16 - len(s) % 16) * chr(16 - len(s) % 16) return str(pad) def unpad(s): unpad = s[:-ord(s[len(s)-1:])] return str(unpad)
  • @Tomalak 有pad和unpad功能。
  • @t.m.adam 问题是当我从文件中读取时,它的行为就像是“b's”的文字字符串,并且不允许我对其进行解码。

标签: python-3.x encryption python-unicode unicode-string


【解决方案1】:

您的加密和解密操作不是彼此的镜像。

def encrypt( self, s ):
    iv = Random.new().read( AES.block_size )       # new IV
    cipher = AES.new( self.key, AES.MODE_CBC, iv ) # create cipher
    payload = s.encode("utf-8")                    # string to bytes
    encrypted = cipher.encrypt(pad(payload))       # pad before encrypt
    return base64.b64encode( iv + encrypted )      # b64 data

def decrypt( self, enc ):
    data = base64.b64decode( enc )                 # b64 data
    iv = data[:AES.block_size]                     # split it up
    encrypted = data[AES.block_size:]              # 
    cipher = AES.new(self.key, AES.MODE_CBC, iv )  # recreate cipher
    payload = unpad(cipher.decrypt( encrypted ))   # unpad after decrypt
    return payload.decode("utf8")                  # bytes to string

只有字节可以加密。字符串不是字节,因此必须首先将字符串编码为字节表示。 UTF-8 是一种合适的表示,但它可以是 UTF-16 甚至是 UTF-32 (read about the differences)。

但是,由于密码可以处理任何字节的有效负载,我将删除当前将这些函数限制为字符串的部分。我会将它们更改为期望并返回字节,然后:

  • 将它们分别称为x = aes.encrypt(s.encode('utf8'))s = aes.decrypt(x).decode('utf8'),或
  • 为字符串处理制作包装函数。

对于加密文件,您可以直接执行以下操作:

with open('some.txt', 'rb') as fp:
    encrypted = aes.encrypt(fp.read())

这根本不会强加任何编码假设,而是按原样加密文件的字节。

AES 是分组密码,这意味着encrypt(a) + encrypt(b)encrypt(a + b) 相同。对于加密文件非常有用,因为您可以使用read the file incrementally in chunksN * AES.block_size,只填充最后一个块。这比先将整个文件读入内存更节省内存。您当前的 encryptdecrypt 设置没有使用它。

【讨论】:

  • 我试过了,但现在我在解密过程中遇到错误,需要将数据填充到 16 字节块:ValueError: Data must be padded to 16 byte boundary in CBC mode
  • 它是填充的。 “unpad”调用发生在解密调用之后,decrypt 的输入是来自 base-64 编码数据的填充字节。
  • 请注意,您的 padunpad 函数必须处理 bytes,而不是 str。您需要相应地修改它们。
  • 抱歉这个愚蠢的问题,但我如何让它处理字节而不是 str?
  • 您的函数执行input + (N * chr(something)),换句话说,它执行input + str。这只有在input 本身是一个字符串时才能成功。你需要做input + bytes,所以input可以是bytes。这意味着N * chr(something) 部分必须更改为产生bytes 的部分。
猜你喜欢
  • 2012-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-07
  • 1970-01-01
  • 2018-08-20
  • 2018-06-18
相关资源
最近更新 更多