Pregunta

Esta pregunta es una continuación de mi último, en cuanto a Como hacer Rubí AES-256-CBC y PHP MCRYPT_RIJNDAEL_128 juegan bien juntos . Tengo que trabajar ahora, pero todavía estoy luchando para ir en otra dirección. El criptograma generada PHP parece tener toda la información que se proporcionó, pero no puedo obtener el código Ruby para descifrarlo sin error.

Este es 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='

A continuación, 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 en claro fuera de esa cadena cifrada. Repitiendo lo anterior, pero la adición de 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 de utilizar este esquema de entrada y detectar mal cifrada sin realizar el cipher.final. Sin embargo, no puedo tolerar este tipo de kludge en mi código, por lo que me gustaría entender cómo hacer que el código rubí manejar el bloque final con gracia.

¿Fue útil?

Solución

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

  

Los trabajos de relleno PKCS mediante la adición de n acolchado   bytes de valor n para hacer que el total de   longitud de los datos una   múltiplo del tamaño de bloque. El relleno es   añadido siempre por lo que si los datos ya están   un múltiplo del tamaño de bloque n se   igualar el tamaño de bloque. Por ejemplo, si   el tamaño del bloque es de 8 y 11 bytes son   a cifrar entonces bytes de relleno 5   de valor se añadirán 5.

Se tendrá que añadir manualmente el acolchado apropiado al final del texto en claro en PHP antes de cifrar. Para hacer eso, pasar su $cleartext través de esta función pkcs5_pad en el lado de PHP antes de encriptarlo (16 pasar 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 al margen: 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 del último bloque es siempre la cantidad de relleno añadido. De lo contrario, no se puede decir la diferencia entre el texto plano con un solo byte de relleno y un texto claro sin bytes de relleno que acaba de pasar a terminar en el valor 0x01.

Otros consejos

Parece PHP almohadillas \0 el texto en claro antes de cifrarlos. Puede configurar Ruby a 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 se debe quitar 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?"
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top