Decrypt data with python, encrypt in php - php

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.

Related

Warning: mcrypt_decrypt(): Key of size 21 not supported..Only keys of sizes 16, 24 or 32 supported [duplicate]

mcrypt_decrypt(): Key of size 15 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported
How Can I fix this issue? my key is set - can not change it.
It has to be a local change, I think my local PHP version is too advanced for the project I loaded.
How can I fix this?
Did you update to 5.6? It says
Invalid key and iv sizes are no longer accepted. mcrypt_decrypt() will now throw a warning and return FALSE if the inputs are invalid. Previously keys and IVs were padded with '\0' bytes to the next valid size.
Reference
Read the last line of that quote, and there you will find your solution :)
mcrypt_decrypt(): Key of size 15 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported
That means you need to pad your key with \0 (that's what previous versions were doing for you)
$key=$key."\0";
I went ahead and created a function based on Hanky 웃 Panky's answer.
This can be used with any key length to make sure it's the correct size.
function pad_key($key){
// key is too large
if(strlen($key) > 32) return false;
// set sizes
$sizes = array(16,24,32);
// loop through sizes and pad key
foreach($sizes as $s){
while(strlen($key) < $s) $key = $key."\0";
if(strlen($key) == $s) break; // finish if the key matches a size
}
// return
return $key;
}
For Laravel 5
Just run php artisan key:generate:
Application key [EaaJgaD0uFDEg7tpvMOqKfAQ46Bqi8Va] set successfully.
If you don't see your key updated, just paste it in your .env file.
APP_KEY=EaaJgaD0uFDEg7tpvMOqKfAQ46Bqi8Va
Refresh your page
I had this issue with OSTicket 1.6 ST (yes old version I know). Hosting company just went to PHP 5.6 and it broke the Mail Fetch for cron.php. I'm posting this hoping it helps others fix this issue faster.
You have to edit the file "include/class.misc.php".
Add the function "pad_key" provided in the answer authored by #troskater to the "include/class.misc.php" file and then on line 51 in the function "decrypt" change
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt,...
to instead use
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, pad_key($salt),...
You can just use str_pad() for this. In its simplest form, this will suffice.
function padKey($key)
{
// Get the current key size
$keySize = strlen($key);
// Set an array containing the valid sizes
$validSizes = [16,24,32];
// Loop through sizes and return correct padded $key
foreach($validSizes as $validSize) {
if ($keySize <= $validSize) return str_pad($key, $validSize, "\0");
}
// Throw an exception if the key is greater than the max size
throw new Exception("Key size is too large");
}
The other answers will do just fine. I'm just taking advantage of the built in PHP function str_pad here instead of appending "\0" in a loop.
You don't need to pad the key with "\0".
I had the same issue when migrating to a new PHP 7 server and I got the message :
mcrypt_decrypt(): Key of size 19 not supported by this algorithm. Only
keys of sizes 16, 24 or 32 supported.
The key I had in the code was a string of 19 characters, I simply changed it to a string of 32 characters and everything was fine again.
So as the error message suggests, use a valid size key.
I had the same problem, but fixed it with this
public function setKey($key) {
$len = strlen($key);
if($len < 24 && $len != 16){
$key = str_pad($key, 24, "\0", STR_PAD_RIGHT);
} elseif ($len > 24 && $len < 32) {
$key = str_pad($key, 32, "\0", STR_PAD_RIGHT);
}elseif ($len > 32){
$key = substr($key, 0, 32);
}
$this->key = $key;
}
If your encryption code looks like this:
<?php
function encryptCookie($value){
if(!$value){return false;}
$key = 'aNdRgUkXp2s5v8y/B?E(H+MbQeShVmYq';
$text = $value;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv);
return trim(base64_encode($crypttext)); //encode for cookie
}
function decryptCookie($value){
if(!$value){return false;}
$key = 'aNdRgUkXp2s5v8y/B?E(H+MbQeShVmYq';
$crypttext = base64_decode($value); //decode cookie
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $crypttext, MCRYPT_MODE_ECB, $iv);
return trim($decrypttext);
}
?>
You will want to change the $key to a 128 or 256 bit encrypted code. I simply copied a code that I generated from here: Generate Code
I created a 256 bit code for mine which consists of 32 characters and thus fixes the issue of the invalid key size of 15 or whatever number is causing the error. So whatever is set for $key you need to change that to a valid code and then it should work fine.

mcrypt_decrypt() error change key size

