在 Python 中实现 AES/ECB/PKCS5 填充

Implementing AES/ECB/PKCS5 padding in Python

我正在尝试实现一个 python 程序来使用 AES/ECB/PKCS5 填充来加密纯文本。我得到的输出与预期的略有不同。

Python3 程序:

import base64
from Crypto.Cipher import AES

 
def add_to_16(value):
    while len(value) % 16 != 0:
        value += '[=12=]'
    return str.encode (value) # returns bytes
 

# Encryption method
def encrypt(text):
         # Secret key 
    key='92oifgGh893*cj%7' 

         # Text to be encrypted
         # Initialize encryptor
    aes = AES.new(key, AES.MODE_ECB) 

         # Aes encryption to be
    encrypt_aes = aes.encrypt(add_to_16(text)) 

         # Converted into a string with base64
    encrypted_text = str(base64.encodebytes (encrypt_aes), encoding = 'utf-8')

    print(encrypted_text)
    return encrypted_text

if __name__ == '__main__': 

    text = '{  "Message": "hello this is a plain text" , "user":"john.doe", "Email":"john.doe@example.com}'
    entrypted_text = encrypt(text)

以上程序的输出是:

oo8jwHQNQnBwVUsJ5piShFRM3PFFIfULwcoFOEQhPMTAvexSr6eE9aFLVQTpAKBFkGi8vNbtScvyexSxHBlwVapJ5Szz1JPR9q9cHHJYYMzGocln4TRPFQ6S3e8jjVud

使用第 3 方工具验证时 online,结果是:

oo8jwHQNQnBwVUsJ5piShFRM3PFFIfULwcoFOEQhPMTAvexSr6eE9aFLVQTpAKBFkGi8vNbtScvyexSxHBlwVapJ5Szz1JPR9q9cHHJYYMwnIIuNCUVn/IExpxebqXV1

有人可以指导我哪里做错了吗?

PKCS 5(或 7)填充是 不是 添加 0 字节,而是添加 c 字节 with valuec(where1 < = c <= 16) if you're c` 个字节不足一个块长度的倍数。

因此,如果您已经有 16 的倍数,请添加值为 16 的完整 16 个字节,如果您的最后一个块是 'stop'(4 个字节),我们将添加值为 [=14= 的 12 个字节](十六进制的 12)来填充块。等等

这样接收方(在最后一个块解密之后)可以检查最后一个字节 c 并检查值是否为 1 <= c <= 16 (如果不是,拒绝解密)然后检查最后 c 字节确实是 all 相同的值,然后将它们从解密中删除。这样接收方就不必猜测最后一个块中有多少字节只是填充或者真正是纯文本的一部分。以 PKCS 方式进行操作是明确的。

我会把编码留给你。

我已经用下面的代码构建了用于使用 PKCS5 填充的代码,并且按预期工作。

block_size=16
pad = lambda s: s + (block_size - len(s) % block_size) * chr(block_size - len(s) % block_size)

加密方式re-written如下:

def encrypt(plainText,key):
    
    aes = AES.new(key, AES.MODE_ECB)    
    encrypt_aes = aes.encrypt(pad(plainText))   
    encrypted_text = str(base64.encodebytes (encrypt_aes), encoding = 'utf-8')
    return encrypted_text

这是完整的代码,以防有人还在看。

测试对象:
  • python3.6
  • python3.8

** 使用了 pycryptodome

  • encrypt_aes.py
import hashlib
from Crypto.Cipher import AES
import base64 

class AES_pkcs5:
    def __init__(self,key:str, mode:AES.MODE_ECB=AES.MODE_ECB,block_size:int=16):
        self.key = self.setKey(key)
        self.mode = mode
        self.block_size = block_size

    def pad(self,byte_array:bytearray):
        """
        pkcs5 padding
        """
        pad_len = self.block_size - len(byte_array) % self.block_size
        return byte_array + (bytes([pad_len]) * pad_len)
    
    # pkcs5 - unpadding 
    def unpad(self,byte_array:bytearray):
        return byte_array[:-ord(byte_array[-1:])]


    def setKey(self,key:str):
        # convert to bytes
        key = key.encode('utf-8')
        # get the sha1 method - for hashing
        sha1 = hashlib.sha1
        # and use digest and take the last 16 bytes
        key = sha1(key).digest()[:16]
        # now zero pad - just incase
        key = key.zfill(16)
        return key

    def encrypt(self,message:str)->str:
        # convert to bytes
        byte_array = message.encode("UTF-8")
        # pad the message - with pkcs5 style
        padded = self.pad(byte_array)
        # new instance of AES with encoded key
        cipher = AES.new(self.key, AES.MODE_ECB)
        # now encrypt the padded bytes
        encrypted = cipher.encrypt(padded)
        # base64 encode and convert back to string
        return base64.b64encode(encrypted).decode('utf-8')

    def decrypt(self,message:str)->str:
        # convert the message to bytes
        byte_array = message.encode("utf-8")
        # base64 decode
        message = base64.b64decode(byte_array)
        # AES instance with the - setKey()
        cipher= AES.new(self.key, AES.MODE_ECB)
        # decrypt and decode
        decrypted = cipher.decrypt(message).decode('utf-8')
        # unpad - with pkcs5 style and return 
        return self.unpad(decrypted)
        
if __name__ == '__main__':
    # message to encrypt 
    message = 'hello world'
    secret_key = "65715AC165715AC165715AC165715AC1"
    AES_pkcs5_obj = AES_pkcs5(secret_key)
    
    encrypted_message = AES_pkcs5_obj.encrypt(message)

    print(encrypted_message)
    decrypted_message = AES_pkcs5_obj.decrypt(encrypted_message)
    print(decrypted_message)

输出:

>>> python encrypt_aes.py
>>> PDhIFEVqLrJiZQC90FPHiQ== # encrypted message
>>> hello world # and the decrypted one

我已经测试了许多已经可用的代码,但其中 none 给出了与 java 相同的精确加密。因此,这是所有找到的博客和早期编写的与 python2

兼容的代码的组合