Skip to content

Instantly share code, notes, and snippets.

@ve3
Last active August 10, 2023 10:51
Show Gist options
  • Save ve3/0f77228b174cf92a638d81fddb17189d to your computer and use it in GitHub Desktop.
Save ve3/0f77228b174cf92a638d81fddb17189d to your computer and use it in GitHub Desktop.
Encrypt and decrypt between programming languages (PHP & JavaScript). (Newer version here https://gist.github.com/ve3/b16b2dfdceb0e4e24ecd9b9078042197 )
/**
* Encryption class for encrypt/decrypt that works between programming languages.
*
* @author Vee Winch.
* @link https://stackoverflow.com/questions/41222162/encrypt-in-php-openssl-and-decrypt-in-javascript-cryptojs Reference.
* @link https://github.com/brix/crypto-js/releases crypto-js.js can be download from here.
*/
class Encryption {
/**
* @var integer Return encrypt method or Cipher method number. (128, 192, 256)
*/
get encryptMethodLength() {
var encryptMethod = this.encryptMethod;
// get only number from string.
// @link https://stackoverflow.com/a/10003709/128761 Reference.
var aesNumber = encryptMethod.match(/\d+/)[0];
return parseInt(aesNumber);
}// encryptMethodLength
/**
* @var integer Return cipher method divide by 8. example: AES number 256 will be 256/8 = 32.
*/
get encryptKeySize() {
var aesNumber = this.encryptMethodLength;
return parseInt(aesNumber / 8);
}// encryptKeySize
/**
* @link http://php.net/manual/en/function.openssl-get-cipher-methods.php Refer to available methods in PHP if we are working between JS & PHP encryption.
* @var string Cipher method.
* Recommended AES-128-CBC, AES-192-CBC, AES-256-CBC
* due to there is no `openssl_cipher_iv_length()` function in JavaScript
* and all of these methods are known as 16 in iv_length.
*/
get encryptMethod() {
return 'AES-256-CBC';
}// encryptMethod
/**
* Decrypt string.
*
* @link https://stackoverflow.com/questions/41222162/encrypt-in-php-openssl-and-decrypt-in-javascript-cryptojs Reference.
* @link https://stackoverflow.com/questions/25492179/decode-a-base64-string-using-cryptojs Crypto JS base64 encode/decode reference.
* @param string encryptedString The encrypted string to be decrypt.
* @param string key The key.
* @return string Return decrypted string.
*/
decrypt(encryptedString, key) {
var json = JSON.parse(CryptoJS.enc.Utf8.stringify(CryptoJS.enc.Base64.parse(encryptedString)));
var salt = CryptoJS.enc.Hex.parse(json.salt);
var iv = CryptoJS.enc.Hex.parse(json.iv);
var encrypted = json.ciphertext;// no need to base64 decode.
var iterations = parseInt(json.iterations);
if (iterations <= 0) {
iterations = 999;
}
var encryptMethodLength = (this.encryptMethodLength/4);// example: AES number is 256 / 4 = 64
var hashKey = CryptoJS.PBKDF2(key, salt, {'hasher': CryptoJS.algo.SHA512, 'keySize': (encryptMethodLength/8), 'iterations': iterations});
var decrypted = CryptoJS.AES.decrypt(encrypted, hashKey, {'mode': CryptoJS.mode.CBC, 'iv': iv});
return decrypted.toString(CryptoJS.enc.Utf8);
}// decrypt
/**
* Encrypt string.
*
* @link https://stackoverflow.com/questions/41222162/encrypt-in-php-openssl-and-decrypt-in-javascript-cryptojs Reference.
* @link https://stackoverflow.com/questions/25492179/decode-a-base64-string-using-cryptojs Crypto JS base64 encode/decode reference.
* @param string string The original string to be encrypt.
* @param string key The key.
* @return string Return encrypted string.
*/
encrypt(string, key) {
var iv = CryptoJS.lib.WordArray.random(16);// the reason to be 16, please read on `encryptMethod` property.
var salt = CryptoJS.lib.WordArray.random(256);
var iterations = 999;
var encryptMethodLength = (this.encryptMethodLength/4);// example: AES number is 256 / 4 = 64
var hashKey = CryptoJS.PBKDF2(key, salt, {'hasher': CryptoJS.algo.SHA512, 'keySize': (encryptMethodLength/8), 'iterations': iterations});
var encrypted = CryptoJS.AES.encrypt(string, hashKey, {'mode': CryptoJS.mode.CBC, 'iv': iv});
var encryptedString = CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
var output = {
'ciphertext': encryptedString,
'iv': CryptoJS.enc.Hex.stringify(iv),
'salt': CryptoJS.enc.Hex.stringify(salt),
'iterations': iterations
};
return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(JSON.stringify(output)));
}// encrypt
}
<?php
/**
* Encryption class for encrypt/decrypt that works between programming languages.
*
* @author Vee Winch.
* @link https://stackoverflow.com/questions/41222162/encrypt-in-php-openssl-and-decrypt-in-javascript-cryptojs Reference.
*/
class Encryption
{
/**
* @link http://php.net/manual/en/function.openssl-get-cipher-methods.php Available methods.
* @var string Cipher method. Recommended AES-128-CBC, AES-192-CBC, AES-256-CBC
*/
protected $encryptMethod = 'AES-256-CBC';
/**
* Decrypt string.
*
* @link https://stackoverflow.com/questions/41222162/encrypt-in-php-openssl-and-decrypt-in-javascript-cryptojs Reference.
* @param string $encryptedString The encrypted string that is base64 encode.
* @param string $key The key.
* @return mixed Return original string value. Return null for failure get salt, iv.
*/
public function decrypt(string $encryptedString, string $key)
{
$json = json_decode((string) base64_decode($encryptedString), true);
// check array keys must exists to prevent errors.
if (
!is_array($json) ||
!array_key_exists('salt', $json) ||
!array_key_exists('iv', $json) ||
!array_key_exists('ciphertext', $json) ||
!array_key_exists('iterations', $json)
) {
return null;
}
try {
$salt = hex2bin($json['salt']);
$iv = hex2bin($json['iv']);
} catch (\Exception $e) {
return null;
}
$cipherText = base64_decode($json['ciphertext']);
$iterations = intval(abs((int)$json['iterations']));
if ($iterations <= 0) {
$iterations = 999;
}
$hashKey = hash_pbkdf2('sha512', $key, $salt, $iterations, ($this->encryptMethodLength() / 4));
unset($iterations, $json, $salt);
$decrypted= openssl_decrypt($cipherText , $this->encryptMethod, hex2bin($hashKey), OPENSSL_RAW_DATA, $iv);
if (!is_string($decrypted)) {
$decrypted = null;
}
unset($cipherText, $hashKey, $iv);
return $decrypted;
}// decrypt
/**
* Encrypt string.
*
* @link https://stackoverflow.com/questions/41222162/encrypt-in-php-openssl-and-decrypt-in-javascript-cryptojs Reference.
* @param string $string The original string to be encrypt.
* @param string $key The key.
* @return string Return encrypted string.
*/
public function encrypt(string $string, string $key): string
{
$ivLength = openssl_cipher_iv_length($this->encryptMethod);
$iv = openssl_random_pseudo_bytes($ivLength);
$salt = openssl_random_pseudo_bytes(256);
$iterations = 999;
$hashKey = hash_pbkdf2('sha512', $key, $salt, $iterations, ($this->encryptMethodLength() / 4));
$encryptedString = openssl_encrypt($string, $this->encryptMethod, hex2bin($hashKey), OPENSSL_RAW_DATA, $iv);
$encryptedString = base64_encode($encryptedString);
unset($hashKey);
$output = ['ciphertext' => $encryptedString, 'iv' => bin2hex($iv), 'salt' => bin2hex($salt), 'iterations' => $iterations];
unset($encryptedString, $iterations, $iv, $ivLength, $salt);
return base64_encode(json_encode($output));
}// encrypt
/**
* Get encrypt method length number (128, 192, 256).
*
* @return integer.
*/
protected function encryptMethodLength(): int
{
$number = (int) filter_var($this->encryptMethod, FILTER_SANITIZE_NUMBER_INT);
return intval(abs($number));
}// encryptMethodLength
/**
* Set encryption method.
*
* @link http://php.net/manual/en/function.openssl-get-cipher-methods.php Available methods.
* @param string $cipherMethod
*/
public function setCipherMethod(string $cipherMethod)
{
$this->encryptMethod = $cipherMethod;
}// setCipherMethod
}
<?php
$nonceValue = 'nonce_value';// use nonce that generated while using OAuth.
$readableString = 'asdf-ghjk-qwer-tyui';
$encryptedString = 'eyJjaXBoZXJ0ZXh0IjoiNkRuSzRueVR5aERIQTVCdkF6SU9Mc0E0S1llUW5tZndvS0hIbERRMlE1VT0iLCJpdiI6IjNlNGU0YjFlNTBjNGRmODc2ZWExZTg3NjY3MDc4ZjBkIiwic2FsdCI6IjY0OWUxZDQ0NGNiZDc1YjBhODk2NmY2YTRjZTNjYzUzMmIyYTA4ZDQzZjlmYTQzNDRiOGU2MDFmNWIxODlkNzFjZGE3ZDc1YzU1YTBjMzNhMmM1ZWRlMjc5MTMxZTM5ZjNhYjgzY2JjNGQ5ZjIwYmY5YWE3YjdjN2MwNmVlMTZmNjJmYWEzMWU1MjFiMWZjNWFmZDcxMmRlNDQ3MWEyOTg3MDM0MzliODk0N2E0NGViOTMyMWFlMzI0ZWM2Zjg1ZjkwYmQzYzRmNjk5YzdmN2ViMTVhOGE0ZWExYjU1OGJmNWFiYjg5MzFjMjA5YTkzMWEwY2Q1NWM1NTgxMTRkNTY5NTIzZTk5OWMwZDA4Y2FiYmY4MzAzMTA0MzJkNzE2NmJlMDZlYzk3NjQzNzY1MzQ2NDI4YTM0ODM3MWUyOWRkNDU2ZTVmOGQ0NDgxZGVmZjY4M2FlOGYwOTJjODk3NjdhMzRhN2I0MWNlM2VlMDVlOWQ2ZDg4ZDI5MzVmZGM5MDUxY2VlZDhiYjllZDM5MzNjNjg2ODczZGNiOTJhZWI2MzBkMjNjODNhMjIyNTRjZDkxMDg4OTc4OWQ1MTI1MTc2MjQ2ZGYwOTQyODE5MTZlMmY4Y2RjYTU2MDEwMzEzZTM2NmE2ZDMyOTA4OGM3NzI5MWY3NDE3ODRiNTdmNTc1IiwiaXRlcmF0aW9ucyI6OTk5fQ==';
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="resultPlaceholder"></div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="crypto-js.js"></script><!-- https://github.com/brix/crypto-js/releases crypto-js.js can be download from here -->
<script src="Encryption.js"></script>
<script>
var readableString = '<?php echo $readableString; ?>';
var nonceValue = '<?php echo $nonceValue; ?>';
var encryptedString = '<?php echo $encryptedString; ?>';
// on page loaded.
jQuery(document).ready(function($) {
let encryption = new Encryption();
var encrypted = encryption.encrypt(readableString, nonceValue);
console.log(encrypted);
var decrypted = encryption.decrypt(encrypted, nonceValue);
console.log(decrypted);
var decryptedOldString = encryption.decrypt(encryptedString, nonceValue);
console.log(decryptedOldString);
$('.resultPlaceholder').html('readable string: '+readableString+'<br>');
$('.resultPlaceholder').append('encrypted: '+encrypted+'<br>');
$('.resultPlaceholder').append('decrypted: '+decrypted+'<br>');
$('.resultPlaceholder').append('decrypted from old encrypted string: <strong>'+decryptedOldString+'</strong><br>');
});
</script>
</body>
</html>
<?php
require 'Encryption.php';
$nonceValue = 'nonce_value';// use nonce that generated while using OAuth.
$readableString = 'asdf-ghjk-qwer-tyui';
$encryptedString = 'eyJjaXBoZXJ0ZXh0IjoiNkRuSzRueVR5aERIQTVCdkF6SU9Mc0E0S1llUW5tZndvS0hIbERRMlE1VT0iLCJpdiI6IjNlNGU0YjFlNTBjNGRmODc2ZWExZTg3NjY3MDc4ZjBkIiwic2FsdCI6IjY0OWUxZDQ0NGNiZDc1YjBhODk2NmY2YTRjZTNjYzUzMmIyYTA4ZDQzZjlmYTQzNDRiOGU2MDFmNWIxODlkNzFjZGE3ZDc1YzU1YTBjMzNhMmM1ZWRlMjc5MTMxZTM5ZjNhYjgzY2JjNGQ5ZjIwYmY5YWE3YjdjN2MwNmVlMTZmNjJmYWEzMWU1MjFiMWZjNWFmZDcxMmRlNDQ3MWEyOTg3MDM0MzliODk0N2E0NGViOTMyMWFlMzI0ZWM2Zjg1ZjkwYmQzYzRmNjk5YzdmN2ViMTVhOGE0ZWExYjU1OGJmNWFiYjg5MzFjMjA5YTkzMWEwY2Q1NWM1NTgxMTRkNTY5NTIzZTk5OWMwZDA4Y2FiYmY4MzAzMTA0MzJkNzE2NmJlMDZlYzk3NjQzNzY1MzQ2NDI4YTM0ODM3MWUyOWRkNDU2ZTVmOGQ0NDgxZGVmZjY4M2FlOGYwOTJjODk3NjdhMzRhN2I0MWNlM2VlMDVlOWQ2ZDg4ZDI5MzVmZGM5MDUxY2VlZDhiYjllZDM5MzNjNjg2ODczZGNiOTJhZWI2MzBkMjNjODNhMjIyNTRjZDkxMDg4OTc4OWQ1MTI1MTc2MjQ2ZGYwOTQyODE5MTZlMmY4Y2RjYTU2MDEwMzEzZTM2NmE2ZDMyOTA4OGM3NzI5MWY3NDE3ODRiNTdmNTc1IiwiaXRlcmF0aW9ucyI6OTk5fQ==';
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<?php
echo 'readable string: ' . $readableString . '<br>';
$Encryption = new Encryption();
$encrypted = $Encryption->encrypt($readableString, $nonceValue);
echo 'encrypted: ' . $encrypted . '<br>';
echo "\n\n\n";
echo '<hr>';
echo "\n\n\n";
$decrypted = $Encryption->decrypt($encrypted, $nonceValue);
echo 'decrypted: ' . $decrypted . '<br>';
$decrypted = $Encryption->decrypt($encryptedString, $nonceValue);
echo 'decrypted from old encrypted string: <strong>' . $decrypted . '</strong><br>';
?>
</body>
</html>
@ve3
Copy link
Author

