【问题标题】:Encrypt with AES/ECB/PKSC5, migrating Java to Python使用 AES/ECB/PKSC5 加密,将 Java 迁移到 Python
【发布时间】:2016-05-14 04:29:06
【问题描述】:

我的任务是在一个项目中实施 AES 加密。参考代码是用 Java 编写的 - 它需要转换为 Python。在整理笔记写一个 SO 问题时,我不小心偶然发现了答案!希望其他人觉得这很有用,我将在这里提到我的笔记作为“分享你的知识”类型的问题。

要求是使用带有给定密钥的 AES 加密消息。以下是参考代码的简化视图(Java 中),

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import sun.misc.BASE64Encoder;


public class EncryptAES {

    private static String toHexString(byte[] data) {        
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < data.length; ++i) {
            String s = Integer.toHexString(data[i] & 0XFF);
            buf.append((s.length() == 1) ? ("0" + s) : s);
        }
        return buf.toString();
    }

    public static String encrypt(String input, String key) {
        byte[] crypted = null;
        try {
            SecretKeySpec skey = new SecretKeySpec(key.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, skey);
            crypted = cipher.doFinal(input.getBytes());
            final String encryptedString = toHexString(Base64.encodeBase64(crypted));
            return encryptedString;
        } catch (Exception e) {
            System.out.println(e.toString());
        }
        return new String(new BASE64Encoder().encode(crypted));
    }

    public static void main(String[] args) {
        String key = args[0];
        String plaintext = args[1];
        System.out.println("KEY = " + key);
        System.out.println("PLAINTEXT = " + plaintext);
        System.out.println("CIPHER = " + EncryptAES.encrypt(plaintext, key));
    }
}

如果将上面的内容保存为“EncryptAES.java”,并将库文件commons-codec-1.7.jar保存在同一目录下,则可以使用以下命令进行编译,

$ javac EncryptAES.java -cp commons-codec-1.7.jar

这是程序运行几次时的输出,

$ java -cp "commons-codec-1.7.jar:." EncryptAES ddddffffeeeerrrr message
KEY = ddddffffeeeerrrr
MESSAGE = message
CRYPTO = 397a59594d35524e6b6a463253706f41467668646b773d3d
$

$ java -cp "commons-codec-1.7.jar:." EncryptAES qqqqwwwweeeerrrr ThisIsAVeryImportantMessage
KEY = qqqqwwwweeeerrrr
PLAINTEXT = ThisIsAVeryImportantMessage
CIPHER = 56536a384d667736756b595a394e396b6d504d736231444673375250736d5639596f637072792f6e4b424d3d
$

环顾四周,我找到了 Python Crypto 库。这是我必须复制上述输出的早期尝试之一,

#!/usr/bin/python

import sys
from Crypto.Cipher import AES

if __name__ == '__main__':
    key = sys.argv[1]
    plaintext = sys.argv[2]
    print 'KEY = ' + key
    print 'PLAINTEXT = ' + plaintext

    encobj = AES.new(key, AES.MODE_ECB)
    ciphertext = encobj.encrypt(plaintext)
    print 'CIPHER = ' + ciphertext.encode('hex') 

这并不能完全满足我的需要。相反,我收到一条错误消息,提示输入字符串的长度必须是 16 的倍数。这让我开始了下一次尝试,

#!/usr/bin/python

import sys
from Crypto.Cipher import AES

# ref: https://gist.github.com/crmccreary/5610068
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 
unpad = lambda s : s[0:-ord(s[-1])]

class AESCipher:
    def __init__( self, key ):
        """
        Requires hex encoded param as a key
        """
        self.key = key.decode("hex")

    def encrypt( self, raw ):
        """
        Returns hex encoded encrypted value!
        """
        raw = pad(raw)
        cipher = AES.new(self.key, AES.MODE_ECB)
        return cipher.encrypt(raw).encode("hex")


if __name__ == '__main__':
    key = sys.argv[1]
    plaintext = sys.argv[2]
    print 'KEY = ' + key
    print 'PLAINTEXT = ' + plaintext

    # ref: http://stackoverflow.com/a/16882092
    hex_key = "".join("{:02x}".format(ord(c)) for c in key)

    encryptor = AESCipher(hex_key)
    ciphertext = encryptor.encrypt(plaintext)
    print 'CIPHER = ' + ciphertext

说实话,我不太确定输出是什么,

$ python EncryptAES2.py ddddffffeeeerrrr message
KEY = ddddffffeeeerrrr
PLAINTEXT = message
CIPHER = f7361833944d9231764a9a0016f85d93
$

我尝试了很多东西——不同的加密模式、博客、SO 问题,并放弃了自己寻找解决方案。正是在这一点上,我决定收集我的笔记并在这里提出一个问题。现在,如果我不列出我的尝试就没有什么意义了,所以我开始将它们组织在一个文件夹中并标记它们为EncryptAES.pyEncryptAES2.py .. 等等。

【问题讨论】:

  • 我遇到了几乎相同的问题,很高兴找到这个问题。我有点困惑,编码的字符串是以十六进制输出的,因为 base64 编码的全部目的是将二进制数据转换为可打印的文本,但我假设原始 Java 作者做到了,而您只是保持与该代码的兼容性。
  • This 可能会有所帮助。这不是一回事,而是类似的东西。

标签: java python encryption aes


【解决方案1】:

采用@chronodekar 的答案并将其更改为适用于 Python 3.9。

Python 3 的 PyCryptoDome 版本引发类型错误:无法将对象类型 传递给 C 代码。为了解决这个问题,我将密钥和原始明文转换为字节数组。

    class simple_AES:

    def __init__(self, key):
        self.key = bytearray(key.encode())

    def encrypt_AES(self, raw):
        raw = bytearray(pad(raw).encode())
        cipher = AES.new(self.key, AES.MODE_ECB)
        return base64.b64encode(cipher.encrypt(raw))

但是,我也遇到了输出问题。我找到了将输出转换为十六进制 python base64 to hex 的答案。

    hex_ciphertext = base64.b64decode(ciphertext).hex()
    print('CIPHER = ' + hex_ciphertext)

【讨论】:

    【解决方案2】:
    import base64
    from Crypto.Cipher import AES
    
    BS = 16
    pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
    unpad = lambda s: s[0:-ord(s[-1])]
    
    
    class AESCipher:
        def __init__(self, key):
            self.key = key
    
        def encrypt(self, raw):
            raw = pad(raw)
            cipher = AES.new(self.key, AES.MODE_ECB)
            raw = cipher.encrypt(raw)
            encrypt_val = base64.b64encode(raw)
            return encrypt_val
    
        def decrypt(self, raw):
            raw = raw.decode('base64')
            cipher = AES.new(self.key, AES.MODE_ECB)
            raw = cipher.decrypt(raw)
            raw = unpad(raw)
            return raw
    
    
    if __name__ == '__main__':
        key = '123456789012345678901234'
        plaintext = '111122223333'
        print 'KEY = ' + key
        print 'PLAINTEXT = ' + plaintext
    
        ## Encrptor logic
        encryptor = AESCipher(key)
        ciphertext = encryptor.encrypt(plaintext)
    
        hex_ciphertext = "".join("{:02x}".format(ord(c)) for c in ciphertext)
        print 'Encrypted value : ' + hex_ciphertext
    
        bytesarray = []
        hexstr = ''.join(hex_ciphertext.split(" "))
        for i in range(0, len(hexstr), 2):
            bytesarray.append(chr(int(hexstr[i:i+2], 16)))
    
        val = ''.join(bytesarray)
        decrypt_ciphertxt = encryptor.decrypt(val)
        print 'Decrypted value  : ' + decrypt_ciphertxt
    

    【讨论】:

    • 请添加您的代码说明。它是如何解决OP的问题的?
    • 我在 pycharm 和 python 3.6 TypeError: Object type &lt;class 'str'&gt; cannot be passed to C code987654323@运行时遇到错误
    • @dinu0101 你找到解决办法了吗?
    • @sumanth Shetty,我找到了另一个对我有用的解决方案https://yococoxc.github.io/15493867450071.html.. 它也可以帮助你
    【解决方案3】:

    当我准备清单时,灵感突然涌现,在我最后一次尝试中,我决定将输出重新格式化为十六进制。令我惊喜的是,它奏效了!这是中奖代码,

    #!/usr/bin/python
    
    import sys
    import base64
    from Crypto.Cipher import AES
    
    # ref: http://stackoverflow.com/a/12525165
    BS = 16
    pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 
    
    class AESCipher:
        def __init__( self, key ):
            self.key = key
    
        def encrypt( self, raw ):
            raw = pad(raw)
            cipher = AES.new(self.key, AES.MODE_ECB)
            return base64.b64encode(cipher.encrypt(raw)) 
    
    
    if __name__ == '__main__':
        key = sys.argv[1]
        plaintext = sys.argv[2]
        print 'KEY = ' + key
        print 'PLAINTEXT = ' + plaintext
    
        encryptor = AESCipher(key)
        ciphertext = encryptor.encrypt(plaintext)
    
        hex_ciphertext = "".join("{:02x}".format(ord(c)) for c in ciphertext)
    
        print 'CIPHER = ' + hex_ciphertext
    

    这里的参考是我在 Java 示例中使用的早期输入的输出,

    $ python EncryptAES3.py ddddffffeeeerrrr message
    KEY = ddddffffeeeerrrr
    PLAINTEXT = message
    CIPHER = 397a59594d35524e6b6a463253706f41467668646b773d3d
    $
    
    $ python EncryptAES3.py qqqqwwwweeeerrrr ThisIsAVeryImportantMessage
    KEY = qqqqwwwweeeerrrr
    PLAINTEXT = ThisIsAVeryImportantMessage
    CIPHER = 56536a384d667736756b595a394e396b6d504d736231444673375250736d5639596f637072792f6e4b424d3d
    $
    

    得到这个解决方案让我经历了很多试错。如果有更规范的方法将 Java 转换为 Python,我很想听听!

    【讨论】:

    • 能否提供同样使用python的解密逻辑。
    猜你喜欢
    • 1970-01-01
    • 2017-11-11
    • 2019-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多