【问题标题】:AES encryption issue: "ValueError: Input strings must be a multiple of 16 in length"AES 加密问题:“ValueError:输入字符串的长度必须是 16 的倍数”
【发布时间】:2018-04-20 16:06:06
【问题描述】:

我试图通过这个 python 代码使用 AES 加密数据库中的一些敏感数据

from Crypto.Cipher import AES
import base64
import pymysql
import os
import hashlib    

def encryption(self, tableName):
    # opening db connection
    db_connection = self.getMySqlConnection()
    cursor = db_connection.cursor()

    print("\tTwo Way Encryption")

    # hashing
    cursor.execute("SELECT toMask FROM " + tableName + ";")
    row = cursor.fetchall()
    for item in row:
        string = str(item[0])
        padded = string.rjust(80)
        secret_key = self.key
        cipher = AES.new(secret_key, AES.MODE_ECB)
        encoded = base64.b64encode(cipher.encrypt(padded))
        encoded = encoded.decode('utf-8')
        stmt = "update " + tableName + " set toMask = '" + encoded + "' where toMask =\"" + string + "\";"
        cursor.execute(stmt)
        db_connection.commit()

    # closing db connection
    db_connection.close()

    print("\tDone")

但是我的数据包含一些像这样的特殊字符

所以我收到了这个错误:

ValueError: Input strings must be a multiple of 16 in length

我该如何解决这个问题?其他输入字符串没有任何问题。

或者我需要将其转换为任何其他编码吗?

【问题讨论】:

  • 1.当您嵌套函数调用而不是使用中间语句和变量时,它会使调试变得更加困难。 2. 你用的是什么语言和什么 AES 库?
  • 语言是python,库是Crypto.cipher

标签: python utf-8 base64 aes


【解决方案1】:

我得到了我的问题的答案。我分享这个是为了任何其他可能面临这个问题的同事的利益。

实际上,使用这些数据进行编码是有区别的。所以我所做的是,在填充之前将字符串编码为 utf-8。然后用 16 的倍数填充它,然后加密数据。之后,我对其进行解码并将其更新到数据库中。这样,问题就为我解决了。

所以代码看起来像这样:

from Crypto.Cipher import AES
import base64
import pymysql
import os
import hashlib

    def encryption(self, tableName, paddingLength):
    # opening db connection
    db_connection = self.getMySqlConnection()
    cursor = db_connection.cursor()

    print("\tTwo Way Encryption")


    # hashing
    cursor.execute("SELECT toMask FROM " + tableName + ";")
    row = cursor.fetchall()
    for item in row:
        string = str(item[0])
        #convert to string 
        strng = str(string)
        #encode to utf-8
        string = string.encode('utf-8')
        #pad with length 80
        padded = string.rjust(80)
        secret_key = self.key
        cipher = AES.new(secret_key, AES.MODE_ECB)
        encoded = base64.b64encode(cipher.encrypt(padded))
        #decode utf-8
        encoded = encoded.decode('utf-8')
        encoded = str(encoded)
        # deselect strict mode
        cursor.execute("SET @@global.sql_mode= 'NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';")
        stmt = "update " + tableName + " set toMask = \"" + encoded + "\" where toMask =\"" + strng + "\";"
        # print(stmt)
        cursor.execute(stmt)
        db_connection.commit()

    # closing db connection
    db_connection.close()
    print("\tDone")

所以只需将其编码为 utf-8 即可解决问题。

【讨论】:

    【解决方案2】:

    Crypto.cipherdocumentation 不完整,它没有指定如何处理填充。猜测是它希望调用者进行填充。 (如果您编写开源代码,请提供预期的功能和完整的文档。在这种情况下,应支持 PKCS#7 填充,并应指定输入类型(二进制、字符串、十六进制、Base64)。)

    它似乎还需要以下字节:cipher.encrypt(b'Attack at dawn'),注意 b

    因为您必须填充并提供二进制文件,所以您必须先转换为二进制文件,然后再填充,因此cipher 的输入是块大小的倍数(AES 为 16 字节)。

    空填充不好,数据人曾经有一个0x00字节作为最后一个字节,一般填充是PKCS#7

    咆哮:

    如果您编写开源代码,请提供预期的功能和完整的文档。在这种情况下,应支持 PKCS#7 填充,并应指定输入类型(二进制、字符串、十六进制、Base64 等)。

    【讨论】:

    • b'Attack at dawn' 指的是字节而不是二进制。基本上,b 指的是字节。
    猜你喜欢
    • 2018-03-14
    • 2019-03-24
    • 1970-01-01
    • 2019-01-04
    • 1970-01-01
    • 1970-01-01
    • 2012-05-16
    • 2020-04-28
    相关资源
    最近更新 更多