2012-05-11 16 views
9

Sto tentando di crittografare del contenuto in Python e decrittarlo in un'applicazione nodejs.Crittografia e decrittografia con python e nodejs

Sto lottando per far funzionare le due implementazioni AES. Ecco dove sono.

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

Questo produce l'uscita:

BXSGjDAYKeXlaRXVVJGuREKTPiiXeam8W9e96Nknt3E= 
hello world 

In pitone

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) 

Questo produce l'uscita

BXSGjDAYKeXlaRXVVJGuRA== 
hello world 

Chiaramente sono molto vicini, ma il nodo sembra riempire l'output con qualcosa. Qualche idea su come posso far interagire i due?

+0

1) Avete veramente bisogno di crittografia basata su password, invece di utilizzare una chiave casuale? 2) Se lo fai, non utilizzare le singole funzioni di hash di iterazione. Utilizzare un salt, e le funzioni di derivazione chiave lento, come PBKDF2, bcrypt o scrypt. – CodesInChaos

+0

3) Non si utilizza neanche l'IV correttamente. Dovrebbe essere un nuovo valore casuale per ogni messaggio. Dovrebbe anche avere le stesse dimensioni della dimensione del blocco e non metà della dimensione del blocco, come nel tuo esempio. – CodesInChaos

+0

Grazie a @CodeInChaos questo è un codice di esempio, quindi ho semplificato alcuni di essi. La password viene generata utilizzando PBKDF2 e l'IV verrà randomizzato in produzione. – dave

risposta

18

OK, l'ho capito, il nodo utilizza OpenSSL che utilizza PKCS5 per eseguire il riempimento. PyCrypto non gestisce il padding, quindi lo stavo facendo io stesso aggiungendo '' in entrambi.

Se aggiungo il padding PKCS5 nel codice python e rimuovo il padding nel codice del nodo, funziona.

Codice di funzionamento così aggiornato. 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

C'è un piccolo bug nella funzione di decodifica di Node.js. Non gestirà più '-' o multipli'/'. Inoltre, in decriptare, è necessario sostituire '_' con'/', non viceversa. Puoi semplicemente sostituire la linea con: 'var input = input.replace (/ \ -/g, '+'). Replace (/ _/g, '/');' – Itay

+0

Grazie, ho risolto nella risposta ora – dave

+0

Grazie mille per gli esempi. Stavo ricevendo un errore di "lunghezza finale del blocco errato" con input complessi e ho trovato un aggiornamento descritto in [questo post SO] (http://stackoverflow.com/questions/21292142/decyrpting-aes256-with-node-js- return-wrong-final-block-length # 21292538) che ha risolto il mio problema. Ho applicato le modifiche sopra. –

Problemi correlati