Вопрос по python, encryption, node.js – Шифрование и дешифрование с помощью python и nodejs

11

Я пытаюсь зашифровать некоторый контент в 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 

В питоне

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 

Ясно, что они очень близки, но узел, кажется, заполняет вывод чем-то. Любые идеи, как я могу заставить их взаимодействовать?

Спасибо @CodeInChaos, это пример кода, поэтому я упростил некоторые из них. Пароль генерируется с использованием PBKDF2, и IV будет рандомизирован в производстве. dave
1) Вам действительно нужно шифрование на основе пароля вместо случайного ключа? 2) Если вы это сделаете, не используйте хэш-функции с одной итерацией. Используйте соли и функции медленного вывода ключей, такие как PBKDF2, bcrypt или scrypt. CodesInChaos
3) Вы также неправильно используете IV. Это должно быть новое случайное значение для каждого сообщения. Он также должен иметь тот же размер, что и размер блока, а не половину размера блока, как в вашем примере. CodesInChaos

Ваш Ответ

2   ответа
0

Просто для любого, кто похож на меня, который находил простой способ сделать шифрование и дешифрование для AES в python, который делает то же самое в node.js. Здесь класс поддерживает разные биты AES, а также шестнадцатеричное и base64-кодирование, которое дает одинаковый результат в node.js.

Также отмечено, что если вам не хватает пакета 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')
21

Хорошо, я понял, что узел использует OpenSSL, который используетPKCS5 делать отступы. PyCrypto не обрабатывает отступы, поэтому я делал это сам, просто добавив & apos; & APOS; в обоих.

Если я добавляю заполнение PKCS5 в коде Python и удаляю заполнение в коде узла, это работает.

Так обновил рабочий код. Узел:

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(padded[-1])
    return padded[:-pad]

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

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

    data = pad(data)

    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 unpad(aes.decrypt(edata))

output = _encrypt(input, "", password) 
print(output)
plaintext = _decrypt(output, "", password)
print("'" + plaintext + "'")
Error: User Rate Limit Exceeded dave
Error: User Rate Limit Exceeded-Error: User Rate Limit Exceeded/Error: User Rate Limit Exceeded_Error: User Rate Limit Exceeded/Error: User Rate Limit Exceededvar input = input.replace(/\-/g, '+').replace(/_/g, '/');
Error: User Rate Limit Exceededthis SO postError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededvar data = new Buffer(input, 'utf8').toString('binary');Error: User Rate Limit ExceededencryptedError: User Rate Limit Exceeded

Похожие вопросы