【问题标题】:convert Python AES to Node将 Python AES 转换为节点
【发布时间】:2020-04-22 11:00:02
【问题描述】:

我一直无法让 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/brix/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

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

【问题讨论】:

    标签: python node.js encryption cryptography aes


    【解决方案1】:

    CTR 模式的本质是计数器的递增,这就是为什么这种模式实际上被称为 counter 模式[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;
    }
    

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

    【讨论】:

    • 使用 [1][2] 很难阅读,我已经用悬停写了你的两个链接。至少这对读者来说很容易。
    • 感谢您对工作示例的回答。看起来我只需要重复字节序列 (int(ciphertext.length / 16) + 1) 次。
    • 公式L=bs*(int(l/bs)+1),其中bs是块大小,l是密文/明文的长度,L是字节序列的长度,有效,但产生不必要地长字节序列L=2*bsl=bs 的情况下。更好的是L = ceil(l/bs),其中ceilceiling-function。对于示例l=bs,这将为字节序列生成所需的长度L=bs
    猜你喜欢
    • 1970-01-01
    • 2018-03-02
    • 2017-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-18
    • 1970-01-01
    相关资源
    最近更新 更多