【问题标题】:Implementing AES/ECB/PKCS5 padding in Python在 Python 中实现 AES/ECB/PKCS5 填充
【发布时间】:2021-01-20 00:56:41
【问题描述】:

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

Python3 程序:

import base64
from Crypto.Cipher import AES

 
def add_to_16(value):
    while len(value) % 16 != 0:
        value += '\0'
    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

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

【问题讨论】:

  • 欢迎来到 Stackoverflow。您的“add_to_16”正在将(十六进制)x00 添加到明文中,这被命名为“ZeroPadding”。由于您的参考是“PKCS5Padding”(或 PKCS7Padding,取决于编程语言),您需要更改您的函数以接收这种填充。 安全警告:不要再使用UNSECURE ECB-mode
  • 在线工具当然是对的。
  • 您需要一个工具,该工具还允许您选择填充,例如here。但是,此工具需要十六进制编码的密钥 (39326f69666747683839332a636a2537)。 PKCS7 通常比零填充更可靠,因此 PKCS7 更可取。

标签: python encryption aes ecb


【解决方案1】:

PKCS 5(或 7)填充不是添加 0 字节,而是添加 c byteswith valuec(where1 ) if you're c` 字节缺少块长度倍数。

因此,如果您已经有 16 的倍数,则添加完整的 16 字节值 16,如果您的最后一个块是“停止”(4 字节),我们添加 12 字节,值为 0xc(十六进制中的 12)填满块。等等。

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

我会把编码留给你。

【讨论】:

    【解决方案2】:

    我已经用下面的代码框住了用 PKCS5 填充的代码,并且按预期工作。

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

    加密方法改写如下:

    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
    

    【讨论】:

      【解决方案3】:

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

      测试:
      • 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
      

      我测试了许多已经可用的代码,但没有一个能像 java 那样提供精确的加密。所以,这是所有找到的博客和早期编写的兼容python2的代码的组合

      【讨论】:

        猜你喜欢
        • 2023-04-06
        • 2022-06-10
        • 2021-08-16
        • 2017-04-08
        • 2013-09-10
        • 2019-06-17
        • 2021-04-17
        • 1970-01-01
        • 2014-07-17
        相关资源
        最近更新 更多