mcrypt_decrypt(): Key of size 15 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported
How Can I fix this issue? my key is set - can not change it.
It has to be a local change, I think my local PHP version is too advanced for the project I loaded.
How can I fix this?
Did you update to 5.6? It says
Invalid key and iv sizes are no longer accepted. mcrypt_decrypt() will now throw a warning and return FALSE if the inputs are invalid. Previously keys and IVs were padded with '\0' bytes to the next valid size.
Reference
Read the last line of that quote, and there you will find your solution :)
mcrypt_decrypt(): Key of size 15 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported
That means you need to pad your key with \0 (that's what previous versions were doing for you)
$key=$key."\0";
I went ahead and created a function based on Hanky 웃 Panky's answer.
This can be used with any key length to make sure it's the correct size.
function pad_key($key){
// key is too large
if(strlen($key) > 32) return false;
// set sizes
$sizes = array(16,24,32);
// loop through sizes and pad key
foreach($sizes as $s){
while(strlen($key) < $s) $key = $key."\0";
if(strlen($key) == $s) break; // finish if the key matches a size
}
// return
return $key;
}
For Laravel 5
Just run php artisan key:generate:
Application key [EaaJgaD0uFDEg7tpvMOqKfAQ46Bqi8Va] set successfully.
If you don't see your key updated, just paste it in your .env file.
APP_KEY=EaaJgaD0uFDEg7tpvMOqKfAQ46Bqi8Va
Refresh your page
I had this issue with OSTicket 1.6 ST (yes old version I know). Hosting company just went to PHP 5.6 and it broke the Mail Fetch for cron.php. I'm posting this hoping it helps others fix this issue faster.
You have to edit the file "include/class.misc.php".
Add the function "pad_key" provided in the answer authored by #troskater to the "include/class.misc.php" file and then on line 51 in the function "decrypt" change
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt,...
to instead use
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, pad_key($salt),...
You can just use str_pad() for this. In its simplest form, this will suffice.
function padKey($key)
{
// Get the current key size
$keySize = strlen($key);
// Set an array containing the valid sizes
$validSizes = [16,24,32];
// Loop through sizes and return correct padded $key
foreach($validSizes as $validSize) {
if ($keySize <= $validSize) return str_pad($key, $validSize, "\0");
}
// Throw an exception if the key is greater than the max size
throw new Exception("Key size is too large");
}
The other answers will do just fine. I'm just taking advantage of the built in PHP function str_pad here instead of appending "\0" in a loop.
You don't need to pad the key with "\0".
I had the same issue when migrating to a new PHP 7 server and I got the message :
mcrypt_decrypt(): Key of size 19 not supported by this algorithm. Only
keys of sizes 16, 24 or 32 supported.
The key I had in the code was a string of 19 characters, I simply changed it to a string of 32 characters and everything was fine again.
So as the error message suggests, use a valid size key.
I had the same problem, but fixed it with this
public function setKey($key) {
$len = strlen($key);
if($len < 24 && $len != 16){
$key = str_pad($key, 24, "\0", STR_PAD_RIGHT);
} elseif ($len > 24 && $len < 32) {
$key = str_pad($key, 32, "\0", STR_PAD_RIGHT);
}elseif ($len > 32){
$key = substr($key, 0, 32);
}
$this->key = $key;
}
If your encryption code looks like this:
<?php
function encryptCookie($value){
if(!$value){return false;}
$key = 'aNdRgUkXp2s5v8y/B?E(H+MbQeShVmYq';
$text = $value;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv);
return trim(base64_encode($crypttext)); //encode for cookie
}
function decryptCookie($value){
if(!$value){return false;}
$key = 'aNdRgUkXp2s5v8y/B?E(H+MbQeShVmYq';
$crypttext = base64_decode($value); //decode cookie
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $crypttext, MCRYPT_MODE_ECB, $iv);
return trim($decrypttext);
}
?>
You will want to change the $key to a 128 or 256 bit encrypted code. I simply copied a code that I generated from here: Generate Code
I created a 256 bit code for mine which consists of 32 characters and thus fixes the issue of the invalid key size of 15 or whatever number is causing the error. So whatever is set for $key you need to change that to a valid code and then it should work fine.

manipulating bytes in a binary string

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!

DES-encrypting a string and converting to hex

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])));

Decrypting strings in Python that were encrypted with MCRYPT_RIJNDAEL_256 in PHP

