Hiding my hash number in javascript - javascript

I am trying to use finger printing on my client side and got hold of this code as part of a bigger code.
function checksum(str) {
var hash = 5382,
i = str.length;
while (i--) hash = (hash * 33) ^ str.charCodeAt(i);
return hash >>> 0;
}
As you can see the hash is in plain sight. Can you please show me how or what implementation to use so I can hide or anything that can mask the hash = 5382. Thank you.

If you encode it with base64, but anyone can decode it easily. How sensitive is your hash?
str = "The quick brown fox jumps over the lazy dog";
b64 = btoa(unescape(encodeURIComponent(str)));
str = decodeURIComponent(escape(window.atob(b64)));
The output will be VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==
If you are using PHP you would just base64_encode() and base64_decode() to handle. You can make for example a input hidden with encoded value and then just get it's val and use the last line i gave you.
Base64 PHP http://php.net/manual/en/function.base64-encode.php and base64 JAVASCRIPT https://developer.mozilla.org/pt-BR/docs/Web/API/WindowBase64/atob . Or you could encrypt it's contents then uncrypt it server side. Heres a little class to encrypt/decrypt data (PHP):
<?php
namespace Company\Security;
/*
* #description: Simple class to wrap crypt function calls
* #author: Marco A. Simao
*/
class Crypto {
/*
* returns encrypted data with iv appended at the begining of the string
*/
public static function encrypt($data, $key)
{
$iv = openssl_random_pseudo_bytes(16);
$c = openssl_encrypt($data, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
return $iv . $c;
}
/*
* returns decrypted data. Expects 16 first bytes of data to be iv table.
*/
public static function decrypt($data, $key)
{
return openssl_decrypt(substr($data, 16), 'AES-128-CBC', $key, OPENSSL_RAW_DATA, substr($data, 0, 16));
}
}
And you would need a decrypt in Javascript like: How to use the Web Crypto API to decrypt a file created with OpenSSL?

Related

How can I securely generate the same private key in JS and PHP?

I need to create an internet-less password reset feature. My hardware uses a web (JS/HTML) interface and a PHP back-end. I've seen this in the wild before: the user gets a short key which they read to the IT person, and the IT person generates a short private response key. The user then uses this key and is able to get to a password change screen.
Yes, I have a "security questions" feature already. This is going to be for last-ditch recovery, like if your last Admin user dies or something. Also, it cannot be time-sensitive like standard keyfob RSA. Some of these units are literally on top of mountains, so a person might have a considerable lag time between generating the public key, receiving the private one, and setting it on their device.
I've considered just taking something like the unit's serial and the date and running them through some sort of encryption, but I wonder if you guys have some simpler way? Hell, I could encrypt it the same way I do the passwords, but the strings that generates can be... unwieldy. Ideally this would be maybe a 10-12 character string.
I found a pretty slick way to do it. I took a set of data ($serial, hard-coded $seed, $date, and $type for account type) md5'd it, and encrypted that with openssl using an encrypt function I found on another answer, then did sort of the first half of a checksum to condense the long string to something 1/3 the length. You just run genkey($serial,$seed,$date,$type) to get a nice short string, such as "URMYVAFYNE". The person on the other end does the same thing, then compares the strings to see if they match. Then they generate a second string using the first one as the $seed, and the client enters that to have it verified... you get the idea.
function genkey($serial,$seed,$date,$type) {
$fullstring = $serial.$seed.$date.$type;
$md5 = md5($fullstring);
$encrypted = encrypt_decrypt('encrypt',$md5);
return condense(condense($encrypted));
}
function encrypt_decrypt($action, $string)
{
$output = false;
$encrypt_method = "AES-256-CBC";
$secret_key = '[REDACTED]'; //32 byte
$secret_iv = '[REDACTED]'; //16 byte
// hash
$key = hash('sha256', $secret_key);
// iv - encrypt method AES-256-CBC expects 16 bytes
$iv = substr(hash('sha256', $secret_iv), 0, 16);
if ( $action == 'encrypt' ) {
$output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
$output = base64_encode($output);
} else if( $action == 'decrypt' ) {
$output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
}
return $output;
}
function condense($str) {
$arr = str_split($str, 3);
$retstr = '';
for($i=0;$i<count($arr);$i++) {
$sum=WordSum($arr[$i]);
$letternum = $sum%26 + 65; //ascii code for capital letter
$letter = chr($letternum);
$retstr .= $letter;
}
return $retstr;
}

How to decrypt AES256 data which was encrypted on PHP and get value in Javascript?

I have encrypted some value using aes-256-cbc mode on PHP like this:
public function encrypt(string $data): string
{
$iv = $this->getIv();
$encryptedRaw = openssl_encrypt(
$data,
$this->cryptMethod, //aes-256-cbc
$this->key,
OPENSSL_RAW_DATA,
$iv
);
$hash = hash_hmac('sha256', $encryptedRaw, $this->key, true);
return base64_encode( $iv . $hash . $encryptedRaw );
}
Then I tried to decrypt it on PHP and it works fine:
public function decrypt(string $data): string
{
$decoded = base64_decode($data);
$ivLength = openssl_cipher_iv_length($this->cryptMethod);
$iv = substr($decoded, 0, $ivLength);
$hmac = substr($decoded, $ivLength, $shaLength = 32);
$decryptedRaw = substr($decoded, $ivLength + $shaLength);
$originalData = openssl_decrypt(
$decryptedRaw,
$this->cryptMethod,
$this->key,
OPENSSL_RAW_DATA,
$iv
);
So I'm new to JavaScript and I don't know how to realize the same decrypt method as on php.
Example of encrypted string and it's key:
encrypted string lUIMFpajICh/e44Mwkr0q9xdyJh5Q8zEJHi8etax5BRl78Vsyh+wDknmBga1L8p8SDZA6WKz1CvAAREFGreRAQ== secret key - 9SJ6O6IwmItSRICbXgdJ
Example what I found returns empty string:
const decodedString = base64.decode(
`lUIMFpajICh/e44Mwkr0q9xdyJh5Q8zEJHi8etax5BRl78Vsyh+wDknmBga1L8p8SDZA6WKz1CvAAREFGreRAQ==`
);
const CryptoJS = require("crypto-js");
var key = CryptoJS.enc.Latin1.parse("9SJ6O6IwmItSRICbXgdJ");
var iv = CryptoJS.enc.Latin1.parse(decodedString.slice(0, 16));
var ctx = CryptoJS.enc.Base64.parse(
"lUIMFpajICh/e44Mwkr0q9xdyJh5Q8zEJHi8etax5BRl78Vsyh+wDknmBga1L8p8SDZA6WKz1CvAAREFGreRAQ=="
);
var enc = CryptoJS.lib.CipherParams.create({ ciphertext: ctx });
console.log(
CryptoJS.AES.decrypt(enc, key, { iv: iv }).toString(CryptoJS.enc.Utf8)
);
}
What I did wrong?
The key used in the PHP code is only 20 bytes in size and thus too small for AES-256 (AES-256 requires a 32 bytes key). PHP/OpenSSL implicitly pads the key with 0x00 values to the required key length. In the CryptoJS code, this must be done explicitly.
Furthermore, in the CryptoJS code, IV (the first 16 bytes), HMAC (the following 32 bytes) and ciphertext (the rest) are not separated correctly.
Also, the authentication is missing. To do this, the HMAC for the ciphertext must be determined using the key and compared with the HMAC sent. Decryption only takes place if authentication is successful.
If all of this is taken into account, the posted code can be fixed e.g. as follows:
var key = CryptoJS.enc.Utf8.parse("9SJ6O6IwmItSRICbXgdJ".padEnd(32, "\0")); // pad key
var ivMacCiphertext = CryptoJS.enc.Base64.parse("lUIMFpajICh/e44Mwkr0q9xdyJh5Q8zEJHi8etax5BRl78Vsyh+wDknmBga1L8p8SDZA6WKz1CvAAREFGreRAQ==")
var iv = CryptoJS.lib.WordArray.create(ivMacCiphertext.words.slice(0, 4)); // get IV
var hmac = CryptoJS.lib.WordArray.create(ivMacCiphertext.words.slice(4, 4 + 8)); // get HMAC
var ct = CryptoJS.lib.WordArray.create(ivMacCiphertext.words.slice(4 + 8)); // get Ciphertext
var hmacCalc = CryptoJS.HmacSHA256(ct, key);
if (hmac.toString() === hmacCalc.toString()) { // authenticate
var dt = CryptoJS.AES.decrypt({ciphertext: ct}, key, { iv: iv }).toString(CryptoJS.enc.Utf8); // decrypt
console.log(dt);
} else {
console.log("Decryption failed");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
A few thoughts for you:
Check that your encoding/decoding is working properly. For each stage
of the process, endode/decode, then console log the output and
compare input to output, and also between PHP and javascript.
CBC mode uses padding to fill out the blocks. Check that both stacks
are using the same padding type.
Rather than using CBC and a separate HMAC, how about jumping to AEAD (like AES
GCM) which avoids the padding issue, and also incorporates the MAC
into the encryption, so is a more simple interface?

I tried crypto-js but the output is not correct, please see my code and correct me where I am wrong

I have a simple_crypt function in my backend which is working properly, now what I want is to make a similar function for javascript which for exactly the same as the php one.
So I have researched and got the CryptoJS library, my 'Key' and 'iv' values are correct as compared to the PHP one but when I encrypt my string the output is totally different.
This is my working PHP code and I want to convert this into javascript.
<?php
function simple_crypt( $string ) {
$secret_key = '1234567890';
$secret_iv = '0987654321';
$output = false;
$encrypt_method = "AES-256-CBC";
$key = hash( 'sha256', $secret_key );
$iv = substr( hash( 'sha256', $secret_iv ), 0, 16 );
echo "Key : ".$key."<br>";
echo "iv : ".$iv."<br>";
$output = openssl_encrypt( $string, $encrypt_method, $key, 0, $iv );
return $output;
}
$e = simple_crypt("text");
echo $e;
echo "<br>";
?>
This is my JS code in which I am getting the issue, please have a look and tell me where I am wrong in this js code.
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js" integrity="sha512-nOQuvD9nKirvxDdvQ9OMqe2dgapbPB7vYAMrzJihw5m+aNcf0dX53m6YxM4LgA9u8e9eg9QX+/+mPu8kCNpV2A==" crossorigin="anonymous"></script>
<script type="text/javascript">
function simple_crypt(string) {
var secret_key, secret_iv, output, key, iv;
secret_key = '1234567890';
secret_iv = '0987654321';
output = false;
key = CryptoJS.SHA256(secret_key).toString();
iv = CryptoJS.SHA256(secret_iv).toString().substr(0, 16);
console.log("key",key);
console.log("iv",iv);
var encrypted = CryptoJS.AES.encrypt(string, key, {iv: iv});
return (encrypted.toString());
}
console.log(simple_crypt("text"));
</script>
Here is the output:
PHP: T4F65n4AVlmkkb5LLFhRIQ==
JS: U2FsdGVkX18HJGpPYZPm6crBcxA7TfbZZ9Sc/4qHGBk=
So that both codes produces the same result, the key and IV in the NodeJS Code must be the same as in the PHP code and passed as WordArrays. For this, the key and IV you have generated must be further processed as follows:
key = CryptoJS.enc.Utf8.parse(key.substr(0, 32));
iv = CryptoJS.enc.Utf8.parse(iv);
In the PHP code, the SHA256 hash is returned as hex string. With hex encoding the number of bytes doubles, i.e. a SHA256 hash is hex encoded 64 bytes. PHP implicitly considers only the first 32 bytes regarding the key for AES-256, i.e. ignores the last 32 bytes. In the CryptoJS code this must happen explicitly (for the IV this happens, but for the key this is missing).
By parsing with the UTF8 encoder, key and IV are converted into WordArrays. If the key is passed as a string (as in the code posted in the question), then CryptoJS interprets the value as a password and uses a key derivation function to derive key and IV (which is incompatible with the logic in the PHP code).
With the above changes, the CryptoJS code gives the same result as the PHP code:
function simple_crypt(string) {
var secret_key, secret_iv, output, key, iv;
secret_key = '1234567890';
secret_iv = '0987654321';
output = false;
key = CryptoJS.SHA256(secret_key).toString();
iv = CryptoJS.SHA256(secret_iv).toString().substr(0, 16);
key = CryptoJS.enc.Utf8.parse(key.substr(0, 32));
iv = CryptoJS.enc.Utf8.parse(iv);
console.log("key",key.toString());
console.log("iv",iv.toString());
var encrypted = CryptoJS.AES.encrypt(string, key, {iv: iv});
return (encrypted.toString());
}
console.log(simple_crypt("text")); // T4F65n4AVlmkkb5LLFhRIQ==
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
Please note the following:
Using SHA256 to derive the key from a password is insecure. For this purpose, a reliable key derivation function such as PBKDF2 should be used.
For security reasons, a key/IV pair may only be applied once. Therefore, the IV is usually randomly generated for each encryption. The IV is not a secret and is commonly sent to the recipient along with the ciphertext (usually prepended). Alternatively, the IV can be derived together with the key using a KDF (in combination with a randomly generated salt).

CryptoJs - Encrypt/Decrypt by PHP and Javascript - Simple Output Encrypted String

I want to encrypt and decrypt some string in Php and in Javascript and looking on the web, the best and safest way seems to be CryptoJs.
This post is not a duplicate of Encrypt with PHP, Decrypt with Javascript (cryptojs) because the output string it's not simple.
This is my code but the Js decrypting code doesn't work.
What is it wrong?
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
</head>
<body>
<p>--- PHP ------------------</p>
<?php
function myCrypt($value, $passphrase, $iv){
$encrypted_data = openssl_encrypt($value, 'aes-256-cbc', $passphrase, true, $iv);
return base64_encode($encrypted_data);
}
function myDecrypt($value, $passphrase, $iv){
$value = base64_decode($value);
$data = openssl_decrypt($value, 'aes-256-cbc', $passphrase, true, $iv);
return $data;
}
$valTxt="MyText";
$pswd="MyPassword";
$vector="1234567890123412";
$encrypted = myCrypt($valTxt, $pswd, $vector);
$decrypted = myDecrypt($encrypted, $pswd, $vector);
echo "<p>Text to crypt --------> ".$valTxt." </p>";
echo "<p>Password: ".$pswd." </p>";
echo "<p>Vector: ".$vector." </p>";
echo "<p>TextEncrypt: ".$encrypted." </p>";
echo "<p>TextDecrypt: ".$decrypted." </p>";
?>
<br><br><br>
<p>--- Javascript ------------------</p>
<p>JS-DataEncrypt: --------- <span id="DataEncrypt"></span></p>
<p>JS-DataPassword: -------- <span id="DataPassword"></span></p>
<p>JS-DataVector: ---------- <span id="DataVector"></span></p>
<p>JS-TextDecrypted: ------- <span id="result"></span></p>
<script>
var DataEncrypt='<?php echo $encrypted;?>';
var DataPassword='<?php echo $pswd;?>';
var DataVector='<?php echo $vector;?>';
//var key = CryptoJS.enc.Hex.parse(DataPassword);
//var iv = CryptoJS.enc.Hex.parse(DataVector);
//var decrypted = CryptoJS.AES.decrypt(DataEncrypt, key, { iv: iv });
var decrypted = CryptoJS.AES.decrypt(DataEncrypt, DataPassword, { iv: DataVector });
decrypted= CryptoJS.enc.Utf8.stringify(decrypted)
document.getElementById("DataEncrypt").innerHTML = DataEncrypt;
document.getElementById("DataPassword").innerHTML = DataPassword;
document.getElementById("DataVector").innerHTML = DataVector;
document.getElementById("result").innerHTML = decrypted;
</script>
</body>
</html>
PS. Better if the output string ($encrypted) will be 16 digits A-Za-z0-9... is it possible changing 'aes-256-cbc'?
In the PHP code the following should be considered:
$passphrase does not denote a passphrase, but the key. This key must be 32 bytes in size for the choice aes-256-cbc. If it is too short, it is filled with 0 values, if it is too long, it is truncated. This is a common source of error, so a key of exactly 32 bytes should be used. If you want to work with a passphrase, you have to use a KDF (like PBKDF2).
In the fourth parameter flags are set, and no boolean expression (like true). If the data should be returned in binary form, the OPENSSL_RAW_DATA flag must be set.
Static IVs are insecure, usually a new IV is generated for each encryption, which is sent to the recipient together with the ciphertext. Since the IV is not secret, it is usually placed in front of the ciphertext on byte level without encryption.
The following sample PHP code (based on the posted code):
function myCrypt($value, $key, $iv){
$encrypted_data = openssl_encrypt($value, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
return base64_encode($encrypted_data);
}
function myDecrypt($value, $key, $iv){
$value = base64_decode($value);
$data = openssl_decrypt($value, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
return $data;
}
$valTxt="MyText";
$key="01234567890123456789012345678901"; // 32 bytes
$vector="1234567890123412"; // 16 bytes
$encrypted = myCrypt($valTxt, $key, $vector);
$decrypted = myDecrypt($encrypted, $key, $vector);
print($encrypted . "\n");
print($decrypted . "\n");
returns the following result:
1SF+kez1CE5Rci3H6ff8og==
MyText
The corresponding CryptoJS code for decryption is:
var DataEncrypt = "1SF+kez1CE5Rci3H6ff8og==";
var DataKey = CryptoJS.enc.Utf8.parse("01234567890123456789012345678901");
var DataVector = CryptoJS.enc.Utf8.parse("1234567890123412");
var decrypted = CryptoJS.AES.decrypt(DataEncrypt, DataKey, { iv: DataVector });
var decrypted = CryptoJS.enc.Utf8.stringify(decrypted);
console.log(decrypted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
with the output MyText which corresponds to the original plaintext.
It is important that the key is passed as WordArray, so that it is interpreted as a key and not as a passphrase. For the conversion CryptoJS provides encoders (like CryptoJS.enc.Utf8).
Regarding your question at the end: Ciphertexts are binary arbitrary sequences that can be converted to a string with special binary-to-text encodings (e.g. Base64 as in this case, or hexadecimal), which is generally longer than the raw data (Base64: 75% efficiency, hexadecimal: 50% efficiency, see here).
The representation of a ciphertext block with a number of alphanumeric characters (e.g. 16 chars) equal to the block size (e.g. 16 bytes for AES) is therefore generally not possible.
Note that converting to a string with a character set encoding such as UTF8 is not a solution either, but would corrupt the data.

php mcrypt to javascript aes integration

I am trying to use javascript to encode data with AES-256-CBC and php mcrypt libraries to decode, and vise versa.
I am aware of the problematic nature of javascript and the fact that anyone sees the key, but I am using javascript a scripting tool for non-web environment - so not worried about it.
I found pidder https://sourceforge.net/projects/pidcrypt/
and encrypted some data with the demo page, then tried to decrypt it via php, but something is wrong and I can't seem to find what... I am using the same key with both ends, a 32 byte string
any pointers will be appreciated
~~~
$encrypted = "string after pidder encryption";
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_256,'',MCRYPT_MODE_CBC,'');
$iv_size = mcrypt_enc_get_iv_size($cipher);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
mcrypt_generic_init($cipher, $key, $iv);
$encrypted = base64_decode($encrypted);
echo "after b64decode: " . $encrypted . "\n\n\n";
$encrypted = mdecrypt_generic($cipher, $encrypted);
echo "decrypt:" . $encrypted;
~~~
Try MCRYPT_RIJNDAEL_128 with a 32-byte key for AES-256.
AES is a 128-bit block cipher that supports 128-, 192-, and 256-bit keys. Rijndael-256 is a 256-bit block cipher and AES. AES is a 128-bit block specification for Rijndael.
Pidder uses key derivation function to get the key from password (it should be HMAC-SHA1, i guess), but you seems to use plain password as a key.
Javascript Mcrypt plays well with PHP mcrypt. You could use that instead of pidder.
Your code is sequential, honestly, I dont tried to fix, but I have a function that work well and can help you.
/**
* Encrypt Token
*
* #param unknown $text
*/
private function rijndaelEncrypt($text) {
$iv_size = mcrypt_get_iv_size ( MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB );
$iv = mcrypt_create_iv ( $iv_size, MCRYPT_RAND );
$key = 'your key';
return base64_encode ( mcrypt_encrypt ( MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv ) );
}
/**
* Decrypt
*
* #param unknown $text
*/
private function rijndaelDecrypt($text) {
$iv_size = mcrypt_get_iv_size ( MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB );
$iv = mcrypt_create_iv ( $iv_size, MCRYPT_RAND );
$key = 'your key';
// I used trim to remove trailing spaces
return trim ( mcrypt_decrypt ( MCRYPT_RIJNDAEL_256, $key, base64_decode ( $text ), MCRYPT_MODE_ECB, $iv ) );
}
See http://us3.php.net/manual/en/function.mcrypt-encrypt.php
first of all: MCRYPT_RIJNDAEL_256 is NOT(!) AES-256-CBC, if you want this encryption you have to use MCRYPT_RIJNDAEL_128 with an 265bit aka 32 character key.
This would be the php part:
function decrypt($data, $key) {
if(32 !== strlen($key)) $key= hash('SHA256', $key, true);
$data = base64_decode($data);
$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, str_repeat("\0", 16));
$padding = ord($data[strlen($data) - 1]);
return substr($data, 0, -$padding);
}
This php function includes padding which is an important part, because if the suplied data lenght is not a multiple of the key, you will get something odd.
For decoding we use some of my Node.js scripts with an emulated method of php's str_repeat for the iv:
var crypto = require('crypto');
function encrypt(data, key) {
key = key || new Buffer(Core.config.crypto.cryptokey, 'binary'),
cipher = crypto.createCipheriv('aes-256-cbc', key.toString('binary'), str_repeat('\0', 16));
cipher.update(data.toString(), 'utf8', 'base64');
return cipher.final('base64');
}
function str_repeat(input, multiplier) {
var y = '';
while (true) {
if (multiplier & 1) {
y += input;
}
multiplier >>= 1;
if (multiplier) {
input += input;
} else {
break;
}
}
return y;
}
NOTE: It is not recommend to use a static IV (Initialization vector)!
NOTE: JavaScript part is for Node.js using it's crypto library.
I hope this works for you.

Categories

Resources