How to decrypt using Blowfish algorithm in php? - php

I am supposed to write a PHP script to decrypt Blowfish encrypted data.
The data I am receiving for decryption is encrypted by another application (I have no access to it).
The data decrypts fine when am check it using a javascript script (blowfish.js).
How can I decrypt the data in php?
I have tried the mcrypt function in PHP. The code works fine if I encrypt and decrypt using the same code. If I decrypt an encrypted code (in another app) it gives junk.
No idea about what mode to set.
Can anyone suggest on the code below or any PHP BlowFish code without using mcrypt?
<?php
class Encryption
{
static $cypher = 'blowfish';
static $mode = 'cfb';
static $key = '12345678';
public function encrypt($plaintext)
{
$td = mcrypt_module_open(self::$cypher, '', self::$mode, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, self::$key, $iv);
$crypttext = mcrypt_generic($td, $plaintext);
mcrypt_generic_deinit($td);
return $iv.$crypttext;
}
public function decrypt($crypttext)
{
$plaintext = "";
$td = mcrypt_module_open(self::$cypher, '', self::$mode, '');
$ivsize = mcrypt_enc_get_iv_size($td);
$iv = substr($crypttext, 0, $ivsize);
$crypttext = substr($crypttext, $ivsize);
if ($iv)
{
mcrypt_generic_init($td, self::$key, $iv);
$plaintext = mdecrypt_generic($td, $crypttext);
}
return $plaintext;
}
}
$encrypted_text = Encryption::encrypt('this text is unencrypted');
echo "ENCRY=".$encrypted_text;echo "<br/>";
////I am using this part(decryption) coz data already encryption
// Encrypted text from app
$encrypted_text = '29636E7ADA7081E7F5D73121C45E20D5';
// Decrypt text
$decrypted_text = Encryption::decrypt($encrypted_text);
echo "ENCRY=".$decrypted_text;echo "<br/>";
?>

The $iv you use when decrypting must be the same as the Initialization Vector used when encrypting the data. Your own functions transfer this information by prepending the IV to the ciphertext (return $iv.$crypttext;), but the other application might not do so.
You need to find out what IV the other app uses, and pass that to your own code. Since the decrypt function reads the IV from the beginning of the ciphertext you can simply prepend it.
Also, you can test a bit by encrypting the same text with your encrypt function and with the other application. If the outputs do not have the same length (your own is larger), then the app is not including the IV inside the ciphertext and you must obtain this information in another manner.
And of course the cipher mode used (CFB) must be the same between your code and the other app.

There's a nice, easy to implement solution here:
www.codewalkers.com: Encrypt and Decrypt using Blowfish

There is a PEAR Library for creating blowfish encyptions that allow you to choose weather to use MCRYPT Libs, or purely native PHP:
you may view the Library here: Crypt_Blowfish 1.1.0RC2
Select the PHP.php file will show you the source code to do this hard coded in native PHP.

Related

PHP Startup: Unable to load dynamic library 'mcrypt' [duplicate]

