2012-05-11 27 views
9

Estoy intentando cifrar algo de contenido en Python y descifrarlo en una aplicación nodejs.Encriptación y descifrado con python y nodejs

Sin embargo, estoy luchando para que las dos implementaciones de AES funcionen juntas. Aquí es donde estoy.

En nodo:

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); 
    }); 
}); 

Esto produce la salida:

BXSGjDAYKeXlaRXVVJGuREKTPiiXeam8W9e96Nknt3E= 
hello world 

en 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) 

Esto produce la salida

BXSGjDAYKeXlaRXVVJGuRA== 
hello world 

Claramente están muy cerca, pero el nodo parece estar rellenando la salida con algo. ¿Alguna idea de cómo puedo hacer que los dos interactúen?

+0

1) ¿Usted realmente necesita encriptación basada en contraseña, en lugar de utilizar una clave aleatoria? 2) Si lo hace, no use funciones de hash de iteración única. Use una sal y funciones de derivación de tecla lenta, como PBKDF2, bcrypt o scrypt. – CodesInChaos

+0

3) Tampoco usa el IV correctamente. Debería ser un nuevo valor aleatorio para cada mensaje. También debe tener el mismo tamaño que el tamaño del bloque, y no la mitad del tamaño del bloque, como en su ejemplo. – CodesInChaos

+0

Gracias @CodeInChaos este es código de muestra, así que simplifiqué un poco. La contraseña se genera con PBKDF2 y la IV se asignará al azar en producción. – dave

Respuesta

18

OK, lo he descubierto, el nodo usa OpenSSL que usa PKCS5 para hacer el relleno. PyCrypto no maneja el relleno, así que lo hice yo solo agregué '' en ambos.

Si agrego relleno PKCS5 en el código python y elimino el relleno en el código del nodo, funciona.

Código de trabajo tan actualizado. Nodo:

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 + "'") 
+2

Hay un pequeño error en la función de descifrado de Node.js. No manejará múltiples '-' o múltiples'/'. Además, en el descifrado, debe reemplazar '_' con'/', y no al revés. Simplemente puede reemplazar esa línea con: 'var input = input.replace (/ \ -/g, '+'). Replace (/ _/g, '/');' – Itay

+0

Gracias, lo he arreglado en la respuesta ahora – dave

+0

Muchas gracias por los ejemplos. Recibí un error de "longitud de bloque final incorrecta" con una entrada compleja, y encontré una actualización descrita en [esta publicación SO] (http://stackoverflow.com/questions/21292142/decyrpting-eses256-with-node-js- returns-wrong-final-block-length # 21292538) que resolvió mi problema. Apliqué los cambios anteriores. En –

Cuestiones relacionadas