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));
}
Related
I am trying to compute a PHP function to have the 3DES (Triple DES) in ECB Mode. But I am getting the wrong result.
My result : 615EDC0E8EAD5DDE
Expected result : 7B66D9A5010A8035
(the expected result is computed with HSM and confirmed with the website) http://tripledes.online-domain-tools.com/
Here is my PHP function, taking as parameters :
$data = "3200000025381234"
$key = "98137332E06BBA25AEE51CFD150EA8E3"
function tripleDES($data, $key) {
$key= hex2bin($key);
$data = hex2bin($data);
$enc = openssl_encrypt($data, 'des-ede3', $key, OPENSSL_RAW_DATA |
OPENSSL_ZERO_PADDING);
return strtoupper(bin2hex($enc));
}
What am I doing wrong ?
Thanks to the answer of Topaco, I understood my mistake.
So I used this github project: https://github.com/gilfether/phpcrypt and corrected my code this way (using my 16-bytes key):
function triple_DES($data, $key){
$key = hex2bin($key);
$data = hex2bin($data);
$crypt = new PHP_Crypt($key, PHP_Crypt::CIPHER_3DES, PHP_Crypt::MODE_ECB);
$encrypt = $crypt->encrypt($data);
return strtoupper(bin2hex($encrypt));
}
public function kryptonite($string){
$salt = "";
$salt_vars = array_merge(range("A","Z"),range("a","z"),range(0,9));
for($i=0;$i < 22;$i++){
$salt.= $salt_vars[array_rand($salt_vars)];
}
return crypt($string, '$6$'.$salt);
}
This returns on refresh:
$6$vnuqcEA70$CHWmPVsDVb.lVpq1PNsDn7.0fSmBX6FU2PlofK6dJOH7FQp6EdSsde3Aw6to8fY1L01/WOcWz8OIE0OxK1LTj.
$6$7lmp9sD4g$I0fAcDjno2Lf255gg6TxTLt9TRwR803ZXiU9BOWJXhWrGbJdPJ3LvAW9w2KbRZ/3EDSSbFrgF7rV7DdB0VliA0
If you closely at the first few lines it's changing constantly. I don't think Hashing is suppose to constantly change! So technically I'll never be able to test against this. Can someone help me with my kryptonite crypt function or explain to me what went wrong really.
As a matter of fact, hashing is supposed to randomly change - it's called random salting. Your crypt function is creating a random salt which is fed to the SHA-512 hasher.
The output of crypt() includes the salt value, which you would then use when hashing a password to compare it to the stored hash.
public function kryptonite($string, $salt = null){
if ($salt === null) {
$salt = "";
$salt_vars = array_merge(range("A","Z"),range("a","z"),range(0,9));
for($i=0;$i < 22;$i++){
$salt.= $salt_vars[array_rand($salt_vars)];
}
$salt = '$6$' . $salt;
}
return crypt($string, $salt);
}
To use this, you'd just do the following:
$storedHash = '.....'; // fetched from database
$inputPassword = '.....'; // from the user
$salt = preg_match('/\$[0-9]\$(.+)\$/')[1]; // php 5.4+
if (kryptonite($inputPassword, $salt) == $storedHash) {
//.... success
}
Note that the array_random implementation of creating a random salt isn't cryptographically secure - it'd be better to use openssl_random_pseudo_bytes() or mt_rand() or such.
You won't be able to get the same result when using array_rand() I'd recommend something like this if you're going to use salts and need a two way encryption
function parse($action, $string) {
$output = false;
$encrypt_method = "AES-256-CBC";
// hash
$key = hash('sha256', "random encryption key that must stay the same");
// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
$iv = substr(hash('sha256', "ARANDOMIV"), 0, 16);
if($action == 'encrypt') {
$output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
$output = base64_encode($output);
} else if( $action == 'decrypt' ){
$output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
}
return $output;
}
You won't get the same hashes when you're using the array_rand() function. The result needs to be deterministic.
Is there a reason why sha1() isn't good enough?
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
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
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.