javascript php encryption mcrypt cryptojs

Cifrado en JavaScript y descifrado con PHP



encryption mcrypt (2)

El problema es que en el código CryptoJS se usa una contraseña para derivar la clave y el IV para el cifrado AES, pero mcrypt solo usa la clave para cifrar / descifrar. Esta información debe pasarse a php. Como no desea transmitir la contraseña, debe derivar la clave y el IV de la misma manera en php.

El siguiente código deriva la clave y el IV de una contraseña y salt. Está modelado después del código en mi respuesta here (para más información).

function evpKDF($password, $salt, $keySize = 8, $ivSize = 4, $iterations = 1, $hashAlgorithm = "md5") { $targetKeySize = $keySize + $ivSize; $derivedBytes = ""; $numberOfDerivedWords = 0; $block = NULL; $hasher = hash_init($hashAlgorithm); while ($numberOfDerivedWords < $targetKeySize) { if ($block != NULL) { hash_update($hasher, $block); } hash_update($hasher, $password); hash_update($hasher, $salt); $block = hash_final($hasher, TRUE); $hasher = hash_init($hashAlgorithm); // Iterations for ($i = 1; $i < $iterations; $i++) { hash_update($hasher, $block); $block = hash_final($hasher, TRUE); $hasher = hash_init($hashAlgorithm); } $derivedBytes .= substr($block, 0, min(strlen($block), ($targetKeySize - $numberOfDerivedWords) * 4)); $numberOfDerivedWords += strlen($block)/4; } return array( "key" => substr($derivedBytes, 0, $keySize * 4), "iv" => substr($derivedBytes, $keySize * 4, $ivSize * 4) ); }

La sal se genera durante el cifrado en CryptoJS y debe enviarse a php con el texto cifrado. Antes de invocar evpKDF la sal debe convertirse a una cadena binaria desde hexadecimal.

$keyAndIV = evpKDF("Secret Passphrase", hex2bin($saltHex)); $decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $keyAndIV["key"], hex2bin($cipherTextHex), MCRYPT_MODE_CBC, $keyAndIV["iv"]);

Si solo se envió encryptedPassword.toString() al servidor, entonces es necesario dividir la sal y el texto cifrado real antes de su uso. El formato es un formato compatible con OpenSSL patentado con los primeros 8 bytes "Salados__", los siguientes 8 bytes son la sal aleatoria y el resto es el texto cifrado real. Todo junto está codificado en Base64.

function decrypt($ciphertext, $password) { $ciphertext = base64_decode($ciphertext); if (substr($ciphertext, 0, 8) != "Salted__") { return false; } $salt = substr($ciphertext, 8, 8); $keyAndIV = evpKDF($password, $salt); $decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $keyAndIV["key"], substr($ciphertext, 16), MCRYPT_MODE_CBC, $keyAndIV["iv"]); // unpad (PKCS#7) return substr($decryptPassword, 0, strlen($decryptPassword) - ord($decryptPassword[strlen($decryptPassword)-1])); }

Lo mismo se puede lograr con la extensión OpenSSL en lugar de Mcrypt:

function decrypt($ciphertext, $password) { $ciphertext = base64_decode($ciphertext); if (substr($ciphertext, 0, 8) != "Salted__") { return false; } $salt = substr($ciphertext, 8, 8); $keyAndIV = evpKDF($password, $salt); $decryptPassword = openssl_decrypt( substr($ciphertext, 16), "aes-256-cbc", $keyAndIV["key"], OPENSSL_RAW_DATA, // base64 was already decoded $keyAndIV["iv"]); return $decryptPassword; }

Estoy encriptando mi contraseña de usuario en JavaScript así:

var encryptedPassword = CryptoJS.AES.encrypt(password, "Secret Passphrase");

Funciona bien, pero ahora estoy tratando de descifrar en PHP en el lado del servidor de esta manera:

$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND); $decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, "Secret Passphrase", base64_decode($password), MCRYPT_MODE_CBC, $iv);

no funciona en absoluto, la cadena de contraseña descifrada se ve muy extraña:

string(64) ">�OX2MS��댗v�<$�ʕ��i�̄��_��P���/�կ=�_6(�m����,4WT7��a"

Aquí está el estado actual de mi código en JavaScript después de los útiles comentarios:

var encryptedPassword = CryptoJS.AES.encrypt(password, "Secret Passphrase"); var ivHex = encryptedPassword.iv.toString(); var ivSize = encryptedPassword.algorithm.ivSize; // same as blockSize var keySize = encryptedPassword.algorithm.keySize; var keyHex = encryptedPassword.key.toString(); var saltHex = encryptedPassword.salt.toString(); // must be sent var openSslFormattedCipherTextString = encryptedPassword.toString(); // not used var cipherTextHex = encryptedPassword.ciphertext.toString(); // must be sent

Estoy enviando saltHex y CipherTextHex al servidor PHP y estoy usando mcrypt_decrypt () de esta manera:

$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), $saltHex); $decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, "Secret Passphrase", base64_decode($cipherTextHex), MCRYPT_MODE_CBC, $iv);

Todavía no funciona con este código actualizado.

¿Alguien puede ayudarme a descifrar correctamente con la función PHP mcrypt_decrypt () para un método de cifrado AES simple? Estoy seguro de que estoy haciendo algo mal con el cifrado, el modo mcrypt y los parámetros IV dentro de mi método mcrypt_decrypt (). Gracias si lo sabes.


No puede descifrar con un vector de inicialización aleatorio: debe usar el mismo IV con el que se cifraron los datos. Además, IIRC, AES adopta por defecto una representación de 8 bits de los datos cifrados que deberán manejarse con cuidado en la transferencia a través de HTTP.