2012-02-08 11 views
6

Qui ci sono le mie esigenze:Perché una cosa criptata in PHP non corrisponde alla stessa stringa crittografata in Ruby?

ho bisogno di cifrare una stringa in PHP utilizzando la crittografia AES (tra cui un iv casuale), Base64 codificare, quindi codifica URL in modo che esso può essere passato come parametro URL.

Sto cercando di ottenere lo stesso risultato in entrambi PHP e Ruby, ma non riesco a farlo funzionare.

Ecco il mio codice PHP:

function encryptData($data,$iv){ 
    $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); 
    $iv_size = mcrypt_enc_get_iv_size($cipher); 
    if (mcrypt_generic_init($cipher, 'g6zys8dlvvut6b1omxc5w15gnfad3jhb', $iv) != -1){ 
     $cipherText = mcrypt_generic($cipher,$data); 
     mcrypt_generic_deinit($cipher); 
     return $cipherText; 
    } 
    else { 
     return false; 
    } 
} 
$data = 'Mary had a little lamb'; 
$iv = '96b88a5f0b9efb43'; 
$crypted_base64 = base64_encode(encryptData($data, $iv)); 

Qui è il mio codice Ruby:

module AESCrypt 
    def AESCrypt.encrypt(data, key, iv) 
    aes = OpenSSL::Cipher::Cipher.new("aes-256-cbc") 
    aes.encrypt 
    aes.key = key 
    aes.iv = iv 
    aes.update(data) + aes.final  
    end 
end 

plaintext = "Mary had a little lamb" 
iv = "96b88a5f0b9efb43" 
@crypted = AESCrypt::encrypt(plaintext, "g6zys8dlvvut6b1omxc5w15gnfad3jhb", iv) 
@crypted_base64 = Base64.encode64(@crypted) 
@crypted_base64_url = CGI.escape(@crypted_base64) 

La cosa irritante è che entrambi i campioni di codice producono simile ma non gli hash identici. Ad esempio, il codice di cui sopra genera (Base64 codificati, non URL codificato):

PHP: /aRCGgLBMOOAarjjtfTW2Qg2OtbPDLhx3KmgfgMzDJU=

Rubino: /aRCGgLBMOOAarjjtfTW2XIZhZ9VjBx8PdozxSL8IE0=

Qualcuno può spiegare che cosa sto facendo male qui? Inoltre, è più facile per me (dal momento che sono un ragazzo Ruby, non PHP di solito) per risolvere il codice Ruby piuttosto che il codice PHP. Quindi, se volessi fornire una soluzione in Ruby che si accoppiasse bene con PHP, sarei molto riconoscente.

Oh, e anche, nella produzione iv davvero sarà casuale, ma in questo esempio ho impostato per essere definitivamente stesso modo che uscita potrebbe essere confrontato.

EDIT:

Grazie alla risposta di Eugen Rieck, sono arrivato a una soluzione. Ruby blocca i blocchi, ma PHP non lo fa, e devi farlo manualmente. Modificare il codice PHP per quanto segue, e si ottiene stringhe criptate che quanto sopra Rubino codice può decifrare facilmente:

$iv = '96b88a5f0b9efb43'; 
$data = 'Mary had a little lamb'; 

function encryptData($data,$iv){ 
    $key = 'g6zys8dlvvut6b1omxc5w15gnfad3jhb'; 
    $padded_data = pkcs5_pad($data); 
    $cryptogram = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $padded_data, MCRYPT_MODE_CBC, $iv); 
    return $cryptogram; 
} 

function pkcs5_pad ($text, $blocksize){ 
    $pad = $blocksize - (strlen($text) % $blocksize); 
    return $text . str_repeat(chr($pad), $pad); 
} 
+2

non aiuta in soluzione, ma penso che questo potrebbe avere qualcosa a che fare con il blocco imbottitura (dal momento che le stringhe di output sono gli stessi per i primi n caratteri). Potresti provare a aggiungere padding al testo normale manualmente in base alla dimensione del blocco. – Mikk

+2

Il tuo codice Ruby sta invocando AES-256. Il tuo codice PHP sta invocando AES-128. Sei sicuro che sia corretto? Il tuo IV è chiaramente per 128 ... – Charles

+0

@Charles Non capisco neanche questo. Eppure, questo è l'unico modo in cui funzionano le cose.Passando da PHP a invocare AES-256, Ruby genera un errore di "decrittografia errata" da OpenSSL. (Vedi la discussione elencata nel mio commento alla risposta qui sotto, ne discute ulteriormente) Anche [questo] (http://www.chilkatsoft.com/p/php_aes.asp) fornisce una spiegazione completa delle stranezze di crittografia di PHP. Penso che chiarisca la tua domanda. – John

risposta

8

risulta, questo è abbastanza facile: L'imbottitura è il colpevole.

AES è un codice a blocchi, quindi funziona su blocchi di dimensione fissa. Ciò significa, che l'ultimo blocco sarà allways essere imbottito, e, si sa, la cosa buona di norme è, che ci sono così tanti da scegliere. PHP usa zero padding, dovrai cercare AESCrypt per scoprire cosa usa Ruby.

Lo stesso problema esiste con le varie librerie JS AES e PHP. Abbiamo finito per fare sempre la nostra imbottitura, poiché questo mi ha fatto diventare una furia sanguinosa un bel po 'di volte.

Ovviamente questo spiega perché la prima parte (che riporta le informazioni) è identica, mentre la seconda parte (che trasporta l'imbottitura) è diversa.

+0

Vuoi dire che PHP usa zero padding (cioè non riempie affatto) o che usa zero padding (cioè riempire l'ultimo blocco con 000000)? Oggi ho rintracciato [questa domanda] (http://stackoverflow.com/questions/1864700/part-ii-how-to-make-ruby-aes-256-cbc-and-php-mcrypt-rijndael-128 -play-well-toge) che sembra indicare che Ruby blocca i blocchi, ma mcrypt no. Ho modificato la mia domanda sopra per includere la mia risposta finale. – John

+2

@John È impossibile ** non ** usare il padding: un codice a blocchi per definizione richiede un blocco completo di dati su cui lavorare, quindi "nessun riempimento" non è un'opzione. PHP riempie un blocco incompleto con 0x00, che è una delle possibilità, ma non l'unica. IIRC OpenSSL utilizza RFC1423. Vedi per es. http://www.chilkatsoft.com/p/p_119.asp per maggiori informazioni –

Problemi correlati