I have the following code in Javascript to encrypt a string using a key:
des.js is this: http://www.tero.co.uk/des/code.php
<script src="/js/des.js"></script>
<script>
var key = '12345678';
var message = 'hello world';
var ciph = des(key, message, 1, 0);
ciph = stringToHex(ciph);
console.log("Encrypted Result: " + ciph);
</script>
Then I send it server side and attempt to decrypt with this PHP code:
$key = '12345678';
$hexa = '0x28dba02eb5f6dd476042daebfa59687a'; /This is the output from Javascript
$string = '';
for ($i=0; $i < strlen($hexa)-1; $i+=2) {
$string .= chr(hexdec($hexa[$i].$hexa[$i+1])); }
echo mcrypt_decrypt(MCRYPT_DES, $key, $string, MCRYPT_MODE_ECB);
Ive tried converting it to utf8, changing encoding, changing the hex decoding, etc, but it always comes out gibberish, sometimes as nonreadable characters, other times as readable but nonsense.
The way to decrypt the string is not working properly, try this:
$key = '12345678';
$hexa = '0x28dba02eb5f6dd476042daebfa59687a';
function hexToString ($h) {
$r = "";
for ($i= (substr($h, 0, 2)=="0x")?2:0; $i<strlen($h); $i+=2) {$r .= chr (base_convert (substr ($h, $i, 2), 16, 10));}
return $r;
}
echo mcrypt_decrypt(MCRYPT_DES, $key,hexToString('0x28dba02eb5f6dd476042daebfa59687a'), MCRYPT_MODE_ECB);
The output will be: hello world
This way work properly, however, you should search another method to encrypt your data, in your script the key (12345678) and your encrypt method is visible to everyone.
Data to be encrypted with a block cipher such as DES or AES must be an exact multiple of the block size in length. The solution is to add padding to the data to be encrypted, PKCS#5 padding is the usual padding for DES and probably the default for Javascript. Unfortunately mcrypt does not support PKCS#5 padding, only a non-standard zero padding.
Potential solutions:
Use a better encryption function than mcrypt, see the comment to the question.
Specify no padding in Javascript and manually add zero padding.
Specify no padding in mcrypt and remove the padding manually.
It is better tospecify all options and no to rely on defaults.
Related
I have javascript that encrypts and gives the string.
But I have to do this through PHP.
The method I have tried is giving me different result than javascript.
<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>
var txt="This text will be encrypted.";
var key = CryptoJS.enc.Hex.parse('0123456789abcdef0123456789abcdef');
var iv = CryptoJS.enc.Hex.parse('abcdef9876543210abcdef9876543210');
var encrypted = CryptoJS.AES.encrypt((txt), key, { iv: iv });
var encrypted_data = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
alert(encrypted_data);
I get output:
2X/btHgrMBhNlgD8oKNO9rzqCg+RSydprVKmpbYY+j0=
In PHP
<?php
$plaintext="This text will be encrypted.";
$key = pack("H*", "0123456789abcdef0123456789abcdef");
$iv = pack("H*", "abcdef9876543210abcdef9876543210");
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $iv);
$ciphertext_base64 = base64_encode($ciphertext);
echo $ciphertext_base64;
?>
I get output:
2X/btHgrMBhNlgD8oKNO9nJyd4xC4VTLGxnnrzGim+U=
I want the out as same as the output in javascript.
I found that the starting 21 characters match, but not the rest of the string.
Anything I am missing?
This is due to the different padding. CryptoJS uses PKCS#7, mcrypt applies zero padding.
You should replace mcrypt on the PHP side as it is deprecated (s. here). An alternative would be PHP/OpenSSL, which uses PKCS#7 by default.
A solution with PHP/OpenSSL which produces the same ciphertext is:
<?php
$plaintext="This text will be encrypted.";
$key = hex2bin("0123456789abcdef0123456789abcdef");
$iv = hex2bin("abcdef9876543210abcdef9876543210");
$ciphertext_base64 = openssl_encrypt($plaintext, "aes-128-cbc", $key, 0, $iv);
echo $ciphertext_base64; // 2X/btHgrMBhNlgD8oKNO9rzqCg+RSydprVKmpbYY+j0=
?>
As per this question's related answer, I'm attempting to put together a pack/unpack solution resembling this PHP process, however in Nodejs (Javascript) using md5 and bufferpack
Here's the PHP approach (adapted from DaloRADIUS:
$challenge = 'c731395aca5dcf45446c0ae83db5319e';
$uamsecret = 'secret';
$password = 'password';
$hexchal = pack ("H32", $challenge);
$newchal = pack ("H*", md5($hexchal . $uamsecret));
$response = md5("\0" . $password . $newchal);
$newpwd = pack("a32", $password);
$pappassword = implode ("", unpack("H32", ($newpwd ^ $newchal)));
echo "Response: ---> ", $response, "\n";
echo "New Password: ---> ", $newpwd, "\n";
echo "Pap Password: ---> ", $pappassword, "\n";
The above echos these:
Above in plaintext:
Response: ---> 2d4bd27184f5eb032641137f728c6043
New Password: ---> password
Pap Password: ---> 356a1fb08f909fc400dfe448fc483ce3
In Javascript, here's what I'm doing now:
var challenge = 'c731395aca5dcf45446c0ae83db5319e';
var uamsecret = 'secret';
var password = 'password';
var hexchal = pack.pack("H32", challenge);
var newchal = pack.pack("H*", md5(hexchal + uamsecret));
var response = md5("\0" + password + newchal);
var newpwd = pack.pack("a32", password);
var pappassword = pack.unpack("H32", (newpwd ^ newchal)).join("");
console.log("Response: --> ", response);
console.log("New Password: -->", newpwd);
console.log("Pap Password: --->", pappassword);
Which gives the result:
In JSON:
In plaintext:
Response: --> e8a54a55cbcd81dbc2bdfd9b197d62af
New Password: --> <Buffer >
Pap Password: ---> NaN
All the above snippets are available here: RadiusNES
My understanding in this whole process isn't the best, and will appreciate insights and where I'm going wrong.
Why is there a mismatch?
The translation does not work because the PHP Pack function uses different format strings and returns strings, whilst the Javascript bufferpack module returns arrays. Also you cannot xor strings in Javascript.
Whilst there may be modules to do what you want, I have my own functions for parsing hex strings. Also I like modifying prototypes which not everyone agrees with, but these could be converted to regular functions.
String.prototype.pad = function( length ,padding ) {
var padding = typeof padding === 'string' && padding.length > 0 ? padding[0] : '\x00'
,length = isNaN( length ) ? 0 : ~~length;
return this.length < length ? this + Array( length - this.length + 1 ).join( padding ) : this;
}
String.prototype.packHex = function() {
var source = this.length % 2 ? this + '0' : this
,result = '';
for( var i = 0; i < source.length; i = i + 2 ) {
result += String.fromCharCode( parseInt( source.substr( i , 2 ) ,16 ) );
}
return result;
}
var challenge = 'c731395aca5dcf45446c0ae83db5319e'
,uamsecret = 'secret'
,password = 'password';
var hexchal = challenge.packHex();
var newchal = md5( hexchal + uamsecret ).packHex();
var response = md5( '\0' + password + newchal );
var newpwd = password.pad( 32 );
var pappassword = '';
for( var i = 0; i < newchal.length; i++ ) {
pappassword += ( newpwd.charCodeAt( i ) ^ newchal.charCodeAt( i ) ).toString( 16 );
}
console.log("Response: --> ", response);
console.log("New Password: -->", newpwd);
console.log("Pap Password: --->", pappassword);
Two functions are defined in the String prototype to replace the use of the pack function:
.pad( 32, string ) is used to pad out a string with nulls to give the same results as pack( 'a32', string ). Although not needed here it also takes a second parameter if wanting to pad the string ith a character other than nulls.
.packHex is the equivalent of pack( 'H*' ,string ) and translating the code of each pair of hex characters into a character. The function ideally needs more validation to test the string is a valid hex one if is to be used.
After the inputs have been defined, the next four lines instead set variables using these functions rather than pack.
Because Javascript cannot natively xor strings, you then need to use a loop to extract each character, convert it to a numeric, xor those values, then convert the result back into a character to create the pappassword string.
That will return, for me:
Response: --> – "fbfd42ffde05fcf8dbdd02b7e8ae2d90"
New Password: --> – "password������������������������"
Pap Password: ---> – "dcbdacb03f5d38ca33c128b931c272a"
Which is a result, but unfortunately a different on from the PHP code.
This is because my installation of PHP is configured to use ISO-8859-1 encoding internally, whilst Javascript natively uses UTF-16.
This is not a problem in normal use, but it means the respective md5 functions will be seeing different values and therefore return a different hash.
Assuming you are writing an authentication routine using a PHP backend you will obviously need consistent results. There may be modules available to convert the encoding of the Javscript values for compatibility, but it is much easier to make changes to the PHP code.
Because we know the hex strings will be one byte, Javascript is effectively using UTF-8, so PHP can do the same by using the utf8_encode() function to convert the packed hex strings before md5ing them.
Originally I thought that Javascript was internally converting the encoded hex characters into their unicode equivalents because of this, but this was not the case. Instead it was the md5 module being used in Javascript that was performing a UTF-8 conversion on the input.
This leaves two possible options.
1. Use UTF-8 PHP
If possible you can reconfigure your PHP server to use UTF-8 encoding. Or you can change your script to use the utf8_encode() function to mirror the same process as is happening in the Javascript, and convert the hex packed strings to UTF-8 before passing them to md5()
$challenge = 'c731395aca5dcf45446c0ae83db5319e';
$uamsecret = 'secret';
$password = 'password';
$hexchal = pack ("H32", $challenge);
$newchal = pack ("H*", md5(utf8_encode($hexchal) . $uamsecret));
$response = md5("\0" . $password . utf8_encode($newchal));
$newpwd = pack("a32", $password);
$pappassword = implode ("", unpack("H32", ($newpwd ^ $newchal)));
echo "Response: ---> ", $response, "\n";
echo "New Password: ---> ", $newpwd, "\n";
echo "Pap Password: ---> ", $pappassword, "\n";
This then returns the same results as the Javscript:
Response: ---> fbfd42ffde05fcf8dbdd02b7e8ae2d90
New Password: ---> password
Pap Password: ---> dcbdacb03f5d38ca33c128b9310c272a
2. Change the md5 module in Javascript
I am assuming you are using the bluimp JavaScript-MD5 module, as this is what it used by DaloRADIUS routine you linked. You can patch this to bypass the UTF-8 conversion.
There are various ways you can patch this, but on line 259 is the definition of the md5() function itself. This is simply a set of if statements to parse the input options and call the appropriate internal function.
However, the functions called by this block simply provide the UTF-8 input conversion, through a function called str2rstrUTF8() before then call the appropriate functions to provide the actual hashing. You may therefore want to patch the md5() to accept a third parameter to indicate whether the UTF-8 conversion should be applied and then call other functions as appropriate.
However to simply remove the conversion completely the easier way is to change str2rstrUTF8() to return the input unchanged. This function can be found on line 239, changing it to just read as follows will stop the conversion:
function str2rstrUTF8 (input) {
return input
}
Alternatively to remove the redundant function call you can instead just remove the references to it. Change the function starting on line 246 to read as follows:
function rawMD5 (s) {
return rstrMD5(s)
}
The rawHMACMD5() function on line 252 also includes calls to the str2rstrUTF8() function which you may also want to patch for consistency but this is not required for the above routine. That function is called instead when a second parameter is passed to provide a key hash, a feature not available in the native PHP md5() function.
After making either of those changes the Javascript routine now returns the same output as your original (ISO-8859-1 using) PHP code:
Response: --> – "2d4bd27184f5eb032641137f728c6043"
New Password: --> – "password������������������������"
Pap Password: ---> – "356a1fb08f909fc40dfe448fc483ce3"
I am trying to implement a PHP encryption script into JavaScript. My PHP script returns a 128 character string, while my Javascript based one returns 160 characters. The first 128 characters of the JavaScript based version match the PHP based version.
function pkcs5_pad ($text, $blocksize){
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
$skey = "somekey";
$ivKey = "someIVKey";
$input = "empid=xxxxxx;timestamp=Sat, 19 Nov 2016 00:33:03 UTC";
try {
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128,'cbc');
echo strlen($input) . "\n";
$input = pkcs5_pad($input, $size);
echo strlen($input) . "\n";
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$key = pack('H*', $skey);
$iv = pack('H*', $ivKey);
# The key size used is 16, 24 or 32 bytes - for AES-128, 192 and 256 respectively
if (mcrypt_generic_init($cipher, $key, $iv) != -1){
$encrypted = mcrypt_generic($cipher, $input);
mcrypt_generic_deinit($cipher);
$encryptedString = bin2hex($encrypted);
}
echo $encryptedString . "\n";
echo strlen($encryptedString) . "\n";
} catch (Exception $ex) {
echo $ex->getMessage();
}
The length strlen($encryptedString) here gives me 128 characters.
My JavaScript based version uses CryptoJS to create looks like this
var aesKey = "somekey";
var ivKey = "someIVKey";
function pkcs5_pad (text, blocksize){
console.log(text.length);
var pad = blocksize - (text.length % blocksize);
console.log("pad:" + pad);
return text + str_repeat(chr(pad), pad);
}
input = "empid=xxxxxx;timestamp=Sat, 19 Nov 2016 00:33:03 UTC";
var size = 16;
console.log(input.length);
var input = pkcs5_pad(input, size);
console.log('"' + input + '"');
console.log(input.length);
var key = CryptoJS.enc.Hex.parse(aesKey);
var iv = CryptoJS.enc.Hex.parse(aesIV);
var encryptedString = CryptoJS.AES.encrypt(input,key,{iv: iv});
console.log(encryptedString.ciphertext.toString().length);
encryptedString = encryptedString.ciphertext.toString();
Everything matches, including the string lenght before and after pkcs5_pad. I'm using some additional JavaScript code from locutus.io to call str_repeat, and chr. Here encryptedString.ciphertext.toString().length returns 160 characters and the first 128 match that of my PHP script.
My understanding is that version 3 of CryptoJS uses CBC mode, but I've also set the mode explicitly to CBC to no avail. I've also returned the encrypted string as hex
encryptedString = encryptedString.ciphertext.toString(CryptoJS.enc.Hex);
Where am I going wrong?
EDIT
The output of the PHP version is
86b1c9874069129d0852eade01eb753a176a1c6155c4af3ac447ae0a5350b92c3447f95be9c4f8cdbf14503696bcaa16e6307c1605a2cac503239db9d1ac6fb3
The output of the JavaScript version is
86b1c9874069129d0852eade01eb753a176a1c6155c4af3ac447ae0a5350b92c3447f95be9c4f8cdbf14503696bcaa16e6307c1605a2cac503239db9d1ac6fb33051208849788f8a90db1cbe2494cac7
The extra 32 characters are hex encoding of 16 bytes and that is the padding. The Java is adding padding, the PHP is not.
Note that mcrypt does not use standard PKCS#7 (née PKCS#5) padding.
Good encryption libraries will have a padding option and add the padding on encryption and remove it on decryption. You should not have to do your own padding.
i am trying to store some info in my db. One of the fields are Japanese names. I am getting a error:
Warning: #1366 Incorrect string value: '\xE3\x83\xA9\xE3\x83\x87...' for column 'japan-name' at row 1
So i cannot chnage the charset of my db. Can i use PHP or Javascript to convert Japanese/Korean to something else, and them when i go read it, reconvert to Japanese/Korean?
PHP offers the base64_encode() and base64_decode() functions. They are fast, and impose a storage penalty of about 33%. You can use the first to convert your utf-8 east Asian text to what looks like gibberish in ASCII before you store it in your table. The second will convert it back after you retrieve it.
Here's an example:
$jp = " 私はガラスを食べられます。それは私を傷つけません。";
$jpe = base64_encode ($jp);
$jpd = base64_decode ($jpe);
After you run these lines, the $jpe variable has the value
IOengeOBr+OCrOODqeOCueOCkumjn+OBueOCieOCjOOBvuOBmeOAguOBneOCjOOBr+engeOCkuWCt+OBpOOBkeOBvuOBm+OCk+OAgg==
That stores just fine in an ASCII or Latin-1 column.
utf-8 saves the unicode data in table... but other way is to encode and save and then decode and display
update:
searched on web and found answer at How do you Encrypt and Decrypt a PHP String?
define("ENCRYPTION_KEY", "!##$%^&*");
$string = "This is the original data string!";
echo $encrypted = encrypt($string, ENCRYPTION_KEY);
echo "<br />";
echo $decrypted = decrypt($encrypted, ENCRYPTION_KEY);
/**
* Returns an encrypted & utf8-encoded
*/
function encrypt($pure_string, $encryption_key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
return $encrypted_string;
}
/**
* Returns decrypted original string
*/
function decrypt($encrypted_string, $encryption_key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, $encryption_key, $encrypted_string, MCRYPT_MODE_ECB, $iv);
return $decrypted_string;
}
I am using encodeURIComponent() (and encodeURI() for e-mails) to take inputs safely from the user, and am then sending the output to php via ajax. The php processes it and puts this escaped sting into a $_SESSION[] which I then to to echo later. I was wondering if it was possible to print this to html normally, and then have html ignore anything inside it being code (e.g. would be treated as text instead of a tag) or even combine these two steps. I think the format for JavaScript encoding is different than that of php, so this might be an issue, but if it is, what would be the best way to change these stings in php (I'm storing these escaped strings in MySQL)?
Thanks in advance.
Theese are the functions I use when I handle strings from users in php.
save is for save to database,
edit is editing in input/textareas
show is to write it out showing the tags as text in html.
// SAVE DATA
function save($str)
{
return mysql_real_escape_string($str);
}
//############################################################################
// EDIT DATA
function edit($str)
{
$patterns[0] = '/</';
$patterns[1] = '/>/';
$patterns[2] = '/"/';
$patterns[3] = "/'/";
$replacements[0] = '<';
$replacements[1] = '>';
$replacements[2] = '"';
$replacements[3] = ''';
$str = preg_replace($patterns, $replacements, $str);
$str = trim($str);
return stripslashes(stripslashes(str_replace('\r\n', '
', $str)));
}
//############################################################################
// SHOW DATA
function show($str)
{
$patterns[0] = '/</';
$patterns[1] = '/>/';
$patterns[2] = '/"/';
$patterns[3] = "/'/";
$replacements[0] = '<';
$replacements[1] = '>';
$replacements[2] = '"';
$replacements[3] = ''';
$str = preg_replace($patterns, $replacements, $str);
$str = trim($str);
return stripslashes(stripslashes(str_replace('
', '<br />', $str)));
}
play around with it and see if it works for you :)