I have the following c# code that generates keys:
public static byte[] Encrypt(byte[] plainData, string salt)
{
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
DES.Key = ASCIIEncoding.ASCII.GetBytes(salt);
DES.IV = ASCIIEncoding.ASCII.GetBytes(salt);
ICryptoTransform desencrypt = DES.CreateEncryptor();
byte[] encryptedData = desencrypt.TransformFinalBlock(plainData, 0, plainData.Length);
return encryptedData;
}
private string GetEncryptedKey(string key)
{
return BitConverter.ToString(KeyGeneratorForm.Encrypt(ASCIIEncoding.ASCII.GetBytes(key), "abcdefgh")).Replace("-", "");
}
I'm trying to perform the same thing in PHP:
function get_encrypted_key($key){
$salt = "abcdefgh";
return bin2hex(mcrypt_encrypt(MCRYPT_DES, $salt, $key, MCRYPT_MODE_CBC, $salt));
}
However, there is a small discrepency in the results, as the last 16 chars are always different:
With key "Benjamin Franklin":
C# : 0B3C6E5DF5D747FB3C50DE952FECE3999768F35B890BC391
PHP: 0B3C6E5DF5D747FB3C50DE952FECE3993A881F9AF348C64D
With key "President Franklin D Roosevelt":
C# : C119B50A5A7F8C905A86A43F5694B4D7DD1E8D0577F1CEB32A86FABCEA5711E1
PHP: C119B50A5A7F8C905A86A43F5694B4D7DD1E8D0577F1CEB37ACBE60BB1D21F3F
I've also tried to perform the padding transform to my key using the following code:
function get_encrypted_key($key){
$salt = "abcdefgh";
$extra = 8 - (strlen($key) % 8);
if($extra > 0) {
for($i = 0; $i < $extra; $i++) {
$key.= "\0";
}
}
return bin2hex(mcrypt_encrypt(MCRYPT_DES, $salt, $key, MCRYPT_MODE_CBC, $salt));
}
But I end up with the same results as without padding.
If you have any clue as to what's going on, I'd be glad to hear about it! :)
Thanks
You mentioned trying a "classic" padding snippet. The following quick adaptation of the snippet posted on the mcrypt_encrypt documentation gives the same results you were getting from C#.
PKCS #7 (the default padding scheme used by C#'s SymmetricAlgorithm) pads with bytes where each padding byte's value is the same as the number of bytes of padding, not with zero bytes.
function get_encrypted_key($key)
{
$salt = 'abcdefgh';
$block = mcrypt_get_block_size('des', 'cbc');
$pad = $block - (strlen($key) % $block);
$key .= str_repeat(chr($pad), $pad);
return bin2hex(mcrypt_encrypt(MCRYPT_DES, $salt, $key, MCRYPT_MODE_CBC, $salt));
}
Test output:
php > echo get_encrypted_key('Benjamin Franklin');
0b3c6e5df5d747fb3c50de952fece3999768f35b890bc391
php > echo get_encrypted_key('President Franklin D Roosevelt');
c119b50a5a7f8c905a86a43f5694b4d7dd1e8d0577f1ceb32a86fabcea5711e1
Related
I use the following functions to encrypt/decrypt with NodeJS, they work fine. But I was unable to decrypt the data with PHP to use in some part of the same project.
NodeJS:
function encrypt(text){
var cipher = crypto.createCipher('aes-256-cbc','strong-key')
var crypted = cipher.update(text,'utf8','hex')
crypted += cipher.final('hex');
return crypted;
}
function decrypt(text){
var decipher = crypto.createDecipher('aes-256-cbc','strong-key')
var dec = decipher.update(text,'hex','utf8')
dec += decipher.final('utf8');
return dec;
}
What I tried with PHP is:
openssl_decrypt('fdf32748aa4ce37fc600bbe7be14bfc7', 'AES-256-CBC', "strong-key");
But it keeps returning false/empty. I appreciate healping me to know what I am doing wrong.
Edit:
For example, decrypting 28e1dfdedac467a015a9c8720d0a6451 with PHP should return "Hello World", using the same key as above.
Make sure that your incoming data is the correct format (ie doesn't have any extra layers of encoding). It looks like hex but it's not what openssl_decrypt necessarily expects.
Here's a back and forth PHP example (using an IV which you should too):
$data = 'hello this is some data';
$key = 'this is a cool and secret password';
$iv = random_bytes(16);
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $key, 0, $iv);
echo 'iv (as hex): ', bin2hex($iv), PHP_EOL;
echo 'encrypted: ', $encrypted, PHP_EOL; // note this is not hex
$decrypted = openssl_decrypt($encrypted, 'aes-256-cbc', $key, 0, $iv);
echo 'decrypted: ', $decrypted, PHP_EOL;
$ php test.php
iv (as hex): 02c00788438518f241cb86dc90237102
encrypted: oRZAXMjNle6hkJ9rTHTeUl5VoHQol+020Q/iFnbgbeU=
decrypted: hello this is some data
Edit, even more specific example, highlighting the importance of knowing your encodings:
// test.js
const crypto = require('crypto');
let data = 'hello this is some data';
const key = crypto.scryptSync('Password used to generate key', '', 32); // 256 / 8 = 32
const iv = crypto.randomBytes(16); // Initialization vector.
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let crypted = cipher.update(data,'utf8','hex')
crypted += cipher.final('hex');
console.log('data: ', data);
console.log('key: ', key.toString('hex')); // key: 9266bc531befd01b6a55c232fa0efeb35625079e7024758b2e65d0dd72fe59df
console.log('crypted (as hex): ', crypted); // crypted (as hex): b571d864da0680d77e4880d0071b49e456a1eead4b1cbfa42a9337965a466362
console.log('iv (as hex): ', iv.toString('hex')); // iv (as hex): 788ac1dcee25824b713b5201d07cc133
Here we know all our outputs are hex, so we can re-format them to binary data on the PHP side:
// test.php
$iv = hex2bin( '788ac1dcee25824b713b5201d07cc133' );
$encrypted = hex2bin( 'b571d864da0680d77e4880d0071b49e456a1eead4b1cbfa42a9337965a466362' );
$key = hex2bin('9266bc531befd01b6a55c232fa0efeb35625079e7024758b2e65d0dd72fe59df');
$decrypted = openssl_decrypt($encrypted, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
echo 'decrypted: ', $decrypted, PHP_EOL; // hello this is some data
Final Edit: Now with working key derivation implementation. This successfully decrypts your 'Hello world':
$password = 'strong-key';
// derrive key and IV using function from SO, which implements same method node uses
$ar = deriveKeyAndIV($password, 1, 'aes-256-cbc', 'md5');
$key = $ar['key'];
$iv = $ar['iv'];
$decrypted = openssl_decrypt(hex2bin('28e1dfdedac467a015a9c8720d0a6451'), 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
echo 'decrypted: ', $decrypted, PHP_EOL; // Hello world
function deriveKeyAndIV($data,$count,$cipher,$digest) {
$ivlen = openssl_cipher_iv_length($cipher);
$keylen = 32;
$hash = "";
$hdata = "";
while(strlen($hash) < $keylen+$ivlen) {
$hdata .= $data;
$md_buf = openssl_digest($hdata, $digest);
//
for ($i = 1; $i < $count; $i++) {
$md_buf = openssl_digest ( hex2bin($md_buf),$digest);
}
$hdata = hex2bin($md_buf);
$hash .= $hdata;
}
//
$key = substr($hash,0,$keylen);
$iv = substr($hash,$keylen,$ivlen);
//
return array('key' => $key, 'iv' => $iv);
}
Hi and good day to all members, admin and to everyone. I would like to ask a question that has a connection from my previous post which can be seen here entitled Crypto-Js different output from mcrypt Upon chage of data to encrypt. Now my question is I made another php function that will eventually call this function stated in the link. See below the basic php function I created.
function login($word,$word2)
{
$word = mcrypts_encrypt($word);
$word2 = mcrypts_encrypt($word2);
return $word;
return $word2;
}
Now my question is this, I have tried placing the $word and the $word 2 with real data such as CROW and Blader but It only echoes the encrypted word of CROW ($word) and not Blader ($w0rd2).
For reference purpose I will also include the script for the encrypt.
MCRYPT_ENCRYPT
function mcrypts_encrypt($encrypted)
{
//Padding 6/25/2014
$pad = 16 - (strlen($encrypted) % 16);
$encrypted = $encrypted . str_repeat(chr($pad), $pad);
//Encrypt//Decode
$iv = base64_decode('AAAAAAAAAAAAAAAAAAAAAA==');
$key = base64_decode('ITU2NjNhI0tOc2FmZExOTQ==');
$plaintext = mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv );
//Return encrypted Data
return base64_encode($plaintext);
}
Thanks for the help in advance.
You can only call return from a function once, at that point, the flow of code is returned back to the caller.
To pass multiple values back to the caller, return an array containing both of the values, e.g.
function login($word,$word2)
{
$word = mcrypts_encrypt($word);
$word2 = mcrypts_encrypt($word2);
return array($word, $word2);
}
and use as this;
$encrypted = login('first-word', 'second-word');
echo $encrypted[0]; // the first word, encrypted
echo $encrypted[1]; // the second word, encrypted
function login($word,$word2)
{
$word = mcrypts_encrypt($word);
$word2 = mcrypts_encrypt($word2);
$returnArray["user"] = $word;
$returnArray["pass"] = $word2;
return $returnArray;
}
function call
$loginValues = login('CROW','Blader');
extract($loginValues);
print $user; // prints $word
print $pass; // prints $word2
This works
<?php
function login($word,$word2)
{
$word = mcrypts_encrypt($word);
$word2 = mcrypts_encrypt($word2);
return array($word, $word2);
}
function mcrypts_encrypt($encrypted)
{
//Padding 6/25/2014
$pad = 16 - (strlen($encrypted) % 16);
$encrypted = $encrypted . str_repeat(chr($pad), $pad);
//Encrypt//Decode
$iv = base64_decode('AAAAAAAAAAAAAAAAAAAAAA==');
$key = base64_decode('ITU2NjNhI0tOc2FmZExOTQ==');
$plaintext = mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv );
//Return encrypted Data
return base64_encode($plaintext);
}
var_dump(login("test1", "test2"));
?>
outputs:
array(2) {
[0]=>
string(24) "eeyZfxyUnMykJ23fMamEBQ=="
[1]=>
string(24) "0egb4dfuXbgFg7GzuuBZcQ=="
}
Using PHP, I'm trying to encode a number into another number that I can decode back to the original number. The encoded string needs to be only numbers and should not contain anything else.
Eg: 10 becomes 573563547892 or something like that.
How can I do something like this in PHP? I tried quite a few encrypt decrypt functions, but none output only numbers.
I looking for something to use in a URL that isn't easy to guess.
So: http://www.me.com/index.PHP?page=20 becomes http://www.me.com/index.PHP?page=5705254782562466
Why not using a mathematicat operation on the original number? like x becomes x * y + z. you would only have to make the reverse operation to get the original number. consider using large enough prime numbers for y and/or z
Quite heavy, but very good encryption, by using ord & chr a bit. While this works, consider other options: just being able to use strings rather then numbers already makes it a lot simpler (base64_encode etc.):
<?php
class Crypter {
private $key = '';
private $iv = '';
function __construct($key,$iv){
$this->key = $key;
$this->iv = $iv;
}
protected function getCipher(){
$cipher = mcrypt_module_open(MCRYPT_BLOWFISH,'','cbc','');
mcrypt_generic_init($cipher, $this->key, $this->iv);
return $cipher;
}
function encrypt($string){
$binary = mcrypt_generic($this->getCipher(),$string);
$string = '';
for($i = 0; $i < strlen($binary); $i++){
$string .= str_pad(ord($binary[$i]),3,'0',STR_PAD_LEFT);
}
return $string;
}
function decrypt($encrypted){
//check for missing leading 0's
$encrypted = str_pad($encrypted, ceil(strlen($encrypted) / 3) * 3,'0', STR_PAD_LEFT);
$binary = '';
$values = str_split($encrypted,3);
foreach($values as $chr){
$chr = ltrim($chr,'0');
$binary .= chr($chr);
}
return mdecrypt_generic($this->getCipher(),$binary);
}
}
$crypt = new Crypter('secret key','12348765');
$encrypted = $crypt->encrypt(1234);
echo $encrypted.PHP_EOL;
//fake missing leading 0
$encrypted = ltrim($encrypted,'0');
echo $encrypted.PHP_EOL;
$decrypted = $crypt->decrypt($encrypted);
echo $decrypted.PHP_EOL;
Result:
057044206104214236155088
57044206104214236155088
1234
There is a function in Mysql AES_encrypt.
SELECT AES_encrypt( "Hello World", "password" ) AS encrypted_value
This gives the result: 9438eb79863e7009722fc3f0ad4b7198
But when I use the code in php to do AES_encrypt it gives me a different value.
The PHP code I got from stackoverflow -- PHP AES encrypt / decrypt
<?php
base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$sSecretKey, $sValue,
MCRYPT_MODE_ECB,
mcrypt_create_iv(
mcrypt_get_iv_size(
MCRYPT_RIJNDAEL_256,
MCRYPT_MODE_ECB
),
MCRYPT_RAND)
)
), "\0"
?>
The result from PHP code is ytip2sEkD87gmRk3IVI09qE7T+RoLr20YK4rJp16NkY=
Is there a method in php or codeigniter so that it returns the same value.?
--Thank you.
There are three problems with the code you are using:
As others have mentioned, your PHP code is currently using MCRYPT_RIJNDAEL_256 whereas, as documented under AES_ENCRYPT():
Encoding with a 128-bit key length is used, but you can extend it up to 256 bits by modifying the source. We chose 128 bits because it is much faster and it is secure enough for most purposes.
As others have mentioned, you are applying base64_encode() to convert PHP's binary result to text, whereas the MySQL result appears merely to be a hexadecimal representation of its binary result. You can either use TO_BASE64() in MySQL since v5.6.1 or else bin2hex() in PHP.
As documented under mcrypt_encrypt():
If the size of the data is not n * blocksize, the data will be padded with '\0'.
Whereas MySQL uses PKCS7 padding.
Therefore, to obtain the same results in PHP as you currently show for MySQL:
<?php
class MySQL_Function {
const PKCS7 = 1;
private static function pad($string, $mode, $blocksize = 16) {
$len = $blocksize - (strlen($string) % $blocksize);
switch ($mode) {
case self::PKCS7:
$padding = str_repeat(chr($len), $len); break;
default:
throw new Exception();
}
return $string.$padding;
}
public static function AES_ENCRYPT($str, $key_str) {
return mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key_str, self::pad($str, self::PKCS7),
MCRYPT_MODE_ECB
);
}
}
echo bin2hex(MySQL_Function::AES_encrypt( "Hello World", "password" ));
?>
mcrypt_encrypt is deprecated, so here's a solution that's capable of falling back on openssl_encrypt instead. Truthfully, I don't know how all of it works. It's kind of a composite of some solutions I found regarding replicating MySQL's AES_ENCRYPT in mcrypt_encrypt, and then replicating mcrypt_encrypt in openssl_encrypt. The generation of the key from what would otherwise be used as the salt arguments in AES_ENCRYPT, as well as understanding which cypher to use when, is a little beyond me. But I can say these functions have been time-tested to be functionally identical to their MySql counterparts.
if (!function_exists('mysql_aes_key')) {
/**
* #param string $key
* #return string
*/
function mysql_aes_key($key)
{
$new_key = str_repeat(chr(0), 16);
for ($i = 0, $len = strlen($key); $i < $len; $i++) {
$new_key[$i % 16] = $new_key[$i % 16] ^ $key[$i];
}
return $new_key;
}
}
if (!function_exists('aes_encrypt')) {
/**
* #param string $val
* #param string $cypher
* #param bool $mySqlKey
* #return string
* #throws \BadFunctionCallException
*/
function aes_encrypt($val, $cypher = null, $mySqlKey = true)
{
$salt = getenv('SALT') ?: '1234567890abcdefg';
$key = $mySqlKey ? mysql_aes_key($salt) : $salt;
if (function_exists('mcrypt_encrypt')) {
$cypher = (!$cypher || $cypher == strtolower('aes-128-ecb')) ? MCRYPT_RIJNDAEL_128 : $cypher;
$pad_value = 16 - (strlen($val) % 16);
$val = str_pad($val, (16 * (floor(strlen($val) / 16) + 1)), chr($pad_value));
return #mcrypt_encrypt($cypher, $key, $val, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_DEV_URANDOM));
} elseif (function_exists('openssl_encrypt')) {
//TODO: Create a more comprehensive map of mcrypt <-> openssl cyphers
$cypher = (!$cypher || $cypher == MCRYPT_RIJNDAEL_128) ? 'aes-128-ecb' : $cypher;
return openssl_encrypt($val, $cypher, $key, true);
}
throw new \BadFunctionCallException('No encryption function could be found.');
}
}
if (!function_exists('aes_decrypt')) {
/**
* #param string $val
* #param string $cypher
* #param bool $mySqlKey
* #return string
* #throws \BadFunctionCallException
*/
function aes_decrypt($val, $cypher = null, $mySqlKey = true)
{
$salt = getenv('SALT') ?: '1234567890abcdefg';
$key = $mySqlKey ? mysql_aes_key($salt) : $salt;
if (function_exists('mcrypt_decrypt')) {
$cypher = (!$cypher || $cypher == strtolower('aes-128-ecb')) ? MCRYPT_RIJNDAEL_128 : $cypher;
$val = #mcrypt_decrypt($cypher, $key, $val, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_DEV_URANDOM));
return rtrim($val, chr(0)."..".chr(16));
} elseif (function_exists('openssl_decrypt')) {
//TODO: Create a more comprehensive map of mcrypt <-> openssl cyphers
$cypher = (!$cypher || $cypher == MCRYPT_RIJNDAEL_128) ? 'aes-128-ecb' : $cypher;
return openssl_decrypt($val, $cypher, $key, true);
}
throw new \BadFunctionCallException('No decryption function could be found.');
}
}
So...
putenv('SALT=1234567890abcdefg');
aes_encrypt('some_value') === SELECT AES_ENCRYPT('some_value', '1234567890abcdefg')
aes_decrypt('some_encrypted_value') === SELECT AES_DECRYPT('some_encrypted_value', '1234567890abcdefg')
I tested these by encrypting a value with the php function, and decrypting it with the MySQL one, and visa-versa.
The MySQL AES_encrypt uses a 128-bit key length - Reference here
Whereas your PHP code uses 256-bit key lengths.
To fix the problem you should be able to uses 'MCRYPT_RIJNDAEL_128' instead of 256.
The accepted answer works but is a lot of code, Here's the one liner
function aes_encrypt_str($val,$key){
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128,$key, $val,MCRYPT_MODE_ECB,mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256,MCRYPT_MODE_ECB),MCRYPT_RAND));
}
I have no idea what I'm doing wrong. I just need to be able to encrypt and decrypt without getting weird characters or warnings. It says I'm supposed to be using an IV of length 16 and that I'm using a length of 9 but "0123456789abcdef" is 16 characters.
Warning: mcrypt_generic_init() [function.mcrypt-generic-init]: Iv size incorrect; supplied length: 9, needed: 16 in /home/mcondiff/public_html/projects/enc/enc.php on line 10
See http://www.teamconcept.org/projects/enc/enc.php
I'm lost, confused, a little lightheaded. Here do I go from here? I have to use this encryption and get it working for a project.
<?php
class enc
{
function encrypt($str, $key) {
$key = $this->hex2bin($key);
$td = mcrypt_module_open("rijndael-128", "", "cbc", "fedcba9876543210");
mcrypt_generic_init($td, $key, CIPHER_IV);
$encrypted = mcrypt_generic($td, $str);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return bin2hex($encrypted);
}
function decrypt($code, $key) {
$key = $this->hex2bin($key);
$code = $this->hex2bin($code);
$td = mcrypt_module_open("rijndael-128", "", "cbc", "fedcba9876543210");
mcrypt_generic_init($td, $key, CIPHER_IV);
$decrypted = mdecrypt_generic($td, $code);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return utf8_encode(trim($decrypted));
}
function hex2bin($hexdata) {
$bindata = "";
for ($i = 0; $i < strlen($hexdata); $i += 2) {
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
}
$theEncryption = new enc();
$user = "John Doe";
$email = "john#example.com";
$user = $theEncryption->encrypt($user, "0123456789abcdef");
$email = $theEncryption->encrypt($email, "0123456789abcdef");
echo 'User: '.$user;
echo 'Email: '.$email;
?>
Can somone point me in the right direction or point out what i'm doing wrong?
Thanks
Mike
CIPHER_IV is probably an undefined constant. PHP raises a "Use of undefined constant" notice and then uses the "constant" as string. The string "CIPHER_IV" is 9 characters long.
In your php file, do a print of CIPHER_IV and see what it contains.
See http://us2.php.net/mcrypt_generic_init for the specifics
You've probably copy-pasted the code from a blog: googling mcrypt_generic_init CIPHER_IV only gives this post and a blog ;)
The IV is a parameter that you need to specify to the function, not a constant that the first blogger put in misinterpreting the second blogger's article.
At http://propaso.com/blog/?cat=6, they declare these:
$secret_key = "01234567890abcde";
$iv = "fedcba9876543210";
and then do:
mcrypt_generic_init($td, $secret_key, $iv);
Simply declare your IV to be something, then use it.