ve3 commented Jan 3, 2020

@maulvi The nonce value must be match or the same because it is the key. If you had run my test files (test-js.php, test-php.php) without any modification and it work properly then it means it is working, the trouble maybe from key changed.

@maulvi
Copy link

maulvi commented Jan 3, 2020

@maulvi The nonce value must be match or the same because it is the key. If you had run my test files (test-js.php, test-php.php) without any modification and it work properly then it means it is working, the trouble maybe from key changed.

yes I tried mine working just fine the decrypted value is appear in console but not in browser page :(, idk if it's because html source.
here's screenshot https://i.imgur.com/yTgmTEb.png
https://gist.github.com/maulvi/f5e6d8005a5fc3d06a81396db966b5c1

@ve3
Copy link
Author

ve3 commented Jan 3, 2020

I confirm it is working. Here is example https://jsfiddle.net/xb5Lteqk/
If something wrong, please carefully check your code.
The encrypted can be decrypt by using the same 'key' as when it was encrypted. The key must be same.

@salazarr-js
Copy link

I made a implementation in angular with typescript and works perfectly, nice work, thanks bro 🍻

@aozfen
Copy link

aozfen commented Apr 6, 2020

Thank you so much. It helped me post a large html code to the server

@Yateenp88
Copy link

not working in ie and edge browser

@ve3
Copy link
Author

ve3 commented Apr 10, 2020

https://jsfiddle.net/xb5Lteqk/

It is working on MS Edge 44.18362.449.0, EdgeHTML 18.18363

@adityarathoreworld
Copy link

Md5 encode your encrypted data

@avenCN
Copy link

avenCN commented Nov 3, 2020

I use it through php encrypt a jpeg file then use js decrypt appear js error.

image

@ve3
Copy link
Author

ve3 commented Nov 3, 2020

I use it through php encrypt a jpeg file then use js decrypt appear js error.

image

I have never encrypt/decrypt jpeg (binary) file. I'm not sure how you do it.
Do you base64 the file content before doing this or not?
If it is string, number, any text please try it with my demo here -> https://jsfiddle.net/xb5Lteqk/
You may change my encrypted string to yours and check if it is working or not.

@avenCN
Copy link

avenCN commented Nov 3, 2020

yes. It's need base64. I'm also try it for other type files like docx, xls etc...

@jesshub
Copy link

jesshub commented Feb 8, 2021

Is it possible this could be updated for PHP 8?
Fatal error: Uncaught TypeError: abs(): Argument #1 ($num) must be of type int|float, string given in Encryption.php

@ve3
Copy link
Author

ve3 commented Feb 8, 2021

Is it possible this could be updated for PHP 8?
Fatal error: Uncaught TypeError: abs(): Argument #1 ($num) must be of type int|float, string given in Encryption.php

Fixed, please update your Encryption.php.

@jafarpagerjaya
Copy link

jafarpagerjaya commented Jan 3, 2022

@ve3 The encrpty value is to long, How to make it a short?

@Mayuri-JoshiA
Copy link

Can you help me to develop Encryption.js like encryption in java android?

@ve3
Copy link
Author

ve3 commented Jan 12, 2022

@Mayuri-JoshiA Sorry, I don't know JAVA.

@Wellusioner
Copy link

@maulvi The nonce value must be match or the same because it is the key. If you had run my test files (test-js.php, test-php.php) without any modification and it work properly then it means it is working, the trouble maybe from key changed.

That helped

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment