Java 中的 AES 加密移植到 Python

AES Encryption in Java porting to Python

免责声明:这是我继承的不安全加密,所有相关人员都知道这一点。我在这里要做的是摆脱这个遗留系统的第一步,这个遗留系统有比这个更大的问题。

我在 Java 中有一个现有系统,我正在尝试将其移植到 Python,该系统执行 AES 加密:

public static String encrypt(String text, SecretKey secretKey) throws Exception {
    byte[] cipherText = null;
    String encryptedString = null;

    // get an RSA cipher object and print the provider
    Cipher cipher = Cipher.getInstance(SYMMETRIC_KEY_ALGORITHM); // AES

    // encrypt the plain text using the public key
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    cipherText = cipher.doFinal(text.getBytes());
    encryptedString = Base64.getEncoder().encodeToString(cipherText);

    return encryptedString;
}

我遇到的问题是尝试使用 Python 和密码学库来获得相同的 AES 设置组合以获得相同的结果:

Python Cryptography

我看到很多来自看似已失效的 PyCrypto 库的示例,我什至无法在我的 Windows 系统上安装它们,更不用说工作了。

我最近的尝试是这样的,我确实得到了加密,但它与 Java AES 输出不匹配:

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

key = b'Some random key '
cipher = Cipher(algorithms.AES(key), modes.GCM(b'000000000000'))
encryptor = cipher.encryptor()
ct = encryptor.update(ENC_MESSAGE)

base64EncodedStr = base64.b64encode(ct).decode('utf-8')
print('BASE64:', base64EncodedStr)

根据 rzwitserloot 的回答更新。

我将模式从 GCM 更改为 ECB,现在我在 Python 中获得的结果几乎相同。首先是更新后的代码:

key = b'Some random key '

cipher = Cipher(algorithms.AES(key), modes.ECB())
encryptor = cipher.encryptor()
ct = encryptor.update(PLAINTEXT.encode('utf-8'))

base64EncodedStr = base64.b64encode(ct).decode('utf-8')
print('Encrypted:', base64EncodedStr)

参考(即Java)输出长度为 1004 个字符,而 Python 输出长度仅为 984 个字符。但最多匹配 Python 字符串末尾的 3 个字符:

我确实检查了解密,发现两个加密文本字符串解密为相同的明文。

最终更新:

填充是问题所在。我更新了代码以像这样使用 PKCS7 填充,现在我从 Java 和 Python:

得到相同的结果
from cryptography.hazmat.primitives import padding as symmetric_padding

padder = symmetric_padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(PLAINTEXT.encode('utf-8')) + padder.finalize()

ct = encryptor.update(padded_data)

您需要所有内容在两个版本之间完全相同。目前情况并非如此;解决这个问题。

您的 java 代码使用:

  • 块大小为 128 的 AES,因为这正是 AES 的运行方式。无需配置。
  • AES 密钥大小为 128、192 或 256 位。
  • 钥匙。
  • 操作模式。您的 java 代码使用 ECB(不安全)。您已指定 python 代码使用 GCM。显然,那是行不通的。您也需要在那里指定 ECB。
  • 鉴于它是 ECB,因此没有 IV。
  • 填充模式。 Java 代码在此处执行 PKCS5Padding。
  • 加密基本上是基于字节的,但您要加密的不是字符串。这意味着字符串正在转换为字节,这意味着使用了字符集编码。您没有在 java 代码中指定,这意味着您得到 'platform default'。可怕的想法,但如果你不能改变 java 的一面,弄清楚那是什么,并在你的 python 代码中使用相同的编码。
  • 最后,您的 java 代码 base64 就是结果。

对于其中的大部分,我根本无法告诉你;您粘贴的代码不足。例如,AES 密钥大小和密钥实际上是否相同?我不知道 - 你告诉我。你是如何制作那个 SecretKey key 对象的?

我对 python 不太熟悉,但看起来确实像是您对其进行了 base64 编码,然后再次解码。那是.. 不,不要那样做。您的 java 代码进行编码,仅此而已。您的 python 代码应该使用 base64 编码,仅此而已。

我很确定 python 也默认为 PKCS5Padding。

剩下 java 和 python 之间 100% 不匹配的编码模式给出了您粘贴的少量代码,以及您构造密钥的方式。如果您要加密的文本不是直接的 ASCII,字符集编码可能也会造成差异。

这是加密货币。一个微小的差异,输出就会大不相同。