2009-12-08 13 views
10

Esta pregunta es la continuación de la última, con respecto a How to make Ruby AES-256-CBC and PHP MCRYPT_RIJNDAEL_128 play well together. Lo estoy trabajando ahora, pero todavía estoy luchando por ir en la otra dirección. El criptograma generado por PHP parece tener toda la información que se proporcionó, pero no puedo obtener el código de Ruby para descifrarlo sin error.Parte II: Cómo hacer que Ruby AES-256-CBC y PHP MCRYPT_RIJNDAEL_128 jueguen bien juntos

Aquí está el código PHP que estoy usando para generar el criptograma:

$cleartext = "Who's the clever boy?"; 
$key = base64_decode("6sEwMG/aKdBk5Fa2rR6vVw==\n"); 
$iv = base64_decode("vCkaypm5tPmtP3TF7aWrug=="); 
$cryptogram = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $cleartext, MCRYPT_MODE_CBC, $iv); 
$result = base64_encode($cryptogram); 
print "\n'$result'\n"; 

RESULT 
'JM0OxMINPTnF1vwXdI3XdKI0KlVx210CvpJllFja+GM=' 

Entonces aquí está el intento de descifrar en Ruby:

>> cipher = OpenSSL::Cipher::Cipher.new('aes-128-cbc') 
>> cipher.key = Base64.decode64("6sEwMG/aKdBk5Fa2rR6vVw==\n") 
>> cipher.iv = Base64.decode64("vCkaypm5tPmtP3TF7aWrug==") 
>> cryptogram = Base64.decode64('JM0OxMINPTnF1vwXdI3XdKI0KlVx210CvpJllFja+GM=') 
>> cleartext = cipher.update(cryptogram) 
=> "Who's the clever" 
>> cleartext << cipher.final 
OpenSSL::Cipher::CipherError: bad decrypt 
from (irb):100:in `final' 
from (irb):100 

Lo que es realmente frustrante de esto es que es posible obtener todo el texto sin formato de esa cadena encriptada. Repitiendo lo anterior, pero añadiendo una almohadilla sin sentido para el criptograma:

>> cleartext = cipher.update(cryptogram + 'pad') 
    => "Who's the clever boy?\000\000\000\000\000\000\000\000\000\000\000" 
    >> cleartext << cipher.final 
    OpenSSL::Cipher::CipherError: bad decrypt 
    from (irb):119:in `final' 
    from (irb):119 

En mi caso uso real del texto claro está estructurado (una cadena JSON, ya que preguntas), así que me siento cómoda una este punto que me di cuenta utilice este esquema y detecte la entrada mal encriptada sin realizar el cipher.final. Sin embargo, no puedo tolerar este tipo de kludge en mi código, así que me gustaría entender cómo hacer que el código de rubí maneje el último bloque con gracia.

Respuesta

15

El problema es que mcrypt no está rellenando el último bloque, mientras que el enlace OpenSSL de Ruby usa el método de relleno de OpenSSL predeterminado, que es el relleno de PKCS. Realmente no puedo mejorar en la descripción de la documentación de OpenSSL:

de relleno PKCS funciona mediante la adición de n acolchado bytes de valor de n para hacer la longitud total de los datos de un múltiplo del tamaño de bloque. El relleno es siempre agregado, por lo que si los datos ya son , un múltiplo del tamaño de bloque n será igual al tamaño del bloque. Por ejemplo, si el tamaño del bloque es 8 y 11 bytes son para encriptar, se agregarán 5 bytes de relleno del valor 5.

Deberá agregar manualmente el relleno adecuado al final del texto sin formato en PHP antes de cifrar. Para hacerlo, pase su $cleartext a través de esta función pkcs5_pad en el lado de PHP antes de encriptarlo (pasando 16 como el tamaño de bloque).

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

Si también va a la inversa (cifrar y descifrar en Ruby con mcrypt), que tendrá que quitarse los bytes de relleno después de descifrar.

nota lateral: La razón por la que hay que añadir el relleno incluso si el texto no cifrado ya es un múltiplo del tamaño de bloque (un bloque entero de relleno), es por lo que cuando se está descifrando sabe que el último byte de la el último bloque es siempre la cantidad de relleno añadido. De lo contrario, no podría distinguir entre texto sin formato con un solo byte de relleno y un texto sin formato sin bytes de relleno que acaba de terminar en el valor 0x01.

+1

Gracias por su opinión, caf. Agregar texto al texto claro es simplemente crear una cadena diferente para encriptar; no cambia el resultado. Acepto que el problema tiene algo que ver con la forma en que las dos implementaciones se ocupan del último bloque de la secuencia cifrada. La salida de los dos algoritmos es idéntica hasta el último bloque de 32 bytes, donde los últimos 16 bytes son completamente diferentes. Me he quedado sin paciencia con el problema, así que a menos que algún samaritano venga y me lo resuelva, voy a ir con el kludge arriba. – dondo

+0

El texto agregado al final del texto sin formato (relleno) tiene que ser de una forma muy específica, que el lado de Ruby está esperando. Investigaré qué método de relleno usa y actualizaré la respuesta. – caf

+0

... El relleno de PKCS funciona al agregar n bytes de relleno de valor n para hacer que la longitud total de los datos ** encriptados sea un múltiplo del tamaño de bloque ... – dondo

0

Parece que PHP \0 rellena el texto sin formato antes de encriptarlo. Puede configurar Ruby para desactivar el relleno.

http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-padding-3D

Esto funciona, pero entonces debe despojar el relleno de forma manual.

1.9.3p125 :008 > cipher = OpenSSL::Cipher::Cipher.new('aes-128-cbc') 
=> #<OpenSSL::Cipher::Cipher:0x0000000561ee78> 
1.9.3p125 :009 > cipher.decrypt 
=> #<OpenSSL::Cipher::Cipher:0x0000000561ee78> 
1.9.3p125 :010 > cipher.padding = 0 
=> 0 
1.9.3p125 :011 > cipher.key = Base64.decode64("6sEwMG/aKdBk5Fa2rR6vVw==\n") 
=> "\xEA\xC100o\xDA)\xD0d\xE4V\xB6\xAD\x1E\xAFW" 
1.9.3p125 :012 > cipher.iv = Base64.decode64("vCkaypm5tPmtP3TF7aWrug==") 
=> "\xBC)\x1A\xCA\x99\xB9\xB4\xF9\xAD?t\xC5\xED\xA5\xAB\xBA" 
1.9.3p125 :013 > cryptogram = Base64.decode64('JM0OxMINPTnF1vwXdI3XdI2j8NJ8kr+Du0fnkxorNl0=') 
=> "$\xCD\x0E\xC4\xC2\r=9\xC5\xD6\xFC\x17t\x8D\xD7t\x8D\xA3\xF0\xD2|\x92\xBF\x83\xBBG\xE7\x93\x1A+6]" 
1.9.3p125 :014 > cleartext = cipher.update(cryptogram) 
=> "Who's the clever girl?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 
1.9.3p125 :015 > cleartext << cipher.final 
=> "Who's the clever girl?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 



1.9.3p125 :042 > cleartext.strip 
=> "Who's the clever girl?" 
Cuestiones relacionadas