Страницы

Поиск по вопросам

воскресенье, 15 декабря 2019 г.

Как реализовать шифрование данных на PHP

#php #javascript #aes #cryptojs


Вот пример на JS:

function decryptRandomNumber (pwdHash, encryptedRandomNumber) {
    var key = CryptoJS.enc.Utf8.parse(pwdHash);

    var encrypted = CryptoJS.enc.Hex.parse(encryptedRandomNumber.toUpperCase());

    var result = CryptoJS.AES.decrypt({
        ciphertext: encrypted
    }, key , {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    }).toString(CryptoJS.enc.Utf8);

    return result;
}

function encryptPwdHash (pwdHash, randomNumber) {

    var randomNumberMd5 = CryptoJS.MD5(randomNumber).toString().toUpperCase();
    var key = CryptoJS.enc.Utf8.parse(randomNumberMd5);
    var result = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(pwdHash), key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    }).ciphertext;

    return result;
}
// пример на js - шифрование
 var password = '123456';
 var pwdHash = CryptoJS.MD5(password).toString().toUpperCase();
 var encriptResult = encryptPwdHash (pwdHash, '3456'); // 


Скриншот результата

     если привести в строку то f46492dc512a6df5cd7c6b9ee50e7cc44fb2337c1605726518d353ce800d5cc05d4d5540dd7674850079e785ab5f3b77

Нужно переписать на PHP.

Расшифровку смог сделать вот этой функцией 

 function aes128_cbc_decrypt($key, $data, $iv) {
     $data = pack("H*", $data);
     $iv = pack("H*", $iv);
     $dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC,   $iv);
     return $dec;
 }


Входящие параметры --> $key = strtoupper(md5($password));
 хеш пароля 
$data = '54BAA6158E81E1069EA2AB1C4F9D1F29';- хеш рандомного числа $iv = '00000000000000000000000000000000';
- вектор в виде 16 - ти ричной константы. 

Вот теперь нужно зашифровать обратно пароль с помощью случайного числа и отправить.
Написал функцию которая делает подобное:

function aes128_cbc_encript($key, $data, $iv) {
    $key = pack("H*", strtoupper(md5($key)));
    //var_dump($data);
    //$data = pack("H*", $data);
    $iv = pack("H*", $iv);
    //$enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
    $enc = openssl_encrypt($data,  'AES-128-CBC', $key, true, $iv);
    return bin2hex($enc);
 }


Пробую запускать 

$decript = '34345'; // расшифрованное рандомное число
$pswdHash = strtoupper(md5($password));

$encript = aes128_cbc_encript($decript, $pswdHash, '00000000000000000000000000000000');


На выходе получаю нечто - 72fc1b49db86787b749c3323421496af00933eb79a9b7d845a51f5ea3fefdf750800554f5fdf0c7d3d765fe7f6653da3

Такой шифр и должен быть по длинне и в 16 - ти разрядном виде, но он не правильный
и при отправке на сервер он не расшифровуется.

Вот пример функции на Oracle, которая на сервере делает расшифровку 

    Fc_Decrypt_Password_Hash(In_Encrypted_Password_Hash In Varchar2,
          In_Random_Number_Md5  In Varchar2) Return Varchar2 Is
          Raw_Random_Number_Md5 Raw(2000);
          Raw_Result            Raw(2000);
          Result                Varchar2(2000);
  Begin
      Raw_Random_Number_Md5 := Utl_I18n.String_To_Raw(Upper(In_Random_Number_Md5),
'AL32UTF8');
      Raw_Result := Dbms_Crypto.Decrypt(Typ => Dbms_Crypto.Aes_Cbc_Pkcs5, Src =>
Upper(In_Encrypted_Password_Hash), Key => Raw_Random_Number_Md5);
     Result := Utl_I18n.Raw_To_Char(Raw_Result, 'AL32UTF8');
   Return(Result);
  End;

    


Ответы

Ответ 1



Во-первых, если вы действительно хотите защитить обмен сообщениями - пользуйтесь готовыми решениями. Функция openssl_encrypt не использует весь $key, а только первые 16 байт, остальные отбрасываются (для AES-128). Хотя это только практический опыт, возможно это верно не для всех версий. В качестве $key у вас используется хэш md5 в 16-ричном формате, а это 32 байта. CryptoJS, использует весь ключ (или по крайней мере больше 16 байт). Ожидать, что результаты шифрований совпадут, не приходится. Функция mcrypt_encrypt не умеет Pkcs7. Она всегда выравнивает блок нулями (и это небезопасно). Хоть в вашем случае выравнивание и не требуется (длина исходного текста всегда делится на 16), по вышеуказанному стандарту все равно нужно добавить выравнивание, это будут 16 байт 0x10. Пример рабочего кода (практически повторяет ваш пример на js): function encrypt(){ $data=strtoupper(md5('123456')); $data.=str_repeat(chr(16), 16);// добавляем выравнивание $key=strtoupper(md5('3456')); $iv=hex2bin(str_repeat('00', 16)); $enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv); return bin2hex($enc); } var_dump(encrypt()); // -> string(96) "f46492dc512a6df5cd7c6b9ee50e7cc44fb2337c1605726518d353ce800d5cc05d4d5540dd7674850079e785ab5f3b77"

Ответ 2



Так как у вас стоит задача просто реализовать зашифрованный обмен данными между клиентом и сервером, предлагаю просто готовую реализацию. PHP библиотека: /** * Decrypt data from a CryptoJS json encoding string * * @param mixed $passphrase * @param mixed $jsonString * @return mixed */ function cryptoJsAesDecrypt($passphrase, $jsonString){ $jsondata = json_decode($jsonString, true); $salt = hex2bin($jsondata["s"]); $ct = base64_decode($jsondata["ct"]); $iv = hex2bin($jsondata["iv"]); $concatedPassphrase = $passphrase.$salt; $md5 = array(); $md5[0] = md5($concatedPassphrase, true); $result = $md5[0]; for ($i = 1; $i < 3; $i++) { $md5[$i] = md5($md5[$i - 1].$concatedPassphrase, true); $result .= $md5[$i]; } $key = substr($result, 0, 32); $data = openssl_decrypt($ct, 'aes-256-cbc', $key, true, $iv); return json_decode($data, true); } /** * Encrypt value to a cryptojs compatiable json encoding string * * @param mixed $passphrase * @param mixed $value * @return string */ function cryptoJsAesEncrypt($passphrase, $value){ $salt = openssl_random_pseudo_bytes(8); $salted = ''; $dx = ''; while (strlen($salted) < 48) { $dx = md5($dx.$passphrase.$salt, true); $salted .= $dx; } $key = substr($salted, 0, 32); $iv = substr($salted, 32,16); $encrypted_data = openssl_encrypt(json_encode($value), 'aes-256-cbc', $key, true, $iv); $data = array("ct" => base64_encode($encrypted_data), "iv" => bin2hex($iv), "s" => bin2hex($salt)); return json_encode($data); } Javascript библиотека: var CryptoJSAesJson = { stringify: function (cipherParams) { var j = {ct: cipherParams.ciphertext.toString(CryptoJS.enc.Base64)}; if (cipherParams.iv) j.iv = cipherParams.iv.toString(); if (cipherParams.salt) j.s = cipherParams.salt.toString(); return JSON.stringify(j); }, parse: function (jsonStr) { var j = JSON.parse(jsonStr); var cipherParams = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Base64.parse(j.ct)}); if (j.iv) cipherParams.iv = CryptoJS.enc.Hex.parse(j.iv) if (j.s) cipherParams.salt = CryptoJS.enc.Hex.parse(j.s) return cipherParams; } } Пример на Javascript: var encrypted = CryptoJS.AES.encrypt(JSON.stringify("value to encrypt"), "my passphrase", {format: CryptoJSAesJson}).toString(); var decrypted = JSON.parse(CryptoJS.AES.decrypt(encrypted, "my passphrase", {format: CryptoJSAesJson}).toString(CryptoJS.enc.Utf8)); Пример на PHP $encrypted = cryptoJsAesEncrypt("my passphrase", "value to encrypt"); $decrypted = cryptoJsAesDecrypt("my passphrase", $encrypted); Взято остюда

Ответ 3



Переписал все полностью, если это оно, то прокомментирую после вашего подтверждения.
'; # вы зашифровали: 1152 # моя функция для шифрования в режиме CBC function dataEncrypt($plaintext, $key, $iv) { $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, pack('H*', $iv)); $ciphertext = strtoupper(bin2hex($ciphertext)); return $ciphertext; } # шифрую то, что вы шифровали ранне (число 1152) своей функцией echo dataEncrypt('1152', strtoupper(md5('123456')), '00000000000000000000000000000000') . '

'; # результат: C3364787440D1A610FB3B99C8C6D483C # расшифровываю результат работы своей функции: C3364787440D1A610FB3B99C8C6D483C с помощью вашей функцией aes128_cbc_decrypt echo aes128_cbc_decrypt(strtoupper(md5('123456')), 'C3364787440D1A610FB3B99C8C6D483C', '00000000000000000000000000000000') . '

'; # я зашифровал: 1152

Комментариев нет:

Отправить комментарий