Мой первый раз использовал CryptoJS, и я изо всех сил пытаюсь расшифровать строку, которую я зашифровал с помощью openssl_encrypt () в PHP.
PHP 5.6.13.0 и CryptoJS 3.1.2
Во-первых, мой PHP:
$encryptHash = hash_pbkdf2("sha256", "0000", "secret", 1000, 32); var_dump($encryptHash); $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc')); var_dump(bin2hex($iv)); $encrypted = openssl_encrypt("hello! this is my string!", 'aes-256-cbc', $encryptHash, 0, $iv); var_dump($encrypted); $encrypted = base64_encode($encrypted.":".bin2hex($iv)); echo "\r\n".$encrypted;
Это дает мне следующий результат:
string(32) "59b6ab46d379b89d794c87b74a511fbd" string(32) "0aaff094b6dc29742cc98a4bac8bc8f9" string(44) "xHIxg1HDUOqyhBmAaU2Sx3ct8GaKaeE5w4d1KM1yuDw=" eEhJeGcxSERVT3F5aEJtQWFVMlN4M2N0OEdhS2FlRTV3NGQxS00xeXVEdz06MGFhZmYwOTRiNmRjMjk3NDJjYzk4YTRiYWM4YmM4Zjk=
Теперь мой JS:
var encryptedString = "eEhJeGcxSERVT3F5aEJtQWFVMlN4M2N0OEdhS2FlRTV3NGQxS00xeXVEdz06MGFhZmYwOTRiNmRjMjk3NDJjYzk4YTRiYWM4YmM4Zjk="; var key256Bits = CryptoJS.PBKDF2("0000", "secret", { keySize: 128/32, iterations: 1000, hasher: CryptoJS.algo.SHA256 }); var keyAsHex = key256Bits.toString(CryptoJS.enc.Hex); /* keyAsHex = "59b6ab46d379b89d794c87b74a511fbd" */ var rawData = atob(encryptedString); var rawPieces = rawData.split(":"); var crypttext = rawPieces[0]; var iv = rawPieces[1]; /* crypttext = "xHIxg1HDUOqyhBmAaU2Sx3ct8GaKaeE5w4d1KM1yuDw=" */ /* iv = "0aaff094b6dc29742cc98a4bac8bc8f9" */ /* So far so good? */ var plaintextArray = CryptoJS.AES.decrypt( { ciphertext: CryptoJS.enc.Base64.parse(crypttext) }, CryptoJS.enc.Hex.parse(keyAsHex), { iv: CryptoJS.enc.Hex.parse(iv) } ); /* plaintextArray: d.WordArray.n.extend.init sigBytes: -67 words: Array[8] 0: 1419734786 1: -2048883413 2: -1709437124 3: 736946566 4: 718053567 5: -64039355 6: 1868905697 7: -910423965 */ var output = CryptoJS.enc.Utf8.stringify(plaintextArray); /* output = "" */
Как вы можете видеть, мой вывод пустой строки. Кто-то пытался сделать что-то подобное? Я в тупике!
Оказывается, мои ключевые длины были неправильными! Вот мой рабочий код (шифрование) и JS (расшифровка):
PHP:
$encryptHash = hash_pbkdf2("sha256", "0000", "secret", 1000, 32, true); var_dump($encryptHash); $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("aes-256-cbc")); var_dump($iv); $encrypted = openssl_encrypt("hello! this is a test!", "aes-256-cbc", $encryptHash, 0, $iv); var_dump($encrypted); $encrypted = base64_encode($encrypted.":".bin2hex($iv)); echo "\r\n".$encrypted;
Дает мне следующее:
string(32) "Y½FËy©ØyLçÀJQ▼¢▄▄êI╩öo§(NtÙת‼ç" string(16) "àX§ $VÇ‼♣┘█²áÓßt" string(44) "VIzzao8Wdo8HPM015v6c5Q77ervGUIVbL6ERKRXb0fU=" Vkl6emFvOFdkbzhIUE0wMTV2NmM1UTc3ZXJ2R1VJVmJMNkVSS1JYYjBmVT06ODU1ODE1MjAyNDU2ODAxMzA1ZDlkYmZkYTBlMGUxNzQ=
JS:
var encryptedString = "Vkl6emFvOFdkbzhIUE0wMTV2NmM1UTc3ZXJ2R1VJVmJMNkVSS1JYYjBmVT06ODU1ODE1MjAyNDU2ODAxMzA1ZDlkYmZkYTBlMGUxNzQ="; var key256Bits = CryptoJS.PBKDF2("0000", "secret", { keySize: 256/32, iterations: 1000, hasher: CryptoJS.algo.SHA256 }); var rawData = atob(encryptedString); var rawPieces = rawData.split(":"); var crypttext = rawPieces[0]; var iv = CryptoJS.enc.Hex.parse(rawPieces[1]); var cipherParams = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Base64.parse(crypttext)}); var plaintextArray = CryptoJS.AES.decrypt( cipherParams, key256Bits, { iv: iv } ); var output = CryptoJS.enc.Utf8.stringify(plaintextArray); /* output === 'hello! this is a test!' */
TL; DR – попробуйте использовать 32-байтовый ключ, а не 16-байтовый ключ.
После составления более раннего ответа и, в конечном счете, его удаления, опровергнув мою собственную теорию об этом, это проблема с заполнением :-), теперь я уверен, что проблема может быть связана только с длиной ключа.
При попытке воспроизвести вашу проблему я не смог бы получить первый блок зашифрованного текста одинаковым при создании с помощью openssl_encrypt
vs CryptoJS
. Затем я удвоил длину ключа, и он сработал.
Ключ, который вы генерируете выше, составляет 32 символа, но только 16 байт после преобразования, поэтому попробуйте удвоить это и посмотреть, что произойдет.
FWIW, вот код PHP, который я использовал для проверки длины ключа:
$data = "hello! this is a test!"; $method = 'aes-256-cbc'; $key = '59b6ab46d379b89d794c87b74a511fbd59b6ab46d379b89d794c87b74a511fbd'; $iv = '0aaff094b6dc29742cc98a4bac8bc8f9'; $e = openssl_encrypt( $data, $method, hex2bin( $key ), 0, hex2bin( $iv )); echo 'Ciphertext: [', bin2hex( base64_decode( $e )), "]\n"; echo 'Key: [', $key, "]\n"; echo 'Cleartext: [', openssl_decrypt( $e, $method, hex2bin( $key ), 0, hex2bin( $iv )), "]\n"; // Test with openssl on the command line as well, just to be sure! file_put_contents( 'clear.txt', $data ); $exec = "openssl enc -$method -e -in clear.txt -out encrypted.txt -base64 -nosalt -K $key -iv $iv"; exec ($exec); $out = file_get_contents( 'encrypted.txt' ); echo 'Ciphertext: [', bin2hex( base64_decode(trim($out))), "]\n";
И вот совместимый JavaScript, который я запускаю с помощью jsc
на своем Mac:
var data = "hello! this is a test!"; var key = '59b6ab46d379b89d794c87b74a511fbd59b6ab46d379b89d794c87b74a511fbd'; var iv = '0aaff094b6dc29742cc98a4bac8bc8f9'; var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(data), CryptoJS.enc.Hex.parse(key), { iv: CryptoJS.enc.Hex.parse(iv) }); print( 'Ciphertext: [' + encrypted.ciphertext + ']' ); print( 'Key: [' + encrypted.key + ']' ); cipherParams = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Hex.parse(encrypted.ciphertext.toString())}); var decrypted = CryptoJS.AES.decrypt(cipherParams, CryptoJS.enc.Hex.parse(key), { iv: CryptoJS.enc.Hex.parse(iv) }); print( 'Cleartext: [' + decrypted.toString(CryptoJS.enc.Utf8) + ']');
Эти два фрагмента кода создают идентичный зашифрованный текст независимо от длины ввода, что подтверждает, что стратегия заполнения совместима между двумя библиотеками. Однако, если вы вдвое увеличиваете длину ключей, шифротекст больше не будет идентичным, что, очевидно, означает, что дешифрование также не будет совместимым.
ОБНОВИТЬ
Я только что обнаружил, что hash_pbkdf2()
возвращает шестнадцатеричные строки ASCII по умолчанию, поэтому вам нужно либо преобразовать $encryptHash
в двоичный файл с hex2bin()
прежде чем передать его в openssl_encrypt()
либо установить для последнего параметра hash_pbkdf2()
значение true
чтобы получить исходный вывод.
ОБНОВЛЕНИЕ 2
Я только что подтвердил, что ваш код будет работать, если вы внесете следующие изменения:
В PHP измените размер ключа от 32 до 64 байт и добавьте опцию raw output при генерации ключа:
$encryptHash = hash_pbkdf2("sha256", "0000", "secret", 1000, 64, 1);
Измените длину ключа от 128 до 256 бит в JavaScript:
var key256Bits = CryptoJS.PBKDF2("0000", "secret", { keySize: 256/32, iterations: 1000, hasher: CryptoJS.algo.SHA256 });
Надеюсь, эти изменения будут работать, когда вы их попробуете.