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);
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.
How do I encrypt a file contents in PHP using OpenSSL and decrypt it in C++?
Here's my code:
$dll = file('file.dll')[0];
$iv = substr(hash('sha256', 'test'), 0, 16);
$key = substr(hash('sha256', 'test'), 0, 32);
$dll_en = openssl_encrypt($dll, "AES-256-CBC", $key, 0, $iv);
and here's c++
int main() {
/* A 256 bit key */
byte* key = (byte*)"9f86d081884c7d659a2feaa0c55ad015";
/* A 128 bit IV */
byte* iv = (byte*)"9f86d081884c7d65";
std::vector<byte> data = base64_decode("CyeJtJecBChtVSxeTLw9mYKapHwLNJed/5VVuyGOHNSTksBzH1Ym2JwLJv/LvlT9tqMEahwcX7Yj9jYVRCSnTliz/zQYk0pIi8CKTEGkqffqZd8CdA6joLMl9Ym6d+5wERgHEotURq8Kn+H3/GbUuEBUtLL9Cd1+VsKWDyqkE1c=");
byte* ciphertext = new byte[data.size()];
for (size_t i = 0; i < data.size(); i++)
{
ciphertext[i] = data.at(i);
}
byte decryptedtext[8096];
int decryptedtext_len;
decryptedtext_len = decrypt(ciphertext, data.size(), key, iv, decryptedtext);
decryptedtext[decryptedtext_len] = 0;
std::cout << decryptedtext;
return 0;
}
The decrypt function is from here
The first line of the dll is
MZ����#�� �!�L�!This program cannot be run in DOS mode.
but all I get in console is MZÉ.
What am I doing wrong?
Nothing is wrong except your choice of output method!
Since you're passing a byte* to std::cout, the only way it knows when to stop is to treat the input as a C-string, a sequence of 8-bit bytes. When it encounters one with value ZERO, it thinks it's a null terminator and stops. It's working as it should.
But your input is not ASCII! It is arbitrary, "binary" data.
You should instead use something like std::cout.write(decryptedtext, decryptedtext_len), which just chucks all your bytes out to the output stream. It's then up to your console/teletype/printer to render that as it deems fit (which may still not be identical to what you're looking for, depending on settings).
Nothing, you just get things in ASCII instead of UTF-8 while printing a binary file, and characters are skipped until a 00 valued byte is encountered rather than printed out with as a diamond with a question mark. Perform a binary compare instead.
Of course you should note that key and IV calculation of the key and even more the IV is entirely insecure in PHP mode and that CBC mode doesn't provide authentication, so the code is not as secure as it should be.
I have a problem reproducing the same result generated in PHP vs Coldfusion.
In PHP encrypting this way:
<?php
$key = "$224455#";
$Valor = "TESTE";
$base = chop(base64_encode(mcrypt_encrypt(MCRYPT_DES, $key, $Valor, MCRYPT_MODE_ECB)));
?>
I have the result:
TzwRx5Bxoa0=
In Coldfusion did so:
<cfset Valor = "TESTE">
<cfset Key = "$224455#">
<cfset base = Encrypt(Valor,ToBase64(Key),"DES/ECB/PKCS5Padding","BASE64")>
Result:
qOQnhdxiIKs=
What isn't ColdFusion yielding the same value as PHP?
Thank you very much
(Too long for comments)
Artjom B. already provided the answer above. Artjom B. wrote
The problem is the padding. The mcrypt extension of PHP only uses
ZeroPadding [...] you either need to pad the plaintext in php [...] or
use a different cipher in ColdFusion such as "DES/ECB/NoPadding". I
recommend the former, because if you use NoPadding, the plaintext must
already be a multiple of the block size.
Unfortunately, it is difficult to produce a null character in CF. AFAIK, the only technique that works is to use URLDecode("%00"). If you cannot modify the PHP code as #Artjom B. suggested, you could try using the function below to pad the text in CF. Disclaimer: It is only lightly tested (CF10), but seemed to produce the same result as above.
Update:
Since the CF encrypt() function always interprets the plain text input as a UTF-8 string, you can also use charsetEncode(bytes, "utf-8") to create a null character from a single element byte array, ie charsetEncode( javacast("byte[]", [0] ), "utf-8")
Example:
Valor = nullPad("TESTE", 8);
Key = "$224455#";
result = Encrypt(Valor, ToBase64(Key), "DES/ECB/NoPadding", "BASE64");
// Result: TzwRx5Bxoa0=
WriteDump( "Encrypted Text = "& Result );
Function:
/*
Pads a string, with null bytes, to a multiple of the given block size
#param plainText - string to pad
#param blockSize - pad string so it is a multiple of this size
#param encoding - charset encoding of text
*/
string function nullPad( string plainText, numeric blockSize, string encoding="UTF-8")
{
local.newText = arguments.plainText;
local.bytes = charsetDecode(arguments.plainText, arguments.encoding);
local.remain = arrayLen( local.bytes ) % arguments.blockSize;
if (local.remain neq 0)
{
local.padSize = arguments.blockSize - local.remain;
local.newText &= repeatString( urlDecode("%00"), local.padSize );
}
return local.newText;
}
The problem is the padding. The mcrypt extension of PHP only uses ZeroPadding. It means that the plaintext is filled up with 0x00 bytes until the multiple of the block size is reached.
PKCS#5/PKCS#7 padding on the other hand fills it up with bytes that denote the number of bytes missing until the next multiple of the block size. The block size for DES is 8 bytes.
So you either need to pad the plaintext in php (See this drop-in code: A: How to add/remove PKCS7 padding from an AES encrypted string?) or use a different cipher in ColdFusion such as "DES/ECB/NoPadding". I recommend the former, because if you use NoPadding, the plaintext must already be a multiple of the block size.
$key = "$224455#";
$Valor = "TESTE";
function pkcs7pad($plaintext, $blocksize)
{
$padsize = $blocksize - (strlen($plaintext) % $blocksize);
return $plaintext . str_repeat(chr($padsize), $padsize);
}
$base = chop(base64_encode(mcrypt_encrypt(MCRYPT_DES, $key, pkcs7pad($Valor, 8), MCRYPT_MODE_ECB)));
Result:
qOQnhdxiIKs=
Don't forget to unpad the recovered plaintext if you are decrypting in PHP.
I have this algorithm in PHP:
$encoded_key = 'WHllcnRGYTY3eWpUNjQ';
$decoded_key = base64_decode($encoded_key);
// XyertFa67yjT64
$params_string = implode('', $params);
//U215250.00121715620http://partner.domain.ru/order/U215/successhttp://partner.domain.ru/order/U215/fail
$raw_signature = hash_hmac('sha1', $params_string, $decoded_key, true);
// Byte-encoded, hex: c6881d8665afbb46a93a16b34bd152878a19ab3a
$encoded_signature = base64_encode($raw_signature);
// xogdhmWvu0apOhazS9FSh4oZqzo=
I'm trying to port this code to Ruby and get the same result but Base64 and OpenSSL can't help me. Does any one know whats wrong?
One problem is that you are using HMAC.hexdigest instead of HMAC.digest. Your PHP code is generating a raw HMAC and then encoding it in base 64. Therefore, you need to do the same thing in Ruby.
The other problem is the base 64 decoding step of the key. The key you entered is not padded correctly and will therefore be truncated by Ruby's base 64 library. For example:
encoded_key = "WHllcnRGYTY3eWpUNjQ"
Base64.decode64(encoded_key)
#=> "XyertFa67yjT"
# incomplete!
Base64.decode64("#{encoded_key}=\n")
#=> "XyertFa67yjT64"
# this is what you actually want
The padding and the final newline are there to ensure that the base 64 encoded data is complete, since it marks the end. However, it is possible to manually add the padding and just assume that the data is complete:
require 'base64'
require 'openssl'
def base64_pad(unpadded_str)
padding = case unpadded_str.size % 3
when 1 then "=="
when 2 then "="
end
"#{unpadded_str}#{padding}\n"
end
encoded_key = "WHllcnRGYTY3eWpUNjQ"
key = Base64.decode64(base64_pad(encoded_key))
#=> "XyertFa67yjT64"
string = "U215250.00121715620http://partner.domain.ru/order/U215/successhttp://partner.domain.ru/order/U215/fail"
Base64.encode64(OpenSSL::HMAC.digest('SHA1', key, string))
#=> "xogdhmWvu0apOhazS9FSh4oZqzo=\n"
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])));