I am trying to find a way in PHP that can encrypt a string in hex using DES algorithm. The result I need should be exactly like this page.
All PHP codes that I tried gave different results than what I got in that page.
I tried this code for example:
<?php
function Encrypt($data, $key)
{
$encData = mcrypt_encrypt('tripledes', $key, $data, 'ecb');
return $encData;
}
echo strtoupper(bin2hex(Encrypt("12341234", "1100000120140129")));
?>
The result was: 0D54E1C0B08DCB90. While in this link, the result is: 4DC7D8B78F0F33A3.
Note that 31313030303030313230313430313239 is 1100000120140129 in hex
and 3132333431323334 is 12341234 in hex.
This code did the trick for me:
$keyA = "11000001";
$keyB = "20140129";
$data = "12341234";
$TMP = mcrypt_encrypt('tripledes', $keyA, $data, 'ecb');
$TMP = mcrypt_decrypt('tripledes', $keyB, $TMP, 'ecb');
echo strtoupper(bin2hex(mcrypt_encrypt('tripledes', $keyA, $TMP, 'ecb')));
I used two-key triple DES method to generate the exact result in this DES calculator website.
Encrypt the data using the first half of the key (most left 8 digits)
Decrypt the ciphertext using the second half of the key (most right 8 digits)
Re-encrypt the ciphertext using the first half of the key again
Thanks to #Duncan for the useful help.
This problem seems to be caused by the way PHP reads keys and data when you supply them as strings. Solve this problem by using code such as the following:
$key = pack('H*', "0123456789abcdef"); // this correctly maps hex to bytes
$data = pack('H*', "0123456789abcdef");
echo bin2hex(mcrypt_encrypt(MCRYPT_DES, $key, $data, MCRYPT_MODE_ECB));
This outputs 56cc09e7cfdc4cef which matches the DES calculator (proof).
For those interested, I also used the following Java code to deduce what was going on. This prints the same result as the PHP:
SecretKey key = new SecretKeySpec(new byte[8], "DES");
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
System.out.println(DatatypeConverter.printHexBinary(cipher
.doFinal(new byte[8])));
Related
I have an application running on php which have some values encrypted using openssl encrption by using the code below
<?php
define('OSSLENCKEY','14E2E2D1582A36172AE401CB826003C1');
define('OSSLIVKEY', '747E314D23DBC624E971EE59A0BA6D28');
function encryptString($data) {
$encrypt_method = "AES-256-CBC";
$key = hash('sha256', OSSLENCKEY);
$iv = substr(hash('sha256', OSSLIVKEY), 0, 16);
$output = openssl_encrypt($data, $encrypt_method, $key, 0, $iv);
$output = base64_encode($output);
return $output;
}
function decryptString($data){
$encrypt_method = "AES-256-CBC";
$key = hash('sha256', OSSLENCKEY);
$iv = substr(hash('sha256', OSSLIVKEY), 0, 16);
$output = openssl_decrypt(base64_decode($data), $encrypt_method, $key, 0, $iv);
return $output;
}
echo encryptString("Hello World");
echo "<br>";
echo decryptString("MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09");
?>
I have another endpoint which runs on nodejs where I need to decrypt and encrypt values based on the above php encrypt/decrypt rule.
I have searched but could'nt find a solution for this.
I tried with the library crypto But ends up with errors Reference
My nodejs code which I have tried is given below
message = 'MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09';
const cypher = Buffer.from(message, "base64");
const key = crypto.createHash('sha256').update('14E2E2D1582A36172AE401CB826003C1');//.digest('hex');
// $iv = substr(hash('sha256', '747E314D23DBC624E971EE59A0BA6D28'), 0, 16); from php returns '0ed9c2aa27a31693' need nodejs equivalent
const iv = '0ed9c2aa27a31693';
const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
console.log( decipher.update(contents) + decipher.final());
Someone please help me to find a nodejs code for openssl encryption and decyption
Thanks in advance
There are the following problems in the code:
The key is returned hex encoded in the PHP code, so in the NodeJS code for AES-256 only the first 32 bytes must be considered for the key (PHP does this automatically).
The PHP code Base64 encodes the ciphertext implicitly, so because of the explicit Base64 encoding the ciphertext is Base64 encoded twice (which is unnecessary). Therefore, a double Base64 encoding is necessary in the NodeJS code as well.
Also, note that using a static IV is insecure (but you are probably only doing this for testing purposes).
The following NodeJS code produces the same ciphertext as the PHP code:
const crypto = require('crypto');
const plain = 'Hello World';
const hashKey = crypto.createHash('sha256');
hashKey.update('14E2E2D1582A36172AE401CB826003C1');
const key = hashKey.digest('hex').substring(0, 32);
const hashIv = crypto.createHash('sha256');
hashIv.update('747E314D23DBC624E971EE59A0BA6D28');
const iv = hashIv.digest('hex').substring(0, 16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
var encrypted = cipher.update(plain, 'utf-8', 'base64');
encrypted += cipher.final('base64');
encrypted = Buffer.from(encrypted, 'utf-8').toString('base64');
console.log(encrypted); // MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09
encrypted = Buffer.from(encrypted, 'base64').toString('utf-8');
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
var decrypted = decipher.update(encrypted, 'base64', 'utf-8');
decrypted += decipher.final('utf-8');
console.log(decrypted); // Hello World
EDIT:
As mentioned in the comments, PHP's hash() method returns the hash as a hexadecimal string by default (unless the third parameter is explicitly set to true, which is not the case in the reference code). This doubles the length, because in this encoding each byte of the hash is represented by two hex digits (hexits), i.e. 2 bytes.
Therefore it is necessary to shorten the key in the NodeJS code (see the first point of my original answer). This shortening is not necessary in the PHP code, since PHP does this implicitly (which is actually a design flaw, since this way the user does not notice a possible issue with the key).
The use of the hex string has two disadvantages:
With a hex encoded string, each byte consists of 16 possible values (0-15), as opposed to 256 possible values of a byte (0-255). This reduces the security from 256 bit to 128 bit (which is arithmetically equivalent to AES-128), see here.
Depending on the platform, the hexits a-f can be represented as lowercase or uppercase letters, which can result in different keys and IVs (without explicit agreement on one of the two cases).
For these reasons it is more secure and robust to use the raw binary data of the hash instead of the hex encoded strings. If you want to do this, then the following changes are necessary.
In the PHP code:
$key = hash('sha256', OSSLENCKEY, true);
$iv = substr(hash('sha256', OSSLIVKEY, true), 0, 16);
in the NodeJS code:
const key = hashKey.digest();
const iv = hashIv.digest().slice(0, 16)
Note, however, that this version is not compatible with the old one, i.e. encryptions before this change cannot be decrypted after the change. So the old data would have to be migrated.
I need to decrypt a string encrypted with des algorithm. How can I do in PHP? I have real test cases as follows:
key ='0123456789abcdef'
encryptedValue = '88C10F0B8C084E5F'; //hex value
decodedValue = '2020202039353538'; // this is hex
I've tried
$keyValue ='0123456789abcdef';
$encryptedValue = '88C10F0B8C084E5F'; //hex value
$decodedValue = '2020202039353538'; // this is hex
$decryptedData = mcrypt_decrypt( MCRYPT_DES, $keyValue, $encryptedValue , 'ecb');
var_dump($decryptedData);
var_dump($decodedValue);
Output of decryptedData is null. I checked this solution. Please suggest me a solution.
Update:2017 Jan 18:
Many people are suggeting me not use des or mcrypt. I need to decrypt this because my API provider reponds me with this algorithm. And about mcrypt_decrypt function, I did not find an alternative. Now please suggest me more.
I tried according to #duskwuff, I made modifications as.
$decryptedData = mcrypt_decrypt( MCRYPT_DES, $keyValue, hex2bin($encryptedValue) 'ecb');
var_dump(bin2hex($decryptedData));
Output is empty string which is obviously binary representation of bool false
For you convenience I want to share the result of crypto calculator.
I'm getting this warning as well:Warning: mcrypt_decrypt(): Key of size 16 not supported by this algorithm. Only keys of size 8 supported in /var/www/html/encdec/enc.phtml on line 13
The values you're passing into mcrypt_decrypt() look like they're intended to be a representation of hexadecimal data, not passed in directly. Use hex2bin() on the inputs to convert them to binary data, and bin2hex() to convert the output back to the expected representation.
Also, stop using mcrypt. It's old and broken, and has been removed from PHP 7.2.
I solved my issue by using following code:
$keyValue ='0123456789abcdef'; //hex value
$encryptedOrderId = '88C10F0B8C084E5F'; //hex value
$decodeValueByOnlineTool = '2020202039353538'; // this is hex
$opensslDecrypt = openssl_decrypt( hex2bin($encryptedOrderId) , 'des-ecb' , hex2bin($keyValue) , OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING , '' );
var_dump($opensslDecrypt);
I get the necrypted string to pycrypto ,but it returned a incorrect result.
<?php
define('MCRYPT_SECRET_KEY', '1d46a31baeab9cf69184d1f92ba5b9f8');
function decode($encode_str) {
$key = pack('H*',MCRYPT_SECRET_KEY);
//var_dump($key);echo "\n";
$iv_size = mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ECB);
//var_dump($iv_size);echo "\n";
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
//var_dump($iv);echo "\n";
$encode_str = str_replace(['-', '_'], ['+', '/'], $encode_str);
$mod4 = strlen($encode_str) % 4 and $encode_str .= substr('====', $mod4);
//var_dump($encode_str);echo "\n";
$decrypt = base64_decode($encode_str);
//var_dump($decrypt);echo "\n";
$decrypt = mcrypt_decrypt(MCRYPT_3DES, $key, $decrypt, MCRYPT_MODE_ECB);
return $decrypt;
}
echo "aFOYNZB4Ye4 : ".decode("aFOYNZB4Ye4")."\n";
result:
aFOYNZB4Ye4 : 13455
but when I use python,I could not get the correct result.
# coding: utf-8
import sys,os,base64
from Crypto.Cipher import DES3
key = "1d46a31baeab9cf69184d1f92ba5b9f8".decode("hex")
def urlsafe_mcryptdecode(idstr):
try:
print len(key),key,'\n'
idstr = idstr.replace('-','+').replace('_','/')
mod4 = len(idstr) % 4
data=idstr+"===="[mod4:]
#print len(data),data,'\n'
base64_str = base64.b64decode(data)
#print len(base64_str),base64_str,'\n'
cipher = DES3.new(key, DES3.MODE_ECB)
id_ = cipher.decrypt(base64_str)
#print len(id_),id_,'\n'
return id_
except Exception,e:
print "ERROR",e
return idstr+"#error"
print urlsafe_mcryptdecode("aFOYNZB4Ye4")
result is not 13455.
Before the decrypt,every result of all output is same. What's wrong with my code? Thanks.
The problem is that prior to PHP 5.6.0 the function mcrypt_decrypt() silently pads the key. Your key is 16 bytes, however, the key needs to be 24 bytes, and so it is padded internally to 24 bytes with trailing NUL bytes. This is mentioned in the newer PHP documentation for mcrypt_decrypt() - see the Changelog section.
You need to take this into account when decrypting in Python. You can do that by appending NUL bytes to the end of the decoded key using ljust():
key = "1d46a31baeab9cf69184d1f92ba5b9f8".decode("hex").ljust(24, '\0')
or
key = "1d46a31baeab9cf69184d1f92ba5b9f8".decode("hex") + ('\0' * 8)
I noticed that your PHP code generates an IV which it does not use. That's fine, it's not causing a problem, just thought I'd point that out in case you think that it's being used.
Incidentally, if you are using PHP >= 5.6.0, you need to explicitly pad the key, or use a 24 byte key in the first place. In your code you can pad like this:
$key = pack('H*x8', MCRYPT_SECRET_KEY);
which will append an additional 8 NUL bytes to the end of the key.
I have some encryption code that works fine. In order to make it a bit sneakier, I wanted to tweak the byte array after its encrypted and un-tweak it on the other side before decryption. This way if somebody gets my encryption key, just maybe they won't figure out why its not working.
However whenever I manipulate the bytes it breaks things, which to me means I am not correctly modifying the string byte array. Here is my implementation as suggested below. Its doing the encrypt and decrypt directly after each other for testing purposes.
$string = "My Test String";
$size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($size, MCRYPT_RAND);
$key = pack('H*', encryptKey());
$result = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, addpadding($string), MCRYPT_MODE_CBC, $iv);
$ordVal = ord($result[5]);
if($ordVal == 0)
{
$ordVal = 255;
}
else
{
$ordVal--;
}
//$result[5] = $ordVal;
$data = base64_encode($iv . $result);
$str = base64_decode($data);
if(!str)
{
dieEncrypted("Unable to base64 decode string");
}
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = substr($str,0,$ivSize);
$str = substr($str,$ivSize);
$ordVal = ord($str[5]);
if($ordVal == 255)
{
$ordVal = 0;
}
else
{
$ordVal++;
}
//$str[5] = $ordVal;
$key = pack('H*', encryptKey());
$result = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $str, MCRYPT_MODE_CBC, $iv);
if(!$result)
{
dieEncrypted("Unable to unencrypt string");
}
$result = strippadding($result);
echo "The result is: $result|";
If $iv[5] is really a numeric character(0-9), it should work. But otherwise, it wont because php will cast the letter / character to a numeric so that adding 1 to it makes sense. Letters numerically cast to 0, so the result will always be 0 + 1 = 1, which isn't what you want.
If you want to increment the ascii code by one, try this.
$iv[5] = ord($iv[5]) + 1;
// undo it
$iv[5] = chr($iv[5]) - 1;
Let's disregard the fact that security by obscurity doesn't work and answer your question.
Guess:
255 + 1 = 256 (or 0 for single-byte-characters). That would change zero-terminated string length.
Try base64 encoding actual byte array, and then decode it, so you don't loose anything.
Ok I figured this out. It looks like the string format of this data is such that you can't manipulate a single character. Perhaps its multi byte characters or something. Anyhow the solution was to encode to base64 as suggested above, then perform the byte manipulation using a rollover logic since base64 is not linear. This combines the ord\chr solution mentioned in the 2nd answer. So both answers put together in this manner seemed to do the trick. Thanks all!
I have one issue, that I'm not able to solve...
Instructions:
Create hash with SHA1 from string
Take first 16 bytes from string and encode them with AES256 and KEY
You get 16 bytes signature. You have to convert this signature to 32-bytes string, which represent signature in hexadecimal
My function:
public function GetSign($str) {
$strSIGN = sha1($str, true);
$strSIGN = substr($strSIGN, 0, 16);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, self::KEY, $iv);
$strSIGN = mcrypt_generic($td, substr($strSIGN, 0, 16));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$strSIGNhex = '';
for ($i = 0; $i < strlen($strSIGN); $i++)
{
$ord = ord($strSIGN[$i]);
$hexCode = dechex($ord);
$strSIGNhex .= ((strlen($hexCode) == 1) ? '0' : '') . $hexCode;
}
return $strSIGNhex;
}
But the result is incorrect...
Any suggestions?
Are you sure, the result is incorrect? AES256 returns different values based on the iv, which is in your case random.
Its completely acceptable to have different signatures after different executions - the only requirement is, you can verify, that the output is correct.
I'm not sure what problem this instructions should solve. As far as i understand, you want to generate a signature, using a hash and a key. This problem is normally solved with a HMAC.
With your code you won't be able to recreate the signature to do a verification, because you used the CBC mode with an IV (initialisation vector). This is actually a good thing, but you would have to store the IV too, so you could use the same IV to encrypt another string and do the verification. Of course storing the IV would result in a much longer string than 16 bytes.
Either you use the ECB mode, which doesn't need an IV, or you use the HMAC which is made for such situations.