2009-12-08 23 views
10

Cette question est une continuation de mon dernier, concernant How to make Ruby AES-256-CBC and PHP MCRYPT_RIJNDAEL_128 play well together. Je travaille maintenant, mais je me bats encore pour aller dans l'autre direction. Le cryptogramme généré par PHP semble avoir toutes les informations fournies, mais je ne peux pas obtenir le code Ruby pour le déchiffrer sans erreur.Partie II: Comment faire Ruby AES-256-CBC et PHP MCRYPT_RIJNDAEL_128 jouent bien ensemble

Voici le code PHP que je utilise pour générer le cryptogramme:

$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=' 

Alors voici la tentative de déchiffrer dans 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 

Ce qui est vraiment frustrant à ce sujet est qu'il est possible de récupère tout le texte en clair de cette chaîne cryptée. En répétant ce qui précède, mais en ajoutant un tampon non-sens au cryptogramme:

Dans mon cas réel d'utilisation
>> 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 

le texte clair est structuré (une chaîne JSON, puisque vous me demandez), donc je me sens à l'aise un ce point que je pourrais dire utiliser ce schéma et détecter les entrées mal cryptées sans effectuer le cipher.final. Cependant, je ne peux pas tolérer ce genre de kludge dans mon code, donc je voudrais comprendre comment faire pour que le code ruby ​​gère le bloc final avec élégance.

Répondre

15

Le problème est que mcrypt ne remplit pas le dernier bloc, tandis que la liaison OpenSSL de Ruby utilise la méthode de remplissage OpenSSL par défaut, qui est le remplissage PKCS. Je ne peux pas vraiment améliorer la description de la documentation OpenSSL:

fonctionne padding PKCS en ajoutant n padding octets de valeur n pour rendre la longueur totale des données un multiple de la taille du bloc. Le remplissage est toujours ajouté, donc si les données sont déjà , un multiple de la taille de bloc n sera égal à la taille du bloc. Par exemple si la taille de bloc est 8 et 11 octets sont pour être cryptés alors 5 octets de remplissage de la valeur 5 seront ajoutés.

Vous devrez ajouter manuellement un remplissage approprié à la fin du texte en PHP avant de procéder au chiffrement. Pour ce faire, passez votre $cleartext via cette fonction pkcs5_pad du côté PHP avant de le crypter (en passant 16 comme taille de bloc).

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

Si vous allez aussi dans l'autre sens (chiffrer et déchiffrer en Ruby avec mcrypt), vous devrez dépouiller les octets de remplissage après décryptage.

Side note: La raison pour laquelle vous devez ajouter le remplissage, même si le texte clair est déjà un multiple de la taille de blocs (un bloc entier de rembourrage), est de sorte que lorsque vous Décryptage vous savez que le dernier octet de la le dernier bloc est toujours la quantité de remplissage ajoutée. Dans le cas contraire, vous ne pouvez pas faire la différence entre cleartext avec un seul octet de remplissage et un texte clair sans octets de remplissage qui se termine par la valeur 0x01.

+1

Merci pour vos commentaires, caf. L'ajout de texte au texte en clair crée simplement une chaîne différente à chiffrer; ça ne change pas le résultat. Je suis d'accord que le problème a quelque chose à voir avec la façon dont les deux implémentations traitent avec le dernier bloc du flux crypté. La sortie des deux algorithmes est identique jusqu'au dernier bloc de 32 octets, où les 16 derniers octets sont complètement différents. Je n'ai plus de patience avec le problème, donc à moins qu'un samaritain ne vienne et ne le résout pour moi, je vais avec le kludge ci-dessus. – dondo

+0

Le texte ajouté à la fin du cleartext (padding) doit être d'une forme très spécifique, que le côté Ruby attend. J'étudierai la méthode de remplissage utilisée et mettra à jour la réponse. – caf

+0

... Le remplissage PKCS fonctionne en ajoutant n octets de remplissage de valeur n pour que la longueur totale des données ** chiffrées ** soit un multiple de la taille du bloc ... – dondo

0

Il semble que PHP \0 remplisse le cleartext avant de le chiffrer. Vous pouvez définir Ruby pour désactiver le remplissage.

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

Cela fonctionne, mais vous devez enlever le rembourrage manuellement.

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?"