I have a function in PHP that encrypts text as follows:
function encrypt($text)
{
$Key = "MyKey";
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Key, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
}
How do I decrypt these values in Python?
To decrypt this form of encryption, you will need to get a version of Rijndael. One can be found here. Then you will need to simulate the key and text padding used in the PHP Mcrypt module. They add '\0' to pad out the text and key to the correct size. They are using a 256 bit block size and the key size used with the key you give is 128 (it may increase if you give it a bigger key). Unfortunately, the Python implementation I've linked to only encodes a single block at a time. I've created python functions which simulate the encryption (for testing) and decryption in Python
import rijndael
import base64
KEY_SIZE = 16
BLOCK_SIZE = 32
def encrypt(key, plaintext):
padded_key = key.ljust(KEY_SIZE, '\0')
padded_text = plaintext + (BLOCK_SIZE - len(plaintext) % BLOCK_SIZE) * '\0'
# could also be one of
#if len(plaintext) % BLOCK_SIZE != 0:
# padded_text = plaintext.ljust((len(plaintext) / BLOCK_SIZE) + 1 * BLOCKSIZE), '\0')
# -OR-
#padded_text = plaintext.ljust((len(plaintext) + (BLOCK_SIZE - len(plaintext) % BLOCK_SIZE)), '\0')
r = rijndael.rijndael(padded_key, BLOCK_SIZE)
ciphertext = ''
for start in range(0, len(padded_text), BLOCK_SIZE):
ciphertext += r.encrypt(padded_text[start:start+BLOCK_SIZE])
encoded = base64.b64encode(ciphertext)
return encoded
def decrypt(key, encoded):
padded_key = key.ljust(KEY_SIZE, '\0')
ciphertext = base64.b64decode(encoded)
r = rijndael.rijndael(padded_key, BLOCK_SIZE)
padded_text = ''
for start in range(0, len(ciphertext), BLOCK_SIZE):
padded_text += r.decrypt(ciphertext[start:start+BLOCK_SIZE])
plaintext = padded_text.split('\x00', 1)[0]
return plaintext
This can be used as follows:
key = 'MyKey'
text = 'test'
encoded = encrypt(key, text)
print repr(encoded)
# prints 'I+KlvwIK2e690lPLDQMMUf5kfZmdZRIexYJp1SLWRJY='
decoded = decrypt(key, encoded)
print repr(decoded)
# prints 'test'
For comparison, here is the output from PHP with the same text:
$ php -a
Interactive shell
php > $key = 'MyKey';
php > $text = 'test';
php > $output = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB);
php > $encoded = base64_encode($output);
php > echo $encoded;
I+KlvwIK2e690lPLDQMMUf5kfZmdZRIexYJp1SLWRJY=
If you're willing to use MCRYPT_RIJNDAEL_128 rather than 256 on the PHP side, this is as simple as:
from Crypto.Cipher import AES
import base64
key="MyKey"
def decrypt(text)
cipher=AES.new(key)
return cipher.decrypt(base64.b64decode(text))
Although the answer from #101100 was a good one at the time, it's no longer viable. The reference is now a broken link, and the code would only run on older Pythons (<3).
Instead, the pprp project seems to fill the void nicely. On Python 2 or Python 3, just pip install pprp, then:
import pprp
import base64
KEY_SIZE = 16
BLOCK_SIZE = 32
def encrypt(key, plaintext):
key = key.encode('ascii')
plaintext = plaintext.encode('utf-8')
padded_key = key.ljust(KEY_SIZE, b'\0')
sg = pprp.data_source_gen(plaintext, block_size=BLOCK_SIZE)
eg = pprp.rjindael_encrypt_gen(padded_key, sg, block_size=BLOCK_SIZE)
ciphertext = pprp.encrypt_sink(eg)
encoded = base64.b64encode(ciphertext)
return encoded.decode('ascii')
def decrypt(key, encoded):
key = key.encode('ascii')
padded_key = key.ljust(KEY_SIZE, b'\0')
ciphertext = base64.b64decode(encoded.encode('ascii'))
sg = pprp.data_source_gen(ciphertext, block_size=BLOCK_SIZE)
dg = pprp.rjindael_decrypt_gen(padded_key, sg, block_size=BLOCK_SIZE)
return pprp.decrypt_sink(dg).decode('utf-8')
key = 'MyKey'
text = 'test'
encoded = encrypt(key, text)
print(repr(encoded))
# prints 'ju0pt5Y63Vj4qiViL4VL83Wjgirq4QsGDkj+tDcNcrw='
decoded = decrypt(key, encoded)
print(repr(decoded))
# prints 'test'
I'm a little dismayed that the ciphertext comes out different than what you see with 101100's answer. I have, however, used this technique to successfully decrypt data encrypted in PHP as described in the OP.

Categories