使用python和nodejs进行加密和解密

hgncfbus  于 2022-12-03  发布在  Node.js
关注(0)|答案(4)|浏览(133)

我尝试用Python加密一些内容,然后在nodejs应用程序中解密。
我正在努力让这两个AES实现一起工作。这里是我在哪里。
在节点中:

var crypto = require('crypto');

var password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
var input = 'hello world';

var encrypt = function (input, password, callback) {
    var m = crypto.createHash('md5');
    m.update(password)
    var key = m.digest('hex');

    m = crypto.createHash('md5');
    m.update(password + key)
    var iv = m.digest('hex');

    // add padding
    while (input.length % 16 !== 0) {
        input += ' ';
    }

    var data = new Buffer(input, 'utf8').toString('binary');

    var cipher = crypto.createCipheriv('aes-256-cbc', key, iv.slice(0,16));
    var encrypted = cipher.update(data, 'binary') + cipher.final('binary');
    var encoded = new Buffer(encrypted, 'binary').toString('base64');

    callback(encoded);
};

var decrypt = function (input, password, callback) {
    // Convert urlsafe base64 to normal base64
    var input = input.replace('-', '+').replace('/', '_');
    // Convert from base64 to binary string
    var edata = new Buffer(input, 'base64').toString('binary')

    // Create key from password
    var m = crypto.createHash('md5');
    m.update(password)
    var key = m.digest('hex');

    // Create iv from password and key
    m = crypto.createHash('md5');
    m.update(password + key)
    var iv = m.digest('hex');

    // Decipher encrypted data
    var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv.slice(0,16));
    var decrypted = decipher.update(edata, 'binary') + decipher.final('binary');
    var plaintext = new Buffer(decrypted, 'binary').toString('utf8');

    callback(plaintext);
};

encrypt(input, password, function (encoded) {
    console.log(encoded);
    decrypt(encoded, password, function (output) {
        console.log(output);
    });
});

这会产生输出:

BXSGjDAYKeXlaRXVVJGuREKTPiiXeam8W9e96Nknt3E=
hello world

Python皮

from Crypto.Cipher import AES
from hashlib import md5
import base64

password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
input = 'hello world'

def _encrypt(data, nonce, password):
    m = md5()
    m.update(password)
    key = m.hexdigest()

    m = md5()
    m.update(password + key)
    iv = m.hexdigest()

    # pad to 16 bytes
    data = data + " " * (16 - len(data) % 16)

    aes = AES.new(key, AES.MODE_CBC, iv[:16])

    encrypted = aes.encrypt(data)
    return base64.urlsafe_b64encode(encrypted)

def _decrypt(edata, nonce, password):
    edata = base64.urlsafe_b64decode(edata)

    m = md5()
    m.update(password)
    key = m.hexdigest()

    m = md5()
    m.update(password + key)
    iv = m.hexdigest()

    aes = AES.new(key, AES.MODE_CBC, iv[:16])
    return aes.decrypt(edata)

output = _encrypt(input, "", password) 
print(output)
plaintext = _decrypt(output, "", password)
print(plaintext)

这将生成输出

BXSGjDAYKeXlaRXVVJGuRA==
hello world

显然它们非常接近,但是节点似乎在输出中填充了一些东西。有什么想法吗?我如何才能让这两个节点互操作?

8i9zcol2

8i9zcol21#

好的,我已经弄明白了,节点使用OpenSSL,它使用PKCS5来做填充。PyCrypto不处理填充,所以我自己做,只是在两者中都添加“”。
如果我在python代码中添加PKCS5填充并删除节点代码中的填充,它就能工作。
因此更新了工作代码。节点:

var crypto = require('crypto');

var password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
var input = 'hello world';

var encrypt = function (input, password, callback) {
    var m = crypto.createHash('md5');
    m.update(password)
    var key = m.digest('hex');

    m = crypto.createHash('md5');
    m.update(password + key)
    var iv = m.digest('hex');

    var data = new Buffer(input, 'utf8').toString('binary');

    var cipher = crypto.createCipheriv('aes-256-cbc', key, iv.slice(0,16));
    
    // UPDATE: crypto changed in v0.10
    // https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10 
    var nodev = process.version.match(/^v(\d+)\.(\d+)/);
    var encrypted;

    if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {
        encrypted = cipher.update(data, 'binary') + cipher.final('binary');
    } else {
        encrypted = cipher.update(data, 'utf8', 'binary') + cipher.final('binary');
    }

    var encoded = new Buffer(encrypted, 'binary').toString('base64');

    callback(encoded);
};

var decrypt = function (input, password, callback) {
    // Convert urlsafe base64 to normal base64
    var input = input.replace(/\-/g, '+').replace(/_/g, '/');
    // Convert from base64 to binary string
    var edata = new Buffer(input, 'base64').toString('binary')
    
    // Create key from password
    var m = crypto.createHash('md5');
    m.update(password)
    var key = m.digest('hex');

    // Create iv from password and key
    m = crypto.createHash('md5');
    m.update(password + key)
    var iv = m.digest('hex');

    // Decipher encrypted data
    var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv.slice(0,16));

    // UPDATE: crypto changed in v0.10
    // https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10 
    var nodev = process.version.match(/^v(\d+)\.(\d+)/);
    var decrypted, plaintext;

    if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {  
        decrypted = decipher.update(edata, 'binary') + decipher.final('binary');    
        plaintext = new Buffer(decrypted, 'binary').toString('utf8');
    } else {
        plaintext = (decipher.update(edata, 'binary', 'utf8') + decipher.final('utf8'));
    }

    callback(plaintext);
};

encrypt(input, password, function (encoded) {
    console.log(encoded);
    decrypt(encoded, password, function (output) {
        console.log(output);
    });
});

Python:

