2012-05-11 13 views
9

Próbuję zaszyfrować niektóre treści w Pythonie i odszyfrować je w aplikacji nodejs.Szyfrowanie i odszyfrowywanie za pomocą python i nodejs

Staram się, aby dwie implementacje AES współpracowały ze sobą. Oto, gdzie jestem.

W węźle:

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

To daje wynik:

BXSGjDAYKeXlaRXVVJGuREKTPiiXeam8W9e96Nknt3E= 
hello world 

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

To daje wyjście

BXSGjDAYKeXlaRXVVJGuRA== 
hello world 

Najwyraźniej są bardzo blisko, ale węzeł wydaje się dopełniać wyjście z czymś. Jakieś pomysły, w jaki sposób mogę doprowadzić do współdziałania dwóch?

+0

1) Czy naprawdę trzeba szyfrowania hasła oparte zamiast używać klucza losowego? 2) Jeśli tak, nie używaj funkcji skrótu z pojedynczą iteracją. Używaj funkcji pochodnych soli i wolnego slowowania, takich jak PBKDF2, bcrypt lub scrypt. – CodesInChaos

+0

3) Nie używasz IV poprawnie. Powinna to być nowa, losowa wartość dla każdej wiadomości. Powinien również mieć taki sam rozmiar jak rozmiar bloku, a nie połowę rozmiaru bloku, jak w twoim przykładzie. – CodesInChaos

+0

Dzięki @CodeInChaos to przykładowy kod, więc uprościłem niektóre z nich. Hasło jest generowane przy użyciu PBKDF2, a IV będzie losowo przydzielany do produkcji. – dave

Odpowiedz

18

OK, znalazłem to, węzeł używa OpenSSL, który używa PKCS5 do wypełniania. PyCrypto nie radzi sobie z dopełnieniem, więc robiłem to sam, dodając po prostu "" w obu.

Jeśli dodaję dopełnienie PKCS5 w kodzie Pythona i usunie dopełnienie w kodzie węzła, to działa.

Tak zaktualizowany działający kod. Węzeł:

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

Istnieje niewielki błąd w funkcji odszyfrowywania Node.js. Nie obsłuży wielu '-' lub wielu'/'. Również w odszyfrowaniu musisz zamienić '_' na'/', a nie na odwrót. Możesz po prostu zastąpić tę linię przez: 'var input = input.replace (/ \ -/g, '+'). Replace (/ _/g, '/');' – Itay

+0

Dzięki, mam naprawione w odpowiedzi teraz: – dave

+0

Dziękuję bardzo za przykłady. Otrzymałem błąd "nieprawidłowej długości bloku końcowego" ze złożonymi danymi wejściowymi i znalazłem aktualizację opisaną w [tym poście SO] (http://stackoverflow.com/questions/21292142/decyrpting-aes256-with-node-js- return-wrong-final-block-length # 21292538), który rozwiązał mój problem. Zastosowałem powyższe zmiany. –

Powiązane problemy