The mcrypt-extension is deprecated will be removed in PHP 7.2 according to the comment posted here. So I am looking for an alternative way to encrypt passwords.
Right now I am using something like
mcrypt_encrypt(MCRYPT_RIJNDAEL_128, md5($key, true), $string, MCRYPT_MODE_CBC, $iv)
I need your opinion for the best/strongest way to encrypt passwords, the encrypted password should of course supported by PHP 7.xx and should also be decryptable because my customers do want to have an option to 'recover' their passwords without generating a new one.
It's best practice to hash passwords so they are not decryptable. This makes things slightly more difficult for attackers that may have gained access to your database or files.
If you must encrypt your data and have it decryptable, a guide to secure encryption/decryption is available at https://paragonie.com/white-paper/2015-secure-php-data-encryption. To summarize that link:
Use Libsodium - A PHP extension
If you can't use Libsodium, use defuse/php-encryption - Straight PHP code
If you can't use Libsodium or defuse/php-encryption, use OpenSSL - A lot of servers will already have this installed. If not, it can be compiled with --with-openssl[=DIR]
As suggested by #rqLizard, you can use openssl_encrypt/openssl_decrypt PHP functions instead which provides a much
better alternative to implement AES (The Advanced Encryption Standard) also known as Rijndael encryption.
As per the following Scott's comment at php.net:
If you're writing code to encrypt/encrypt data in 2015, you should use openssl_encrypt() and openssl_decrypt(). The underlying library (libmcrypt) has been abandoned since 2007, and performs far worse than OpenSSL (which leverages AES-NI on modern processors and is cache-timing safe).
Also, MCRYPT_RIJNDAEL_256 is not AES-256, it's a different variant of the Rijndael block cipher. If you want AES-256 in mcrypt, you have to use MCRYPT_RIJNDAEL_128 with a 32-byte key. OpenSSL makes it more obvious which mode you are using (i.e. aes-128-cbc vs aes-256-ctr).
OpenSSL also uses PKCS7 padding with CBC mode rather than mcrypt's NULL byte padding. Thus, mcrypt is more likely to make your code vulnerable to padding oracle attacks than OpenSSL.
Finally, if you are not authenticating your ciphertexts (Encrypt Then MAC), you're doing it wrong.
Further reading:
Using Encryption and Authentication Correctly (for PHP developers).
If You're Typing the Word MCRYPT Into Your PHP Code, You're Doing It Wrong.
Code examples
Example #1
AES Authenticated Encryption in GCM mode example for PHP 7.1+
<?php
//$key should have been previously generated in a cryptographically safe way, like openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$cipher = "aes-128-gcm";
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);
//store $cipher, $iv, and $tag for decryption later
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv, $tag);
echo $original_plaintext."\n";
}
?>
Example #2
AES Authenticated Encryption example for PHP 5.6+
<?php
//$key previously generated safely, ie: openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );
//decrypt later....
$c = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
if (hash_equals($hmac, $calcmac))//PHP 5.6+ timing attack safe comparison
{
echo $original_plaintext."\n";
}
?>
Example #3
Based on above examples, I've changed the following code which aims at encrypting user's session id:
class Session {
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* #param $session_id
* #return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$encrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $session_id, MCRYPT_MODE_CBC, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($encrypt);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* #param $encryptedSessionId
* #return string
*/
public function decrypt($encryptedSessionId) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId);
// Decrypt the string.
$decryptedSessionId = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $decoded, MCRYPT_MODE_CBC, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, "\0");
// Return it.
return $session_id;
}
public function _getIv() {
return md5($this->_getSalt());
}
public function _getSalt() {
return md5($this->drupal->drupalGetHashSalt());
}
}
into:
class Session {
const SESS_CIPHER = 'aes-128-cbc';
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* #param $session_id
* #return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$ciphertext = openssl_encrypt($session_id, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($ciphertext);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* #param $encryptedSessionId
* #return string
*/
public function decrypt($encryptedSessionId) {
// Get the Drupal hash salt as a key.
$key = $this->_getSalt();
// Get the iv.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId, TRUE);
// Decrypt the string.
$decryptedSessionId = openssl_decrypt($decoded, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, '\0');
// Return it.
return $session_id;
}
public function _getIv() {
$ivlen = openssl_cipher_iv_length(self::SESS_CIPHER);
return substr(md5($this->_getSalt()), 0, $ivlen);
}
public function _getSalt() {
return $this->drupal->drupalGetHashSalt();
}
}
To clarify, above change is not a true conversion since the two encryption uses a different block size and a different encrypted data. Additionally, the default padding is different, MCRYPT_RIJNDAEL only supports non-standard null padding. #zaph
Additional notes (from the #zaph's comments):
Rijndael 128 (MCRYPT_RIJNDAEL_128) is equivalent to AES, however Rijndael 256 (MCRYPT_RIJNDAEL_256) is not AES-256 as the 256 specifies a block size of 256-bits, whereas AES has only one block size: 128-bits. So basically Rijndael with a block size of 256-bits (MCRYPT_RIJNDAEL_256) has been mistakenly named due to the choices by the mcrypt developers. #zaph
Rijndael with a block size of 256 may be less secure than with a block size of 128-bits because the latter has had much more reviews and uses. Secondly, interoperability is hindered in that while AES is generally available, where Rijndael with a block size of 256-bits is not.
Encryption with different block sizes for Rijndael produces different encrypted data.
For example, MCRYPT_RIJNDAEL_256 (not equivalent to AES-256) defines a different variant of the Rijndael block cipher with size of 256-bits and a key size based on the passed in key, where aes-256-cbc is Rijndael with a block size of 128-bits with a key size of 256-bits. Therefore they're using different block sizes which produces entirely different encrypted data as mcrypt uses the number to specify the block size, where OpenSSL used the number to specify the key size (AES only has one block size of 128-bits). So basically AES is Rijndael with a block size of 128-bits and key sizes of 128, 192 and 256 bits. Therefore it's better to use AES, which is called Rijndael 128 in OpenSSL.
As detailed by other answers here, the best solution I found is using OpenSSL. It is built into PHP and you don't need any external library. Here are simple examples:
To encrypt:
function encrypt($key, $payload) {
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv);
return base64_encode($encrypted . '::' . $iv);
}
To decrypt:
function decrypt($key, $garble) {
list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
}
Reference link: https://www.shift8web.ca/2017/04/how-to-encrypt-and-execute-your-php-code-with-mcrypt/
Pure-PHP implementation of Rijndael exists with phpseclib available as composer package and works on PHP 7.3 (tested by me).
There's a page on the phpseclib docs, which generates sample code after you input the basic variables (cipher, mode, key size, bit size). It outputs the following for Rijndael, ECB, 256, 256:
a code with mycrypt
$decoded = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, ENCRYPT_KEY, $term, MCRYPT_MODE_ECB);
works like this with the library
$rijndael = new \phpseclib\Crypt\Rijndael(\phpseclib\Crypt\Rijndael::MODE_ECB);
$rijndael->setKey(ENCRYPT_KEY);
$rijndael->setKeyLength(256);
$rijndael->disablePadding();
$rijndael->setBlockLength(256);
$decoded = $rijndael->decrypt($term);
* $term was base64_decoded
You can use phpseclib pollyfill package. You can not use open ssl or libsodium for encrypt/decrypt with rijndael 256.
Another issue, you don't need replacement any code.
You should use OpenSSL over mcrypt as it's actively developed and maintained. It provides better security, maintainability and portability. Secondly it performs AES encryption/decryption much faster. It uses PKCS7 padding by default, but you can specify OPENSSL_ZERO_PADDING if you need it. To use with a 32-byte binary key, you can specify aes-256-cbc which is much obvious than MCRYPT_RIJNDAEL_128.
Here is the code example using Mcrypt:
Unauthenticated AES-256-CBC encryption library written in Mcrypt with PKCS7 padding.
/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeMcryptAES
{
const CIPHER = MCRYPT_RIJNDAEL_128;
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mcrypt_create_iv($ivsize, MCRYPT_DEV_URANDOM);
// Add PKCS7 Padding
$block = mcrypt_get_block_size(self::CIPHER);
$pad = $block - (mb_strlen($message, '8bit') % $block, '8bit');
$message .= str_repeat(chr($pad), $pad);
$ciphertext = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
$message,
MCRYPT_MODE_CBC,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
$plaintext = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$ciphertext,
MCRYPT_MODE_CBC,
$iv
);
$len = mb_strlen($plaintext, '8bit');
$pad = ord($plaintext[$len - 1]);
if ($pad <= 0 || $pad > $block) {
// Padding error!
return false;
}
return mb_substr($plaintext, 0, $len - $pad, '8bit');
}
}
And here is the version written using OpenSSL:
/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeOpensslAES
{
const METHOD = 'aes-256-cbc';
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = openssl_random_pseudo_bytes($ivsize);
$ciphertext = openssl_encrypt(
$message,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
return openssl_decrypt(
$ciphertext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
}
}
Source: If You're Typing the Word MCRYPT Into Your PHP Code, You're Doing It Wrong.
I am using this on PHP 7.2.x, it's working fine for me:
public function make_hash($userStr){
try{
/**
* Used and tested on PHP 7.2x, Salt has been removed manually, it is now added by PHP
*/
return password_hash($userStr, PASSWORD_BCRYPT);
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
and then authenticate the hash with the following function:
public function varify_user($userStr,$hash){
try{
if (password_verify($userStr, $hash)) {
return true;
}
else {
return false;
}
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
Example:
//create hash from user string
$user_password = $obj->make_hash2($user_key);
and to authenticate this hash use the following code:
if($obj->varify_user($key, $user_key)){
//this is correct, you can proceed with
}
That's all.
As pointed out, you should not be storing your users' passwords in a format that is decryptable. Reversable encryption provides an easy route for hackers to find out your users' passwords, which extends to putting your users' accounts at other sites at risk should they use the same password there.
PHP provides a pair of powerful functions for random-salted, one-way hash encryption — password_hash() and password_verify(). Because the hash is automatically random-salted, there is no way for hackers to utilize precompiled tables of password hashes to reverse-engineer the password. Set the PASSWORD_DEFAULT option and future versions of PHP will automatically use stronger algorithms to generate password hashes without you having to update your code.
I was able to translate my Crypto object
Get a copy of php with mcrypt to decrypt the old data. I went to http://php.net/get/php-7.1.12.tar.gz/from/a/mirror, compiled it, then added the ext/mcrypt extension (configure;make;make install). I think I had to add the extenstion=mcrypt.so line to the php.ini as well. A series of scripts to build intermediate versions of the data with all data unencrypted.
Build a public and private key for openssl
openssl genrsa -des3 -out pkey.pem 2048
(set a password)
openssl rsa -in pkey.pem -out pkey-pub.pem -outform PEM -pubout
To Encrypt (using public key) use openssl_seal. From what I've read, openssl_encrypt using an RSA key is limited to 11 bytes less than the key length (See http://php.net/manual/en/function.openssl-public-encrypt.php comment by Thomas Horsten)
$pubKey = openssl_get_publickey(file_get_contents('./pkey-pub.pem'));
openssl_seal($pwd, $sealed, $ekeys, [ $pubKey ]);
$encryptedPassword = base64_encode($sealed);
$key = base64_encode($ekeys[0]);
You could probably store the raw binary.
To Decrypt (using private key)
$passphrase="passphrase here";
$privKey = openssl_get_privatekey(file_get_contents('./pkey.pem'), $passphrase);
// I base64_decode() from my db columns
openssl_open($encryptedPassword, $plain, $key, $privKey);
echo "<h3>Password=$plain</h3>";
P.S. You can't encrypt the empty string ("")
P.P.S. This is for a password database not for user validation.
You should use openssl_encrypt() function.

m_crypt() is deprecated in PHP 7.2 [duplicate]

The mcrypt-extension is deprecated will be removed in PHP 7.2 according to the comment posted here. So I am looking for an alternative way to encrypt passwords.
Right now I am using something like
mcrypt_encrypt(MCRYPT_RIJNDAEL_128, md5($key, true), $string, MCRYPT_MODE_CBC, $iv)
I need your opinion for the best/strongest way to encrypt passwords, the encrypted password should of course supported by PHP 7.xx and should also be decryptable because my customers do want to have an option to 'recover' their passwords without generating a new one.
It's best practice to hash passwords so they are not decryptable. This makes things slightly more difficult for attackers that may have gained access to your database or files.
If you must encrypt your data and have it decryptable, a guide to secure encryption/decryption is available at https://paragonie.com/white-paper/2015-secure-php-data-encryption. To summarize that link:
Use Libsodium - A PHP extension
If you can't use Libsodium, use defuse/php-encryption - Straight PHP code
If you can't use Libsodium or defuse/php-encryption, use OpenSSL - A lot of servers will already have this installed. If not, it can be compiled with --with-openssl[=DIR]
As suggested by #rqLizard, you can use openssl_encrypt/openssl_decrypt PHP functions instead which provides a much
better alternative to implement AES (The Advanced Encryption Standard) also known as Rijndael encryption.
As per the following Scott's comment at php.net:
If you're writing code to encrypt/encrypt data in 2015, you should use openssl_encrypt() and openssl_decrypt(). The underlying library (libmcrypt) has been abandoned since 2007, and performs far worse than OpenSSL (which leverages AES-NI on modern processors and is cache-timing safe).
Also, MCRYPT_RIJNDAEL_256 is not AES-256, it's a different variant of the Rijndael block cipher. If you want AES-256 in mcrypt, you have to use MCRYPT_RIJNDAEL_128 with a 32-byte key. OpenSSL makes it more obvious which mode you are using (i.e. aes-128-cbc vs aes-256-ctr).
OpenSSL also uses PKCS7 padding with CBC mode rather than mcrypt's NULL byte padding. Thus, mcrypt is more likely to make your code vulnerable to padding oracle attacks than OpenSSL.
Finally, if you are not authenticating your ciphertexts (Encrypt Then MAC), you're doing it wrong.
Further reading:
Using Encryption and Authentication Correctly (for PHP developers).
If You're Typing the Word MCRYPT Into Your PHP Code, You're Doing It Wrong.
Code examples
Example #1
AES Authenticated Encryption in GCM mode example for PHP 7.1+
<?php
//$key should have been previously generated in a cryptographically safe way, like openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$cipher = "aes-128-gcm";
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);
//store $cipher, $iv, and $tag for decryption later
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv, $tag);
echo $original_plaintext."\n";
}
?>
Example #2
AES Authenticated Encryption example for PHP 5.6+
<?php
//$key previously generated safely, ie: openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );
//decrypt later....
$c = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
if (hash_equals($hmac, $calcmac))//PHP 5.6+ timing attack safe comparison
{
echo $original_plaintext."\n";
}
?>
Example #3
Based on above examples, I've changed the following code which aims at encrypting user's session id:
class Session {
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* #param $session_id
* #return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$encrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $session_id, MCRYPT_MODE_CBC, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($encrypt);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* #param $encryptedSessionId
* #return string
*/
public function decrypt($encryptedSessionId) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId);
// Decrypt the string.
$decryptedSessionId = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $decoded, MCRYPT_MODE_CBC, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, "\0");
// Return it.
return $session_id;
}
public function _getIv() {
return md5($this->_getSalt());
}
public function _getSalt() {
return md5($this->drupal->drupalGetHashSalt());
}
}
into:
class Session {
const SESS_CIPHER = 'aes-128-cbc';
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* #param $session_id
* #return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$ciphertext = openssl_encrypt($session_id, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($ciphertext);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* #param $encryptedSessionId
* #return string
*/
public function decrypt($encryptedSessionId) {
// Get the Drupal hash salt as a key.
$key = $this->_getSalt();
// Get the iv.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId, TRUE);
// Decrypt the string.
$decryptedSessionId = openssl_decrypt($decoded, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, '\0');
// Return it.
return $session_id;
}
public function _getIv() {
$ivlen = openssl_cipher_iv_length(self::SESS_CIPHER);
return substr(md5($this->_getSalt()), 0, $ivlen);
}
public function _getSalt() {
return $this->drupal->drupalGetHashSalt();
}
}
To clarify, above change is not a true conversion since the two encryption uses a different block size and a different encrypted data. Additionally, the default padding is different, MCRYPT_RIJNDAEL only supports non-standard null padding. #zaph
Additional notes (from the #zaph's comments):
Rijndael 128 (MCRYPT_RIJNDAEL_128) is equivalent to AES, however Rijndael 256 (MCRYPT_RIJNDAEL_256) is not AES-256 as the 256 specifies a block size of 256-bits, whereas AES has only one block size: 128-bits. So basically Rijndael with a block size of 256-bits (MCRYPT_RIJNDAEL_256) has been mistakenly named due to the choices by the mcrypt developers. #zaph
Rijndael with a block size of 256 may be less secure than with a block size of 128-bits because the latter has had much more reviews and uses. Secondly, interoperability is hindered in that while AES is generally available, where Rijndael with a block size of 256-bits is not.
Encryption with different block sizes for Rijndael produces different encrypted data.
For example, MCRYPT_RIJNDAEL_256 (not equivalent to AES-256) defines a different variant of the Rijndael block cipher with size of 256-bits and a key size based on the passed in key, where aes-256-cbc is Rijndael with a block size of 128-bits with a key size of 256-bits. Therefore they're using different block sizes which produces entirely different encrypted data as mcrypt uses the number to specify the block size, where OpenSSL used the number to specify the key size (AES only has one block size of 128-bits). So basically AES is Rijndael with a block size of 128-bits and key sizes of 128, 192 and 256 bits. Therefore it's better to use AES, which is called Rijndael 128 in OpenSSL.
As detailed by other answers here, the best solution I found is using OpenSSL. It is built into PHP and you don't need any external library. Here are simple examples:
To encrypt:
function encrypt($key, $payload) {
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv);
return base64_encode($encrypted . '::' . $iv);
}
To decrypt:
function decrypt($key, $garble) {
list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
}
Reference link: https://www.shift8web.ca/2017/04/how-to-encrypt-and-execute-your-php-code-with-mcrypt/
Pure-PHP implementation of Rijndael exists with phpseclib available as composer package and works on PHP 7.3 (tested by me).
There's a page on the phpseclib docs, which generates sample code after you input the basic variables (cipher, mode, key size, bit size). It outputs the following for Rijndael, ECB, 256, 256:
a code with mycrypt
$decoded = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, ENCRYPT_KEY, $term, MCRYPT_MODE_ECB);
works like this with the library
$rijndael = new \phpseclib\Crypt\Rijndael(\phpseclib\Crypt\Rijndael::MODE_ECB);
$rijndael->setKey(ENCRYPT_KEY);
$rijndael->setKeyLength(256);
$rijndael->disablePadding();
$rijndael->setBlockLength(256);
$decoded = $rijndael->decrypt($term);
* $term was base64_decoded
You can use phpseclib pollyfill package. You can not use open ssl or libsodium for encrypt/decrypt with rijndael 256.
Another issue, you don't need replacement any code.
You should use OpenSSL over mcrypt as it's actively developed and maintained. It provides better security, maintainability and portability. Secondly it performs AES encryption/decryption much faster. It uses PKCS7 padding by default, but you can specify OPENSSL_ZERO_PADDING if you need it. To use with a 32-byte binary key, you can specify aes-256-cbc which is much obvious than MCRYPT_RIJNDAEL_128.
Here is the code example using Mcrypt:
Unauthenticated AES-256-CBC encryption library written in Mcrypt with PKCS7 padding.
/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeMcryptAES
{
const CIPHER = MCRYPT_RIJNDAEL_128;
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mcrypt_create_iv($ivsize, MCRYPT_DEV_URANDOM);
// Add PKCS7 Padding
$block = mcrypt_get_block_size(self::CIPHER);
$pad = $block - (mb_strlen($message, '8bit') % $block, '8bit');
$message .= str_repeat(chr($pad), $pad);
$ciphertext = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
$message,
MCRYPT_MODE_CBC,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
$plaintext = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$ciphertext,
MCRYPT_MODE_CBC,
$iv
);
$len = mb_strlen($plaintext, '8bit');
$pad = ord($plaintext[$len - 1]);
if ($pad <= 0 || $pad > $block) {
// Padding error!
return false;
}
return mb_substr($plaintext, 0, $len - $pad, '8bit');
}
}
And here is the version written using OpenSSL:
/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeOpensslAES
{
const METHOD = 'aes-256-cbc';
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = openssl_random_pseudo_bytes($ivsize);
$ciphertext = openssl_encrypt(
$message,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
return openssl_decrypt(
$ciphertext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
}
}
Source: If You're Typing the Word MCRYPT Into Your PHP Code, You're Doing It Wrong.
I am using this on PHP 7.2.x, it's working fine for me:
public function make_hash($userStr){
try{
/**
* Used and tested on PHP 7.2x, Salt has been removed manually, it is now added by PHP
*/
return password_hash($userStr, PASSWORD_BCRYPT);
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
and then authenticate the hash with the following function:
public function varify_user($userStr,$hash){
try{
if (password_verify($userStr, $hash)) {
return true;
}
else {
return false;
}
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
Example:
//create hash from user string
$user_password = $obj->make_hash2($user_key);
and to authenticate this hash use the following code:
if($obj->varify_user($key, $user_key)){
//this is correct, you can proceed with
}
That's all.
As pointed out, you should not be storing your users' passwords in a format that is decryptable. Reversable encryption provides an easy route for hackers to find out your users' passwords, which extends to putting your users' accounts at other sites at risk should they use the same password there.
PHP provides a pair of powerful functions for random-salted, one-way hash encryption — password_hash() and password_verify(). Because the hash is automatically random-salted, there is no way for hackers to utilize precompiled tables of password hashes to reverse-engineer the password. Set the PASSWORD_DEFAULT option and future versions of PHP will automatically use stronger algorithms to generate password hashes without you having to update your code.
I was able to translate my Crypto object
Get a copy of php with mcrypt to decrypt the old data. I went to http://php.net/get/php-7.1.12.tar.gz/from/a/mirror, compiled it, then added the ext/mcrypt extension (configure;make;make install). I think I had to add the extenstion=mcrypt.so line to the php.ini as well. A series of scripts to build intermediate versions of the data with all data unencrypted.
Build a public and private key for openssl
openssl genrsa -des3 -out pkey.pem 2048
(set a password)
openssl rsa -in pkey.pem -out pkey-pub.pem -outform PEM -pubout
To Encrypt (using public key) use openssl_seal. From what I've read, openssl_encrypt using an RSA key is limited to 11 bytes less than the key length (See http://php.net/manual/en/function.openssl-public-encrypt.php comment by Thomas Horsten)
$pubKey = openssl_get_publickey(file_get_contents('./pkey-pub.pem'));
openssl_seal($pwd, $sealed, $ekeys, [ $pubKey ]);
$encryptedPassword = base64_encode($sealed);
$key = base64_encode($ekeys[0]);
You could probably store the raw binary.
To Decrypt (using private key)
$passphrase="passphrase here";
$privKey = openssl_get_privatekey(file_get_contents('./pkey.pem'), $passphrase);
// I base64_decode() from my db columns
openssl_open($encryptedPassword, $plain, $key, $privKey);
echo "<h3>Password=$plain</h3>";
P.S. You can't encrypt the empty string ("")
P.P.S. This is for a password database not for user validation.
You should use openssl_encrypt() function.

Deprecated: Function mcrypt_decrypt() in php [duplicate]

The mcrypt-extension is deprecated will be removed in PHP 7.2 according to the comment posted here. So I am looking for an alternative way to encrypt passwords.
Right now I am using something like
mcrypt_encrypt(MCRYPT_RIJNDAEL_128, md5($key, true), $string, MCRYPT_MODE_CBC, $iv)
I need your opinion for the best/strongest way to encrypt passwords, the encrypted password should of course supported by PHP 7.xx and should also be decryptable because my customers do want to have an option to 'recover' their passwords without generating a new one.
It's best practice to hash passwords so they are not decryptable. This makes things slightly more difficult for attackers that may have gained access to your database or files.
If you must encrypt your data and have it decryptable, a guide to secure encryption/decryption is available at https://paragonie.com/white-paper/2015-secure-php-data-encryption. To summarize that link:
Use Libsodium - A PHP extension
If you can't use Libsodium, use defuse/php-encryption - Straight PHP code
If you can't use Libsodium or defuse/php-encryption, use OpenSSL - A lot of servers will already have this installed. If not, it can be compiled with --with-openssl[=DIR]
As suggested by #rqLizard, you can use openssl_encrypt/openssl_decrypt PHP functions instead which provides a much
better alternative to implement AES (The Advanced Encryption Standard) also known as Rijndael encryption.
As per the following Scott's comment at php.net:
If you're writing code to encrypt/encrypt data in 2015, you should use openssl_encrypt() and openssl_decrypt(). The underlying library (libmcrypt) has been abandoned since 2007, and performs far worse than OpenSSL (which leverages AES-NI on modern processors and is cache-timing safe).
Also, MCRYPT_RIJNDAEL_256 is not AES-256, it's a different variant of the Rijndael block cipher. If you want AES-256 in mcrypt, you have to use MCRYPT_RIJNDAEL_128 with a 32-byte key. OpenSSL makes it more obvious which mode you are using (i.e. aes-128-cbc vs aes-256-ctr).
OpenSSL also uses PKCS7 padding with CBC mode rather than mcrypt's NULL byte padding. Thus, mcrypt is more likely to make your code vulnerable to padding oracle attacks than OpenSSL.
Finally, if you are not authenticating your ciphertexts (Encrypt Then MAC), you're doing it wrong.
Further reading:
Using Encryption and Authentication Correctly (for PHP developers).
If You're Typing the Word MCRYPT Into Your PHP Code, You're Doing It Wrong.
Code examples
Example #1
AES Authenticated Encryption in GCM mode example for PHP 7.1+
<?php
//$key should have been previously generated in a cryptographically safe way, like openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$cipher = "aes-128-gcm";
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);
//store $cipher, $iv, and $tag for decryption later
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv, $tag);
echo $original_plaintext."\n";
}
?>
Example #2
AES Authenticated Encryption example for PHP 5.6+
<?php
//$key previously generated safely, ie: openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );
//decrypt later....
$c = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
if (hash_equals($hmac, $calcmac))//PHP 5.6+ timing attack safe comparison
{
echo $original_plaintext."\n";
}
?>
Example #3
Based on above examples, I've changed the following code which aims at encrypting user's session id:
class Session {
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* #param $session_id
* #return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$encrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $session_id, MCRYPT_MODE_CBC, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($encrypt);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* #param $encryptedSessionId
* #return string
*/
public function decrypt($encryptedSessionId) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId);
// Decrypt the string.
$decryptedSessionId = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $decoded, MCRYPT_MODE_CBC, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, "\0");
// Return it.
return $session_id;
}
public function _getIv() {
return md5($this->_getSalt());
}
public function _getSalt() {
return md5($this->drupal->drupalGetHashSalt());
}
}
into:
class Session {
const SESS_CIPHER = 'aes-128-cbc';
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* #param $session_id
* #return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$ciphertext = openssl_encrypt($session_id, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($ciphertext);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* #param $encryptedSessionId
* #return string
*/
public function decrypt($encryptedSessionId) {
// Get the Drupal hash salt as a key.
$key = $this->_getSalt();
// Get the iv.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId, TRUE);
// Decrypt the string.
$decryptedSessionId = openssl_decrypt($decoded, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, '\0');
// Return it.
return $session_id;
}
public function _getIv() {
$ivlen = openssl_cipher_iv_length(self::SESS_CIPHER);
return substr(md5($this->_getSalt()), 0, $ivlen);
}
public function _getSalt() {
return $this->drupal->drupalGetHashSalt();
}
}
To clarify, above change is not a true conversion since the two encryption uses a different block size and a different encrypted data. Additionally, the default padding is different, MCRYPT_RIJNDAEL only supports non-standard null padding. #zaph
Additional notes (from the #zaph's comments):
Rijndael 128 (MCRYPT_RIJNDAEL_128) is equivalent to AES, however Rijndael 256 (MCRYPT_RIJNDAEL_256) is not AES-256 as the 256 specifies a block size of 256-bits, whereas AES has only one block size: 128-bits. So basically Rijndael with a block size of 256-bits (MCRYPT_RIJNDAEL_256) has been mistakenly named due to the choices by the mcrypt developers. #zaph
Rijndael with a block size of 256 may be less secure than with a block size of 128-bits because the latter has had much more reviews and uses. Secondly, interoperability is hindered in that while AES is generally available, where Rijndael with a block size of 256-bits is not.
Encryption with different block sizes for Rijndael produces different encrypted data.
For example, MCRYPT_RIJNDAEL_256 (not equivalent to AES-256) defines a different variant of the Rijndael block cipher with size of 256-bits and a key size based on the passed in key, where aes-256-cbc is Rijndael with a block size of 128-bits with a key size of 256-bits. Therefore they're using different block sizes which produces entirely different encrypted data as mcrypt uses the number to specify the block size, where OpenSSL used the number to specify the key size (AES only has one block size of 128-bits). So basically AES is Rijndael with a block size of 128-bits and key sizes of 128, 192 and 256 bits. Therefore it's better to use AES, which is called Rijndael 128 in OpenSSL.
As detailed by other answers here, the best solution I found is using OpenSSL. It is built into PHP and you don't need any external library. Here are simple examples:
To encrypt:
function encrypt($key, $payload) {
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv);
return base64_encode($encrypted . '::' . $iv);
}
To decrypt:
function decrypt($key, $garble) {
list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
}
Reference link: https://www.shift8web.ca/2017/04/how-to-encrypt-and-execute-your-php-code-with-mcrypt/
Pure-PHP implementation of Rijndael exists with phpseclib available as composer package and works on PHP 7.3 (tested by me).
There's a page on the phpseclib docs, which generates sample code after you input the basic variables (cipher, mode, key size, bit size). It outputs the following for Rijndael, ECB, 256, 256:
a code with mycrypt
$decoded = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, ENCRYPT_KEY, $term, MCRYPT_MODE_ECB);
works like this with the library
$rijndael = new \phpseclib\Crypt\Rijndael(\phpseclib\Crypt\Rijndael::MODE_ECB);
$rijndael->setKey(ENCRYPT_KEY);
$rijndael->setKeyLength(256);
$rijndael->disablePadding();
$rijndael->setBlockLength(256);
$decoded = $rijndael->decrypt($term);
* $term was base64_decoded
You can use phpseclib pollyfill package. You can not use open ssl or libsodium for encrypt/decrypt with rijndael 256.
Another issue, you don't need replacement any code.
You should use OpenSSL over mcrypt as it's actively developed and maintained. It provides better security, maintainability and portability. Secondly it performs AES encryption/decryption much faster. It uses PKCS7 padding by default, but you can specify OPENSSL_ZERO_PADDING if you need it. To use with a 32-byte binary key, you can specify aes-256-cbc which is much obvious than MCRYPT_RIJNDAEL_128.
Here is the code example using Mcrypt:
Unauthenticated AES-256-CBC encryption library written in Mcrypt with PKCS7 padding.
/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeMcryptAES
{
const CIPHER = MCRYPT_RIJNDAEL_128;
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mcrypt_create_iv($ivsize, MCRYPT_DEV_URANDOM);
// Add PKCS7 Padding
$block = mcrypt_get_block_size(self::CIPHER);
$pad = $block - (mb_strlen($message, '8bit') % $block, '8bit');
$message .= str_repeat(chr($pad), $pad);
$ciphertext = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
$message,
MCRYPT_MODE_CBC,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
$plaintext = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$ciphertext,
MCRYPT_MODE_CBC,
$iv
);
$len = mb_strlen($plaintext, '8bit');
$pad = ord($plaintext[$len - 1]);
if ($pad <= 0 || $pad > $block) {
// Padding error!
return false;
}
return mb_substr($plaintext, 0, $len - $pad, '8bit');
}
}
And here is the version written using OpenSSL:
/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeOpensslAES
{
const METHOD = 'aes-256-cbc';
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = openssl_random_pseudo_bytes($ivsize);
$ciphertext = openssl_encrypt(
$message,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
return openssl_decrypt(
$ciphertext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
}
}
Source: If You're Typing the Word MCRYPT Into Your PHP Code, You're Doing It Wrong.
I am using this on PHP 7.2.x, it's working fine for me:
public function make_hash($userStr){
try{
/**
* Used and tested on PHP 7.2x, Salt has been removed manually, it is now added by PHP
*/
return password_hash($userStr, PASSWORD_BCRYPT);
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
and then authenticate the hash with the following function:
public function varify_user($userStr,$hash){
try{
if (password_verify($userStr, $hash)) {
return true;
}
else {
return false;
}
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
Example:
//create hash from user string
$user_password = $obj->make_hash2($user_key);
and to authenticate this hash use the following code:
if($obj->varify_user($key, $user_key)){
//this is correct, you can proceed with
}
That's all.
As pointed out, you should not be storing your users' passwords in a format that is decryptable. Reversable encryption provides an easy route for hackers to find out your users' passwords, which extends to putting your users' accounts at other sites at risk should they use the same password there.
PHP provides a pair of powerful functions for random-salted, one-way hash encryption — password_hash() and password_verify(). Because the hash is automatically random-salted, there is no way for hackers to utilize precompiled tables of password hashes to reverse-engineer the password. Set the PASSWORD_DEFAULT option and future versions of PHP will automatically use stronger algorithms to generate password hashes without you having to update your code.
I was able to translate my Crypto object
Get a copy of php with mcrypt to decrypt the old data. I went to http://php.net/get/php-7.1.12.tar.gz/from/a/mirror, compiled it, then added the ext/mcrypt extension (configure;make;make install). I think I had to add the extenstion=mcrypt.so line to the php.ini as well. A series of scripts to build intermediate versions of the data with all data unencrypted.
Build a public and private key for openssl
openssl genrsa -des3 -out pkey.pem 2048
(set a password)
openssl rsa -in pkey.pem -out pkey-pub.pem -outform PEM -pubout
To Encrypt (using public key) use openssl_seal. From what I've read, openssl_encrypt using an RSA key is limited to 11 bytes less than the key length (See http://php.net/manual/en/function.openssl-public-encrypt.php comment by Thomas Horsten)
$pubKey = openssl_get_publickey(file_get_contents('./pkey-pub.pem'));
openssl_seal($pwd, $sealed, $ekeys, [ $pubKey ]);
$encryptedPassword = base64_encode($sealed);
$key = base64_encode($ekeys[0]);
You could probably store the raw binary.
To Decrypt (using private key)
$passphrase="passphrase here";
$privKey = openssl_get_privatekey(file_get_contents('./pkey.pem'), $passphrase);
// I base64_decode() from my db columns
openssl_open($encryptedPassword, $plain, $key, $privKey);
echo "<h3>Password=$plain</h3>";
P.S. You can't encrypt the empty string ("")
P.P.S. This is for a password database not for user validation.
You should use openssl_encrypt() function.

mcrypt is deprecated, what is the alternative?

The mcrypt-extension is deprecated will be removed in PHP 7.2 according to the comment posted here. So I am looking for an alternative way to encrypt passwords.
Right now I am using something like
mcrypt_encrypt(MCRYPT_RIJNDAEL_128, md5($key, true), $string, MCRYPT_MODE_CBC, $iv)
I need your opinion for the best/strongest way to encrypt passwords, the encrypted password should of course supported by PHP 7.xx and should also be decryptable because my customers do want to have an option to 'recover' their passwords without generating a new one.
It's best practice to hash passwords so they are not decryptable. This makes things slightly more difficult for attackers that may have gained access to your database or files.
If you must encrypt your data and have it decryptable, a guide to secure encryption/decryption is available at https://paragonie.com/white-paper/2015-secure-php-data-encryption. To summarize that link:
Use Libsodium - A PHP extension
If you can't use Libsodium, use defuse/php-encryption - Straight PHP code
If you can't use Libsodium or defuse/php-encryption, use OpenSSL - A lot of servers will already have this installed. If not, it can be compiled with --with-openssl[=DIR]
As suggested by #rqLizard, you can use openssl_encrypt/openssl_decrypt PHP functions instead which provides a much
better alternative to implement AES (The Advanced Encryption Standard) also known as Rijndael encryption.
As per the following Scott's comment at php.net:
If you're writing code to encrypt/encrypt data in 2015, you should use openssl_encrypt() and openssl_decrypt(). The underlying library (libmcrypt) has been abandoned since 2007, and performs far worse than OpenSSL (which leverages AES-NI on modern processors and is cache-timing safe).
Also, MCRYPT_RIJNDAEL_256 is not AES-256, it's a different variant of the Rijndael block cipher. If you want AES-256 in mcrypt, you have to use MCRYPT_RIJNDAEL_128 with a 32-byte key. OpenSSL makes it more obvious which mode you are using (i.e. aes-128-cbc vs aes-256-ctr).
OpenSSL also uses PKCS7 padding with CBC mode rather than mcrypt's NULL byte padding. Thus, mcrypt is more likely to make your code vulnerable to padding oracle attacks than OpenSSL.
Finally, if you are not authenticating your ciphertexts (Encrypt Then MAC), you're doing it wrong.
Further reading:
Using Encryption and Authentication Correctly (for PHP developers).
If You're Typing the Word MCRYPT Into Your PHP Code, You're Doing It Wrong.
Code examples
Example #1
AES Authenticated Encryption in GCM mode example for PHP 7.1+
<?php
//$key should have been previously generated in a cryptographically safe way, like openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$cipher = "aes-128-gcm";
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);
//store $cipher, $iv, and $tag for decryption later
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv, $tag);
echo $original_plaintext."\n";
}
?>
Example #2
AES Authenticated Encryption example for PHP 5.6+
<?php
//$key previously generated safely, ie: openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );
//decrypt later....
$c = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
if (hash_equals($hmac, $calcmac))//PHP 5.6+ timing attack safe comparison
{
echo $original_plaintext."\n";
}
?>
Example #3
Based on above examples, I've changed the following code which aims at encrypting user's session id:
class Session {
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* #param $session_id
* #return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$encrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $session_id, MCRYPT_MODE_CBC, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($encrypt);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* #param $encryptedSessionId
* #return string
*/
public function decrypt($encryptedSessionId) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId);
// Decrypt the string.
$decryptedSessionId = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $decoded, MCRYPT_MODE_CBC, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, "\0");
// Return it.
return $session_id;
}
public function _getIv() {
return md5($this->_getSalt());
}
public function _getSalt() {
return md5($this->drupal->drupalGetHashSalt());
}
}
into:
class Session {
const SESS_CIPHER = 'aes-128-cbc';
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* #param $session_id
* #return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$ciphertext = openssl_encrypt($session_id, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($ciphertext);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* #param $encryptedSessionId
* #return string
*/
public function decrypt($encryptedSessionId) {
// Get the Drupal hash salt as a key.
$key = $this->_getSalt();
// Get the iv.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId, TRUE);
// Decrypt the string.
$decryptedSessionId = openssl_decrypt($decoded, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, '\0');
// Return it.
return $session_id;
}
public function _getIv() {
$ivlen = openssl_cipher_iv_length(self::SESS_CIPHER);
return substr(md5($this->_getSalt()), 0, $ivlen);
}
public function _getSalt() {
return $this->drupal->drupalGetHashSalt();
}
}
To clarify, above change is not a true conversion since the two encryption uses a different block size and a different encrypted data. Additionally, the default padding is different, MCRYPT_RIJNDAEL only supports non-standard null padding. #zaph
Additional notes (from the #zaph's comments):
Rijndael 128 (MCRYPT_RIJNDAEL_128) is equivalent to AES, however Rijndael 256 (MCRYPT_RIJNDAEL_256) is not AES-256 as the 256 specifies a block size of 256-bits, whereas AES has only one block size: 128-bits. So basically Rijndael with a block size of 256-bits (MCRYPT_RIJNDAEL_256) has been mistakenly named due to the choices by the mcrypt developers. #zaph
Rijndael with a block size of 256 may be less secure than with a block size of 128-bits because the latter has had much more reviews and uses. Secondly, interoperability is hindered in that while AES is generally available, where Rijndael with a block size of 256-bits is not.
Encryption with different block sizes for Rijndael produces different encrypted data.
For example, MCRYPT_RIJNDAEL_256 (not equivalent to AES-256) defines a different variant of the Rijndael block cipher with size of 256-bits and a key size based on the passed in key, where aes-256-cbc is Rijndael with a block size of 128-bits with a key size of 256-bits. Therefore they're using different block sizes which produces entirely different encrypted data as mcrypt uses the number to specify the block size, where OpenSSL used the number to specify the key size (AES only has one block size of 128-bits). So basically AES is Rijndael with a block size of 128-bits and key sizes of 128, 192 and 256 bits. Therefore it's better to use AES, which is called Rijndael 128 in OpenSSL.
As detailed by other answers here, the best solution I found is using OpenSSL. It is built into PHP and you don't need any external library. Here are simple examples:
To encrypt:
function encrypt($key, $payload) {
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv);
return base64_encode($encrypted . '::' . $iv);
}
To decrypt:
function decrypt($key, $garble) {
list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
}
Reference link: https://www.shift8web.ca/2017/04/how-to-encrypt-and-execute-your-php-code-with-mcrypt/
Pure-PHP implementation of Rijndael exists with phpseclib available as composer package and works on PHP 7.3 (tested by me).
There's a page on the phpseclib docs, which generates sample code after you input the basic variables (cipher, mode, key size, bit size). It outputs the following for Rijndael, ECB, 256, 256:
a code with mycrypt
$decoded = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, ENCRYPT_KEY, $term, MCRYPT_MODE_ECB);
works like this with the library
$rijndael = new \phpseclib\Crypt\Rijndael(\phpseclib\Crypt\Rijndael::MODE_ECB);
$rijndael->setKey(ENCRYPT_KEY);
$rijndael->setKeyLength(256);
$rijndael->disablePadding();
$rijndael->setBlockLength(256);
$decoded = $rijndael->decrypt($term);
* $term was base64_decoded
You can use phpseclib pollyfill package. You can not use open ssl or libsodium for encrypt/decrypt with rijndael 256.
Another issue, you don't need replacement any code.
You should use OpenSSL over mcrypt as it's actively developed and maintained. It provides better security, maintainability and portability. Secondly it performs AES encryption/decryption much faster. It uses PKCS7 padding by default, but you can specify OPENSSL_ZERO_PADDING if you need it. To use with a 32-byte binary key, you can specify aes-256-cbc which is much obvious than MCRYPT_RIJNDAEL_128.
Here is the code example using Mcrypt:
Unauthenticated AES-256-CBC encryption library written in Mcrypt with PKCS7 padding.
/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeMcryptAES
{
const CIPHER = MCRYPT_RIJNDAEL_128;
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mcrypt_create_iv($ivsize, MCRYPT_DEV_URANDOM);
// Add PKCS7 Padding
$block = mcrypt_get_block_size(self::CIPHER);
$pad = $block - (mb_strlen($message, '8bit') % $block, '8bit');
$message .= str_repeat(chr($pad), $pad);
$ciphertext = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
$message,
MCRYPT_MODE_CBC,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
$plaintext = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$ciphertext,
MCRYPT_MODE_CBC,
$iv
);
$len = mb_strlen($plaintext, '8bit');
$pad = ord($plaintext[$len - 1]);
if ($pad <= 0 || $pad > $block) {
// Padding error!
return false;
}
return mb_substr($plaintext, 0, $len - $pad, '8bit');
}
}
And here is the version written using OpenSSL:
/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeOpensslAES
{
const METHOD = 'aes-256-cbc';
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = openssl_random_pseudo_bytes($ivsize);
$ciphertext = openssl_encrypt(
$message,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
return openssl_decrypt(
$ciphertext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
}
}
Source: If You're Typing the Word MCRYPT Into Your PHP Code, You're Doing It Wrong.
I am using this on PHP 7.2.x, it's working fine for me:
public function make_hash($userStr){
try{
/**
* Used and tested on PHP 7.2x, Salt has been removed manually, it is now added by PHP
*/
return password_hash($userStr, PASSWORD_BCRYPT);
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
and then authenticate the hash with the following function:
public function varify_user($userStr,$hash){
try{
if (password_verify($userStr, $hash)) {
return true;
}
else {
return false;
}
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
Example:
//create hash from user string
$user_password = $obj->make_hash2($user_key);
and to authenticate this hash use the following code:
if($obj->varify_user($key, $user_key)){
//this is correct, you can proceed with
}
That's all.
As pointed out, you should not be storing your users' passwords in a format that is decryptable. Reversable encryption provides an easy route for hackers to find out your users' passwords, which extends to putting your users' accounts at other sites at risk should they use the same password there.
PHP provides a pair of powerful functions for random-salted, one-way hash encryption — password_hash() and password_verify(). Because the hash is automatically random-salted, there is no way for hackers to utilize precompiled tables of password hashes to reverse-engineer the password. Set the PASSWORD_DEFAULT option and future versions of PHP will automatically use stronger algorithms to generate password hashes without you having to update your code.
I was able to translate my Crypto object
Get a copy of php with mcrypt to decrypt the old data. I went to http://php.net/get/php-7.1.12.tar.gz/from/a/mirror, compiled it, then added the ext/mcrypt extension (configure;make;make install). I think I had to add the extenstion=mcrypt.so line to the php.ini as well. A series of scripts to build intermediate versions of the data with all data unencrypted.
Build a public and private key for openssl
openssl genrsa -des3 -out pkey.pem 2048
(set a password)
openssl rsa -in pkey.pem -out pkey-pub.pem -outform PEM -pubout
To Encrypt (using public key) use openssl_seal. From what I've read, openssl_encrypt using an RSA key is limited to 11 bytes less than the key length (See http://php.net/manual/en/function.openssl-public-encrypt.php comment by Thomas Horsten)
$pubKey = openssl_get_publickey(file_get_contents('./pkey-pub.pem'));
openssl_seal($pwd, $sealed, $ekeys, [ $pubKey ]);
$encryptedPassword = base64_encode($sealed);
$key = base64_encode($ekeys[0]);
You could probably store the raw binary.
To Decrypt (using private key)
$passphrase="passphrase here";
$privKey = openssl_get_privatekey(file_get_contents('./pkey.pem'), $passphrase);
// I base64_decode() from my db columns
openssl_open($encryptedPassword, $plain, $key, $privKey);
echo "<h3>Password=$plain</h3>";
P.S. You can't encrypt the empty string ("")
P.P.S. This is for a password database not for user validation.
You should use openssl_encrypt() function.

What a robust and easy way to encrypt a string with key without using Open SSL?

I don't want to use Open SSL or any very big none-built-in libraries for this.
I only want to be able to encrypt a string with a key so you can't decrypt it without it.
By the way, i already did a long search on stackoverflow and i've checked out those unsecure ways but i want something robust like md5 (which can be decrypted with a key).
Thanks
Something like mcrypt is probably what you're looking for. It does two way encryption which hashing functions like MD5 do not do.
Try this PHP5 class for encryption using mcrypt. In this case it's using blowfish encryption. You'll want to change the key for each site you use it on. If you don't use it at least it may guide you on writing your own version of it.
<?php
class Encryption
{
const CYPHER = 'blowfish';
const MODE = 'cfb';
const KEY = '7QQvcT9Ga7R6QC3';
public function encrypt($plaintext)
{
$td = mcrypt_module_open(self::CYPHER, '', self::MODE, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, self::KEY, $iv);
$crypttext = mcrypt_generic($td, $plaintext);
mcrypt_generic_deinit($td);
return $iv.$crypttext;
}
public function decrypt($crypttext)
{
$plaintext = '';
$td = mcrypt_module_open(self::CYPHER, '', self::MODE, '');
$ivsize = mcrypt_enc_get_iv_size($td);
$iv = substr($crypttext, 0, $ivsize);
$crypttext = substr($crypttext, $ivsize);
if ($iv)
{
mcrypt_generic_init($td, self::KEY, $iv);
$plaintext = mdecrypt_generic($td, $crypttext);
}
return $plaintext;
}
}
?>
Usage:
$encrypted_string = Encryption::encrypt('this is a test'); // Åž-\Ž“kcþ1ÿ4gî:Xƒã%
$decrypted_string = Encryption::decrypt($encrypted_string); // this is a test
Most proposals will leave out two things key to security:
1: the nonce. Any secure mode of operation will take a nonce (or "IV") which has to be different for every encryption operation. This makes it harder for the attacker to exploit relationships between distinct encryption operations to learn about the plaintext: a trivial example is that the same plaintext encrypted twice with different nonces produces different ciphertexts.
2: the MAC. Unless you are a professional cryptographer, you should treat this as essential whenever you do encryption. This is a code that checks the integrity of the message, and ensures that only real ciphertext encrypted with that key will decrypt.
I strongly recommend using AES in either EAX or GCM mode; they have flexible nonce handling and manage both encryption and authentication in one package, with good solid proofs behind them.
simplest way is to encrypt string using XOR operator

Categories