首页 > 解决方案 > 将 Python AES 转换为节点

问题描述

我一直无法让 Python 中的现有遗留代码像 NodeJS 一样加密/解密。我确实正确解码了前 16 个字符。

这是Python代码:

from Crypto.Cipher import AES
counter = b'HOJFKGQMRCTKUQHP'
cipher = AES.new(self.symmetric_key, AES.MODE_CTR, counter=lambda: counter)
encrypted = cipher.encrypt(data)

我发现每次迭代都使用相同的计数器的来源: PyCrypto problem using AES+CTR

在 NodeJS (ts-node) 中仅对前 16 个字符起作用的是:

import { createDecipheriv, Decipher } from 'crypto'
const decrypt = (inputBase64: string): string => {
    const algorithm = 'aes-192-ctr'; // 24 byte key, 16 byte "counter"
    var decipher: Decipher = createDecipheriv(algorithm, key, counter /* yes, it's an iv */)
    // decipher.setAutoPadding(false);

    return decipher.update(inputBase64, 'base64', 'utf8') + decipher.final('utf8');
}

我找到了各种在线资源,它们都增加了计数器 - 有没有办法使用内置的 Node 加密库来控制计数器的增量?我发现了一些我可能会覆盖的在线实现(如果发生这种情况): https ://github.com/ricmoo/aes-js/blob/master/index.js#L656 https://github.com/白利糖度/crypto-js/blob/develop/src/mode-ctr.js#L26

我怎样才能让它在 Node 中工作?我的 python 代码(这是遗留的,如果不迁移现有值就无法更改)具有以下输出:

encrypt('Testing--StackOverflow')
# outputs: 'r7G8gFNIHuY27nBjSo51nZ6mqZhVUQ=='

decrypt从使用上述功能的节点:

const key = 'LKOXBRRUNBOSMENKEPPZUKWB';
const counter = 'HOJFKGQMRCTKUQHP';
const encrypted = 'r7G8gFNIHuY27nBjSo51nZ6mqZhVUQ==';
const clearText = decrypt(encrypted);
console.log('clear text:', clearText)
// outputs: clear text: Testing--StackOv�::��m

希望有人可以在这里分享一些见解!

标签: pythonnode.jsencryptioncryptographyaes

解决方案


CTR 模式的本质是计数器的递增,这就是为什么这种模式实际上被称为计数器模式[1]。由于在 Python 代码中计数器是一个常量,即根本不递增,因此该代码最多形式地实现了 CTR 模式,但在功能上肯定不是,而且也是疏忽不安全的[2] [3]

大多数图书馆都会尝试防止这种对 CTR 模式的滥用。因此,很难在 NodeJS(或任何其他语言)中找到一个有意使之成为可能的库。

尽管如此,NodeJS(或任何其他语言)中的解密相对容易通过手动实现该模式来实现:在 CTR 模式中,从 IV 生成一个字节序列,从初始 IV 开始,通过为每个附加相应的递增 IV堵塞。然后用 AES 对以这种方式生成的序列进行加密,并将结果与​​明文/密文[1]进行异或运算。该方案适用于加密和解密。由于 Python 代码不执行任何递增,因此必须简单地跳过此步骤,例如:

const crypto = require('crypto');

var algorithm = 'aes-192-ecb';
var key = Buffer.from('LKOXBRRUNBOSMENKEPPZUKWB', 'utf8'); 
var byteSeq = Buffer.from('HOJFKGQMRCTKUQHPHOJFKGQMRCTKUQHP', 'utf8'); 
var ciphertext = Buffer.from('r7G8gFNIHuY27nBjSo51nZ6mqZhVUQ==', 'base64');

// Encrypt the byte-sequence generated from the IV (no incrementation)
var cipher = crypto.createCipheriv(algorithm, key, null);
var byteSeqEnc = Buffer.concat([cipher.update(byteSeq), cipher.final()], byteSeq.length);

// XORing of ciphertext and IV
var decrypted = xor(ciphertext, byteSeqEnc);
console.log("Decrypted: " + decrypted.toString('utf8'));
// Output: Decrypted: Testing--StackOverflow 

// Implementation of XOR
function xor(buf1, buf2){
    var buf = Buffer.alloc(buf1.length);
    for (i = 0; i < buf1.length; i++){
        buf.writeUInt8(buf1.readUInt8(i, i + 1) ^ buf2.readUInt8(i, i + 1), i);
    }
    return buf;
}

字节序列的长度对应于明文/密文的长度,四舍五入为块大小的整数倍。在当前示例中,字节序列的长度为 32 个字节,并且为简单起见进行了硬编码。


推荐阅读