使用 CryptoJS 加密,使用 PyCrypto 解密(将 CryptoJS 调整为 PyCrypto 默认值)

Encrypt with CryptoJS, decrypt with PyCrypto (adjusting CryptoJS to PyCrypto defaults)

我正在尝试在 CryptoJS 上解密并在 PyCrypto 上加密。

我看到了 很好的答案,它很有魅力,唯一的问题是它调整了 PyCrypto 以与 CryptoJS 一起工作,我更喜欢定义我期望输入的规则并尽可能少地进行调整在具有默认值的 PyCrypto 上。

我想要求发送 iv,并使用零填充。

我在 JS (ES6) 中写了以下内容:

  const iv = CryptoJS.enc.Hex.parse("1234567889012345");
  const key = 'aR1h7EefwlPNVkvTHwfs6w=='
  const encrypted = AES.encrypt(
    password,
    key,
    {
      iv,
      padding: CryptoJS.pad.NoPadding
    }
  );

  const payload = {password: encrypted, iv: iv};
  // make HTTPS POST call

Python:

def decrypt_from_cryptoJS(encrypted, iv):
    key = "aR1h7EefwlPNVkvTHwfs6w==".encode()
    aes = AES.new(key.encode(), AES.MODE_CBC, iv)
    encrypted = aes.decrypt(base64.b64decode(encrypted)))

但是,我得到 ValueError:raise TypeError("Object type %s cannot be passed to C code" % type(data))

如果我尝试通过以下方式创建 VI: CryptoJS.lib.WordArray.random(16) 并通过JS的toString()方法发送,我得到:

Incorrect IV length (it must be 16 bytes long)

用于启动 AES

如何在 PyCrypto 中以最少的代码调整在 CryptoJS 中解密? 我什至不确定我是否走对了路..

  • 在 CryptoJS 端,密钥和 IV 必须作为 WordArray-objects [1]. CryptoJS provides encoders for the conversion of strings into WordArray-objects and vice versa [2]. If the key is passed as a string, it is treated as passphrase and the actual key and IV are derived from it (in the referenced answer, the algorithm used for this is implemented on the Python-side ).

  • 传递
  • 密钥是Base64编码的,Base64解码后长度为16字节,所以使用AES-128。为了转换成 WordArray,必须使用 CryptoJS 端的 Base64 编码器。在 Python 端,密钥必须经过 Base64 解码(仍然需要添加到发布的代码中)。注意:在发布的代码中,密钥被视为 Utf8 字符串,因此密钥的长度为 24 字节,并使用 AES-192。如果这是 intended,则必须在 CryptoJS 端使用 Utf8 编码器而不是 Base64 编码器。在 Python 端,不需要对密钥进行 Base64 解码。

  • 用于加密的IV也必须用于解密。通常,IV 在加密端生成为随机字节序列。由于 IV 不是秘密的,它通常放在密文的前面,并且连接的数据被发送给将两个部分分开的接收者。在 CryptoJS 端,可以使用 WordArray#concat 轻松完成连接。在 Python 端,分离是通过切片完成的(仍然需要添加到发布的代码中)。

  • AES/CBC 期望明文的长度是块大小(16 字节)的整数倍。如果明文长度不同,则必须使用填充。 CryptoJS 默认使用 CBC-mode 和 PKCS7-padding,所以这些都不需要明确指定 [4]. PKCS7-padding is more reliable than Zero-padding [5]. However, if Zero-padding must be used instead (which is not clear to me from the question), it has to be specified explicitly with padding: CryptoJS.pad.ZeroPadding. PyCrypto doesn't remove the padding automatically, i.e. this must be done manually (which still needs to be added to the posted code). Note, that in contrast to PyCrypto, PyCryptodome supports padding [6].

可能的 JavaScript 代码是:

var password = "The quick brown fox jumps over the lazy dog";
var iv = CryptoJS.lib.WordArray.random(16);                         // Generate a random 16 bytes IV
var key = CryptoJS.enc.Base64.parse('aR1h7EefwlPNVkvTHwfs6w==');    // Interpret key as Base64 encoded

var encrypted = CryptoJS.AES.encrypt(password, key, {iv: iv});      // Use CBC-mode and PKCS7-padding
var joinedData = iv.clone().concat(encrypted.ciphertext);           // Concat IV and Ciphertext    
var joinedDataB64 = CryptoJS.enc.Base64.stringify(joinedData);
console.log(joinedDataB64.replace(/(.{64})/g, "\n"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>

适当的 Python 代码可以是:

from Crypto.Cipher import AES
import base64

def decrypt_from_cryptoJS(encrypted, iv):
  key = base64.b64decode("aR1h7EefwlPNVkvTHwfs6w==")                # Interpret key as Base64 encoded   
  aes = AES.new(key, AES.MODE_CBC, iv)                              # Use CBC-mode
  encrypted = aes.decrypt(encrypted)                                # Remove Base64 decoding
  return encrypted

def unpadPkcs7(data):
  return data[:-ord(data[-1])]
  #return data[:-data[-1]] #Python 3

joinedDataB64 = "sbYEr73hZVKviuQ2rt5RcJ5ugpn7XBLTtZIKKk5JjTXmGojFkAS+dK0D8NNAET6bC/Ai4sx+El5Bzu4igT1S9g=="
joinedData = base64.b64decode(joinedDataB64)
iv = joinedData[:16]                                                # Determine IV from concatenated data
encrypted = joinedData[16:]                                         # Determine ciphertext from concatenated data

decrypted = unpadPkcs7(decrypt_from_cryptoJS(encrypted, iv))        # Decrypt and remove PKCS7-padding manually
print decrypted
#print(decrypted) #Python 3