5

我正在使用 CryptoJS (v 2.3) 在 Web 应用程序中加密一个字符串,我需要在服务器上用 Python 解密它,所以我使用的是 PyCrypto。我觉得我错过了一些东西,因为我不能让它工作。

这是JS:

Crypto.AES.encrypt('1234567890123456', '1234567890123456',
                   {mode: new Crypto.mode.CBC(Crypto.pad.ZeroPadding)})
// output: "wRbCMWcWbDTmgXKCjQ3Pd//aRasZ4mQr57DgTfIvRYE="

蟒蛇:

from Crypto.Cipher import AES
import base64
decryptor = AES.new('1234567890123456', AES.MODE_CBC)
decryptor.decrypt(base64.b64decode("wRbCMWcWbDTmgXKCjQ3Pd//aRasZ4mQr57DgTfIvRYE="))
# output: '\xd0\xc2\x1ew\xbb\xf1\xf2\x9a\xb9\xb6\xdc\x15l\xe7\xf3\xfa\xed\xe4\xf5j\x826\xde(m\xdf\xdc_\x9e\xd3\xb1'
4

2 回答 2

12

这是 CryptoJS 3.1.2 的版本。始终注意以下事项(在两种语言中使用相同):

  • 操作模式(在这种情况下为 CBC)
  • 填充(在这种情况下为零填充;最好使用 PKCS#7 填充)
  • 密钥(相同的推导函数或清除密钥)
  • 编码(密钥、明文、密文等的编码相同)
  • IV(在加密期间生成,用于解密)

如果将字符串作为key参数传递给 CryptoJSencrypt()函数,则该字符串用于派生用于加密的实际密钥。如果您希望使用密钥(有效大小为 16、24 和 32 字节),则需要将其作为 WordArray 传递。

CryptoJS 加密的结果是一个 OpenSSL 格式的密文字符串。要从中获取实际的密文,您需要访问其ciphertext上的属性。

每次加密的 IV 必须是随机的,这样它在语义上是安全的。这样,攻击者只看密文就无法判断多次加密的相同明文是否实际上是相同的明文。

下面是我做的一个例子。

JavaScript:

var key = CryptoJS.enc.Utf8.parse('1234567890123456'); // TODO change to something with more entropy

function encrypt(msgString, key) {
    // msgString is expected to be Utf8 encoded
    var iv = CryptoJS.lib.WordArray.random(16);
    var encrypted = CryptoJS.AES.encrypt(msgString, key, {
        iv: iv
    });
    return iv.concat(encrypted.ciphertext).toString(CryptoJS.enc.Base64);
}

function decrypt(ciphertextStr, key) {
    var ciphertext = CryptoJS.enc.Base64.parse(ciphertextStr);

    // split IV and ciphertext
    var iv = ciphertext.clone();
    iv.sigBytes = 16;
    iv.clamp();
    ciphertext.words.splice(0, 4); // delete 4 words = 16 bytes
    ciphertext.sigBytes -= 16;
    
    // decryption
    var decrypted = CryptoJS.AES.decrypt({ciphertext: ciphertext}, key, {
        iv: iv
    });
    return decrypted.toString(CryptoJS.enc.Utf8);
}

带有 pycrypto 的 Python 2 代码:

BLOCK_SIZE = 16
key = b"1234567890123456" # TODO change to something with more entropy

def pad(data):
    length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
    return data + chr(length)*length

def unpad(data):
    return data[:-ord(data[-1])]

def encrypt(message, key):
    IV = Random.new().read(BLOCK_SIZE)
    aes = AES.new(key, AES.MODE_CBC, IV)
    return base64.b64encode(IV + aes.encrypt(pad(message)))

def decrypt(encrypted, key):
    encrypted = base64.b64decode(encrypted)
    IV = encrypted[:BLOCK_SIZE]
    aes = AES.new(key, AES.MODE_CBC, IV)
    return unpad(aes.decrypt(encrypted[BLOCK_SIZE:]))

警告:请记住,python2 和 pycrypto 都已过时,因此必须调整代码以适应 python3 和 pycryptodome。


其他注意事项:

您似乎想使用密码作为密钥。密码短语通常是人类可读的,但密钥不是。您可以使用 PBKDF2、bcrypt 或 scrypt 等功能从密码短语派生密钥。

上面的代码并不完全安全,因为它缺乏身份验证。未经身份验证的密文可能会导致可行的攻击和未被注意的数据操纵。通常采用 encrypt-then-MAC 方案和良好的 MAC 函数,例如 HMAC-SHA256。

于 2015-06-23T18:51:17.553 回答
-2

注意: js 和 python 中的 salt , iv , padding 应该相同

生成 salt 和 iv 值并将其转换为字节字符串 uisng CryptoJS.enc.Utf8.parse()

js文件

var encrypted = CryptoJS.AES.encrypt(JSON.stringify(json_data), CryptoJS.enc.Utf8.parse(data['salt']) , { iv: CryptoJS.enc.Utf8.parse(data['iv']) , mode: CryptoJS.mode.CBC , padding: CryptoJS.pad.Pkcs7});
en_data = encrypted.ciphertext.toString(CryptoJS.enc.Base64)

将此加密数据发送到 python 文件

蟒蛇文件

from Crypto.Util.Padding import pad, unpad

ct = request.POST['encrypted_data']
data = base64.b64decode(ct)
cipher1 = AES.new(salt, AES.MODE_CBC, iv)
pt = unpad(cipher2.decrypt(data), 16)
data = json.loads(pt.decode('utf-8'))
        

默认情况下,pycrypto 中的 pad 和 upad 使用 pkcs#7

salt 和 iv 值应该是字节字符串

于 2020-07-25T05:02:14.697 回答