from Crypto.Cipher import AES
from hashlib import md5
import base64

password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
input = 'hello world'

BLOCK_SIZE = 16

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

def unpad (padded):
    pad = ord(chr(padded[-1]))
    return padded[:-pad]

def get_key_iv (password):
    m = md5()
    m.update(password.encode('utf-8'))
    key = m.hexdigest()

    m = md5()
    m.update((password + key).encode('utf-8'))
    iv = m.hexdigest()
    
    return [key,iv]

def _encrypt(data, password):

    key,iv = get_key_iv(password)
    data = pad(data)

    aes = AES.new(key, AES.MODE_CBC, iv[:16])

    encrypted = aes.encrypt(data)
    return base64.urlsafe_b64encode(encrypted)

def _decrypt(edata, password):
    edata = base64.urlsafe_b64decode(edata)
    key,iv = get_key_iv(password)

    aes = AES.new(key, AES.MODE_CBC, iv[:16])
    return unpad(aes.decrypt(edata))

output = _encrypt(input, password) 
print(output)
plaintext = _decrypt(output, password)
print(plaintext)
agxfikkp

agxfikkp2#

在尝试使用Python 3.8运行Python脚本时,我遇到了以下错误:

m.update(password) 
  TypeError: Unicode-objects must be encoded before hashing

密码应为:

password = b'abcd'

我还得到了以下错误:

m.update(password + key) 
TypeError: can't concat str to bytes

我可以通过在key后添加以下行来修复它:

key = bytes.fromhex(key_)

python脚本应按如下方式工作:

from Crypto.Cipher import AES
from hashlib import md5
import base64

password = b'abcd'
input = 'hello world'

BLOCK_SIZE = 16

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

def unpad (padded):
    pad = ord(chr(padded[-1]))
    return padded[:-pad]

def _encrypt(data, nonce, password):
    m = md5()
    m.update(password)
    key_ = m.hexdigest()
    key = bytes.fromhex(key_)

    m = md5()
    m.update(password + key)
    iv = m.hexdigest()
    iv = bytes.fromhex(iv)

    data = pad(data)

    aes = AES.new(key, AES.MODE_CBC, iv[:16])

    encrypted = aes.encrypt(data.encode('utf-8'))
    return base64.urlsafe_b64encode(encrypted)

def _decrypt(edata, nonce, password):
    edata = base64.urlsafe_b64decode(edata)

    m = md5()
    m.update(password)
    key = m.hexdigest()
    key = bytes.fromhex(key)

    m = md5()
    m.update(password + key)
    iv = m.hexdigest()
    iv = bytes.fromhex(iv)

    aes = AES.new(key, AES.MODE_CBC, iv[:16])
    return unpad(aes.decrypt(edata))

output = _encrypt(input, "", password) 
print(output)
plaintext = _decrypt(output, "", password)
print(plaintext)
xoefb8l8

xoefb8l83#

对于任何一个和我相似的人,他在python中找到了一种简单的方法来为AES加密和解密,在node.js中做了同样的事情。这里的类支持AES的不同位,以及在node.js中产生相同结果的十六进制和base64编码。
还需要注意的是,如果您缺少软件包Crypto,您可以简单地安装它

pip install pycrypto

python的代码如下:

import base64
import hashlib
from Crypto.Cipher import AES

class AESCrypto(object):
    def __init__(self, algorithm, password):
        self.algorithm = filter(lambda x: not x.isdigit(), algorithm).lower()
        self.bits = int(filter(str.isdigit, algorithm))
        self.bs = 16
        if not self.algorithm == 'aes':
            raise Exception('Only AES crypto is supported')
        if not self.bits % 8 == 0:
            raise Exception('Bits of crypto must be a multiply of 8.')
        self.bytes = self.bits / 8
        self.password = password
        self.generateKeyAndIv()

    def generateKeyAndIv(self):
        last = ''
        allBytes = ''
        maxBytes = self.bytes + self.bs
        while len(allBytes) < maxBytes:
            last = hashlib.md5(last + self.password).digest()
            allBytes += last
        self.key = allBytes[:self.bytes]
        self.iv = allBytes[self.bytes:maxBytes]

    def encrypt(self, raw, outputEncoding):
        outputEncoding = outputEncoding.lower()
        raw = self._pad(raw)
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        encrypted = cipher.encrypt(raw)
        if outputEncoding == 'hex':
            return encrypted.encode('hex')
        elif outputEncoding == 'base64':
            return base64.b64encode(encrypted)
        else:
            raise Exception('Encoding is not supported.')

    def decrypt(self, data, inputEncoding):
        inputEncoding = inputEncoding.lower()
        if inputEncoding == 'hex':
            data = ''.join(map(chr, bytearray.fromhex(data)))
        elif inputEncoding == 'base64':
            data = base64.b64decode(data)
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        return self._unpad(cipher.decrypt(data))

    def _pad(self, data):
        padding = self.bs - len(data) % self.bs
        return data + padding * chr(padding)

    @staticmethod
    def _unpad(data):
        return data[0:-ord(data[-1])]

下列是使用类别的范例:
加密示例:

password = 'some_random_password'
content = 'content_to_be_encrypted'
cipher = AESCrypto('aes192', password)
encrypted = cipher.encrypt(content, 'hex')

解密示例:

password = 'some_random_password'
content = 'encrypted_content'
cipher = AESCrypto('aes192', password)
decrypted = cipher.decrypt(content, 'hex')
xriantvc

xriantvc4#

因为我在Python 3.10.7和Node.js v18.6.0上花了太多的时间。
下面是两种语言之间完全兼容的工作代码示例。
只需要密码即可获得与预期相同的值:)
注意pycryptodome对于Python是必需的。代码应该调整以支持不同的算法。
第一个
帮助来自:

相关问题