Is there some way to generate different output for same given string, here is example:
echo md5('test');
That always generates same fb469d7ef430b0baf0cab6c436e70375 for the given input. How do I generate different encrypted text each time and be able to decrypt it later if needed ?
I have seen functions such as md5, base64_encode, crypt, sha1, etc but they generate same output and secondly I cannot decrypt later if needed.
P.S: I know I can go with one way encryption and compare encrypted texts but for a particular scenario, I have requirement to be able to decrypt text completely if needed later however I am not able to figure out if there is some way or function in php for it.
Any help will be greatly appreciated. Thanks
To encrypt the same plaintext so that it generates different ciphertext you change the key (and/or Initialization Vector (IV) depending on the mode of the algorithm, like CBC).
Example:
$string = 'Some Secret thing I want to encrypt';
$iv = '12345678';
$passphrase = '8chrsLng';
$encryptedString = encryptString($string, $passphrase, $iv);
// Expect: 7DjnpOXG+FrUaOuc8x6vyrkk3atSiAf425ly5KpG7lOYgwouw2UATw==
function encryptString($unencryptedText, $passphrase, $iv) {
$enc = mcrypt_encrypt(MCRYPT_BLOWFISH, $passphrase, $unencryptedText, MCRYPT_MODE_CBC, $iv);
return base64_encode($enc);
}
Both the same IV and the passphrase must be used when decrypting in CBC mode. The passphrase MUST be kept a secret (from eavesdroppers) while the IV can be transmitted in the clear.
You CAN (but should not) use the same passphrase for every message/data but you should ALWAYS change the IV for each message/data.
This is the basics of encryption but depending on you needs you may need to modify your architecture to keep the system secure.
md5 is a hash method, not an encryption.
in short. there is no "good" way back from md5.
base64_encode and base64_decode and be used to transport messages, but it is no decryption.
please google on the topic RSA, ROT-13 or basic encryption with php.
I have created this class (Thanks to #Sani Huttunen for the idea) for the purpose. It allows to have differ text generated each time even for same input text and decodes it successfully as well.
class Encoder
{
private static $prefix = '#!#';
public static function php_aes_encrypt($text, $key)
{
if (!trim($text)) {
return '';
}
$iv = self::generateRandomString();
$key = self::mysql_aes_key($key);
$pad_value = 16 - (strlen($text) % 16);
$text = str_pad($text, (16 * (floor(strlen($text) / 16) + 1)), chr($pad_value));
$ciphertext = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
$text,
MCRYPT_MODE_CBC,
$iv
);
$ciphertext = self::getPrefix() . base64_encode($ciphertext . $iv);
return $ciphertext;
}
public static function php_aes_decrypt($text, $key)
{
$text = str_replace(self::getPrefix(), '', $text);
$text = base64_decode($text);
if (!trim($text)) {
return '';
}
$iv = substr($text, -16);
$text = str_replace($iv, '', $text);
$key = self::mysql_aes_key($key);
$text = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$text,
MCRYPT_MODE_CBC,
$iv
);
return rtrim($text, "\0..\16");
}
private static 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;
}
private static function getPrefix()
{
return base64_encode(self::$prefix);
}
public static function isEncrypted($ciphertext)
{
$isEncrypted = (false !== strpos($ciphertext, self::getPrefix()));
return $isEncrypted;
}
private static function generateRandomString()
{
return substr(sha1(rand()), 0, 16);
}
}
Usage:
$encrypted = Encoder::php_aes_encrypt('my test string', 'key');
echo $encrypted . '<br>';
echo Encoder::php_aes_decrypt($encrypted, 'key');
Related
I found out that I should not use global variables like global $auth_key for sensitive data's (Correct me if that's not true.) so I wanted to use defined variables for storing security keys.
Inside config.php salt keys are defined.
define('AUTH_KEY','::~K~UC*[tlu4Eq/]Lm|h');
define('SECURE_AUTH_KEY', 'QsTMvbV+tuU{K26!]J2');
In encryption.php contains the encryption functions where AUTH_KEY and SECURE_AUTH_KEY will be used inside.
function encrypt_text($value) {
if(!$value) return false;
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, **AUTH_KEY_HERE**, $value, MCRYPT_MODE_ECB, **SECURE_AUTH_KEY_HERE**);
return trim(base64_encode($crypttext));
}
function decrypt_text($value) {
if(!$value) return false;
$crypttext = base64_decode($value);
$decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, **AUTH_KEY_HERE**, $crypttext, MCRYPT_MODE_ECB, **SECURE_AUTH_KEY_HERE**);
return trim($decrypttext);
}
Is there a way to do that? or any other solutions you can recommend? Please note that these keys are real important for encryption of sensitive informations.
Also, a another question, what is the maximum length of keys to be used on mcrypt?
Thank you and looking forward for reply of yours.
as a rule: the logner the key, the stonger the encryption. Secondly, don't use ECB unless your data is very short, you ought to use CBC or something stronger. Third: use a salt or initialization vector. Lastly read this: https://www.owasp.org/index.php/Cryptographic_Storage_Cheat_Sheet
Using a constant is just like using a variable except there is no dollar sign.
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, AUTH_KEY, $value, MCRYPT_MODE_ECB, SECURE_AUTH);
There is nothing inherently more secure in this approach over using the global key word. Though this approach is preferred. By using a constant you are saying this is a static value I will use across the application. Having to use global on the other hand is often just a result of bad design or laziness. It leads to code that is hard to follow, abusing what scoping tries to accomplish.
Key length is dependent on the encryption algorithm used. RTM.
Yes you can use the define variable like you are doing, see the example
define('AUTH_KEY','::~K~UC*[tlu4Eq/]Lm|h');
function abc()
{
echo AUTH_KEY;
}
abc(); // ::~K~UC*[tlu4Eq/]Lm|h
http://codepad.viper-7.com/tUAg6D
Although choosing constants would be preferable over plain variables, this kind of information is better stored inside a configuration file rather than your code.
Also, for better reuse and avoid having those global values lying around it would be a better idea to encapsulate the functionality:
class MyCrypto
{
private $key;
private $cipher;
private $mode;
public function __construct($key, $cipher, $mode = "cbc")
{
$this->key = $key;
$this->cipher = $cipher;
$this->mode = $mode;
}
public function generate_salt()
{
return mcrypt_create_iv(
mcrypt_get_iv_size($this->cipher, $this->mode),
MCRYPT_DEV_URANDOM
);
}
public function encrypt($data) { ... }
public function decrypt($data) { ... }
}
I've added a salt generator function to be used for every encryption operation;
Lastly, I would recommend using CBC mode - MCRYPT_MODE_CBC.
Update (27/09/17):
Since mcrypt_encrypt is DEPRECATED as of PHP 7.1.0. Ive added a simple encrypt/decrypt using openssl.
function encrypt($string, $key = 'PrivateKey', $secret = 'SecretKey', $method = 'AES-256-CBC') {
// hash
$key = hash('sha256', $key);
// create iv - encrypt method AES-256-CBC expects 16 bytes
$iv = substr(hash('sha256', $secret), 0, 16);
// encrypt
$output = openssl_encrypt($string, $method, $key, 0, $iv);
// encode
return base64_encode($output);
}
function decrypt($string, $key = 'PrivateKey', $secret = 'SecretKey', $method = 'AES-256-CBC') {
// hash
$key = hash('sha256', $key);
// create iv - encrypt method AES-256-CBC expects 16 bytes
$iv = substr(hash('sha256', $secret), 0, 16);
// decode
$string = base64_decode($string);
// decrypt
return openssl_decrypt($string, $method, $key, 0, $iv);
}
$str = 'Encrypt this text';
echo "Plain: " .$str. "\n";
// encrypt
$encrypted_str = encrypt($str);
echo "Encrypted: " .$encrypted_str. "\n";
// decrypt
$decrypted_str = decrypt($encrypted_str);
echo "Decrypted: " .$decrypted_str. "\n";
In your example, you are using the same initialization vector **SECURE_AUTH_KEY_HERE** when you can allow PHP to create the iv for you this way you only need 1 SECURE_KEY defined.
<?php
define('SECURE_KEY',md5('your secret key'));
/**
* Encrypt a value
*/
function encrypt($str){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, SECURE_KEY, $str, MCRYPT_MODE_ECB, $iv);
}
/**
* Decrypt a value
*/
function decrypt($str){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, SECURE_KEY, $str, MCRYPT_MODE_ECB, $iv));
}
//32
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
//Create an initialization vector (IV) from a random source
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
echo decrypt(encrypt('Encrypt me'));
?>
Openfire stores encrypted passwords in a database using blowfish encryption.
http://svn.igniterealtime.org/svn/repos/openfire/trunk/src/java/org/jivesoftware/util/Blowfish.java is the java implementation for how encrypt / decrypt functions work in openfire.
My goal is to create new user entries in the database via PHP and MySQLI. All of the variations I've tried have yielded results that don't match what already exists in the database. For example:
d3f499857b40ac45c41828ccaa5ee1f90b19ca4e0560d1e2dcf4a305f219a4a2342aa7364e9950db is one of the encrypted passwords. clear text, this is stackoverflow
I've tried a few variations:
echo mcrypt_cbc(MCRYPT_BLOWFISH, '1uY40SR771HkdDG', 'stackoverflow', MCRYPT_ENCRYPT, '12345678');
// result: áë*sY¶nŸÉX_33ô
Another based on mcrypt blowfish php slightly different results when compared to java and .net
$key = '1uY40SR771HkdDG';
$pass = 'stackoverflow';
$blocksize = mcrypt_get_block_size('blowfish', 'cbc'); // get block size
$pkcs = $blocksize - (strlen($data) % $blocksize); // get pkcs5 pad length
$data.= str_repeat(chr($pkcs), $pkcs); // append pkcs5 padding to the data
// encrypt and encode
$res = base64_encode(mcrypt_cbc(MCRYPT_BLOWFISH,$key, $pass, MCRYPT_ENCRYPT));
echo $res;
// result: 3WXKASjk35sI1+XJ7htOGw==
Any clever ideas, or any glaring problems? I simply want to implement Blowfish.encryptString() as referenced in the first link in this question.
Here's a class I made, it encrypts and decrypts properly.
Note, you need to save / [pre/app]end the IV in order to reproduce results.
Some test vectors for the java code would be nice.
<?php
/**
* Emulate OpenFire Blowfish Class
*/
class OpenFireBlowfish
{
private $key;
private $cipher;
function __construct($pass)
{
$this->cipher = mcrypt_module_open('blowfish','','cbc','');
$this->key = pack('H*',sha1($pass));
}
function encryptString($plaintext, $iv = '')
{
if ($iv == '') {
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($this->cipher));
}
else {
$iv = pack("H*", $iv);
}
mcrypt_generic_init($this->cipher, $this->key, $iv);
$bs = mcrypt_enc_get_block_size($this->cipher); // get block size
$plaintext = mb_convert_encoding($plaintext,'UTF-16BE'); // set to 2 byte, network order
$pkcs = $bs - (strlen($plaintext) % $bs); // get pkcs5 pad length
$pkcs = str_repeat(chr($pkcs), $pkcs); // create padding string
$plaintext = $plaintext.$pkcs; // append pkcs5 padding to the data
$result = mcrypt_generic($this->cipher, $plaintext);
mcrypt_generic_deinit($this->cipher);
return $iv.$result;
}
function decryptString($ciphertext)
{
$bs = mcrypt_enc_get_block_size($this->cipher); // get block size
$iv_size = mcrypt_enc_get_iv_size($this->cipher);
if ((strlen($ciphertext) % $bs) != 0) { // check string is proper size
return false;
}
$iv = substr($ciphertext, 0, $iv_size); // retrieve IV
$ciphertext = substr($ciphertext, $iv_size);
mcrypt_generic_init($this->cipher, $this->key, $iv);
$result = mdecrypt_generic($this->cipher, $ciphertext); // decrypt
$padding = ord(substr($result,-1)); // retrieve padding
$result = substr($result,0,$padding * -1); // and remove it
mcrypt_generic_deinit($this->cipher);
return $result;
}
function __destruct()
{
mcrypt_module_close($this->cipher);
}
}
$enckey = "1uY40SR771HkdDG";
$enciv = 'd3f499857b40ac45';
$javastring = 'd3f499857b40ac45c41828ccaa5ee1f90b19ca4e0560d1e2dcf4a305f219a4a2342aa7364e9950db';
$a = new OpenFireBlowfish($enckey);
$encstring = bin2hex($a->encryptString('stackoverflow',$enciv));
echo $encstring . "\n";
echo $a->decryptString(pack("H*", $encstring)) . "\n";
$b = new OpenFireBlowfish($enckey);
echo $b->decryptString(pack("H*", $javastring)) . "\n";
There is nothing wrong with your code, however to generate the same code as Openfire, you will need to add in two other items before the encrypted text.
length of ciphertext
CBCIV (initialization variable)
Read "public String decryptString(String sCipherText)" in java code, it's all there. Also check the docs on how to use CBCIV in PHP.
Openfire's code prepends the CBCIV passed with the output string. It also using Unicode as the character set. These together may be the problem area.
I don't know enough about Blowfish's internals to help more, sorry.
I am coding a Drupal payment method module and within this I need to generate a hash to send to a bank. Bank asks me to code certain strings into the DES/ECB hash. They also provide test environment and here comes my problem. With the string B7DC02D5D6F2689E and key 7465737465703031 I should get result hash 3627C7356B25922B (after bin2hex, of course). This is by the bank test page and I have also checked this on this page: http://www.riscure.com/tech-corner/online-crypto-tools/des.html (encryption java applet).
My problem is that whatever I do I cant get my PHP code to provide the correct result. This is a simple function I am trying to use:
function encrypt($hash, $key)
{
$hash = strtoupper(substr(sha1($hash), 0, 16));
$key = strtoupper(bin2hex($key));
$block = mcrypt_get_block_size('des', 'ecb');
if (($pad = $block - (strlen($hash) % $block)) < $block) {
$hash .= str_repeat(chr($pad), $pad);
}
$sig = strtoupper(bin2hex(mcrypt_encrypt(MCRYPT_DES, $key, $hash, MCRYPT_MODE_ECB)));
return $sig;
}
and I have been trying sth like this as well:
function encrypt( $value, $key) {
$hash = strtoupper(substr(sha1($value), 0, 16));
$key = strtoupper(substr(bin2hex($key), 0, 16));
// encrypt hash with key
if (function_exists('mcrypt_module_open')) { // We have mcrypt 2.4.x
$td = mcrypt_module_open(MCRYPT_DES, "", MCRYPT_MODE_ECB, "");
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
mcrypt_generic_init($td, $key, $iv);
$signature = strtoupper(bin2hex(mcrypt_generic ($td, $hash)));
mcrypt_generic_end ($td);
}
else
{ // We have 2.2.x only
$signature = strtoupper(bin2hex(mcrypt_ecb (MCRYPT_3DES, $key, $hash, MCRYPT_ENCRYPT)));
}
return $signature;
}
None of these gave the correct signature. Any idea what's wrong? For now I am dealing with this issue more than 3 hrs, so I appreciate any help. I am not very familiar with this encryption stuff. Thanks a lot.
Btw.: Those $hash and $key mentioned above are after the strtoupper, substr and bin2hex functions at the beginning of my code snippets.
Simple solution:
function encrypt($hash, $key) {
return mcrypt_encrypt("des", pack("H*", $key), pack("H*", $hash), "ecb");
}
print bin2hex(encrypt("B7DC02D5D6F2689E", "7465737465703031"));
This prints 3627c7356b25922b for me, so it looks like that's working.
You were on the right track with bin2hex(), but that was converting in the wrong direction. (There's unfortunately no hex2bin() function, so you have to use pack() instead.)
You also don't need an IV for a single-block encryption like this.
You plaintext, B7DC02D5D6F2689E, is 8 bytes = 64 bits. This is an exact block for DES so you don't need any padding in ECB mode. I suggest that you remove the padding code entirely. All DEC-ECB needs in this case is the block to encrypt and the key; no padding and no IV.
I need to communicate with a asp platform that uses the aspEncrypt from persits.
Can anyone provide an example how to decode a string with PHP and mcrypt that was created via the aspEncrypt routines.
An example page of aspEncrypt is available at this link:
http://support.persits.com/encrypt/demo_text.asp
So if I use the text "Test" and the key "test" it provides an base64 encoded string. I need a php example that convert this encoded string back to the text "Test" with usage of key "test".
This is how i finally solved it:
Expectation:
Key is known
IV is known (in my case, first 32 characters of encoded data)
Encrypted Text is known
In my special case all received data hex encoded.
This means IV and encrypted text.
function decrypt($sString, $sIv, $sKey, $iCipherAlg) {
$sDecrypted = mcrypt_decrypt($iCipherAlg, $sKey, $sString, MCRYPT_MODE_CBC, $sIv);
return trim($sDecrypted);
}
function hex2bin($sData) {
$iLen = strlen($sData);
$sNewData = '';
for($iCount=0;$iCount<$iLen;$iCount+=2) {
$sNewData .= pack("C",hexdec(substr($sData,$iCount,2)));
}
return $sNewData;
}
$sKey = 'this is my key';
// first 32 chars are IV
$sIv = hex2bin(substr($sEncodedData, 0, 32));
$sEncodedData = substr($sEncodedData, 32);
$sEncodedRaw = hex2bin($sEncodedData);
$sDecrypted = decrypt($sEncodedRaw, $sIv, $sKey, MCRYPT_RIJNDAEL_128);
A corresponding encryption works like that:
$sIv = mcrypt_create_iv(mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
$sKey = 'this is my key';
$sContent = 'a lot of content';
$sEncrypted = bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $sKey, $sContent, MCRYPT_MODE_CBC, $sIv));
$sFullEncodedText = bin2hex($sIv) . $sEncrypted;
I encountered an old VBScript project which was encrypting strings with AspEncrypt like this:
Function EncryptString(data, base64Iv)
Set CM = Server.CreateObject("Persits.CryptoManager")
Set Context = CM.OpenContextEx("Microsoft Enhanced RSA and AES Cryptographic Provider", "", True)
Set Key = Context.GenerateKeyFromPassword("secret encryption password", calgSHA512, calgAES256)
Set IVblob = CM.CreateBlob
IVblob.Base64 = base64Iv
Key.SetIV IVblob
Set Blob = Key.EncryptText(data)
EncryptString = Blob.Base64 & ":" & base64Iv
End Function
Based on the arguments to GenerateKeyFromPassword, a binary key is created by hashing the password with SHA-512, and data is encrypted with the aes-256-cbc algorithm. The random Base64-encoded initialization vector is appended to the encrypted value after a colon.
This can be replicated in PHP using the OpenSSL extension:
class Aes256Cbc
{
private string $algo = 'aes-256-cbc';
private string $key;
private int $ivLen;
public function __construct(string $password)
{
$this->key = hash('sha512', $password, true);
$this->ivLen = openssl_cipher_iv_length($this->algo);
}
public function encrypt(string $data): string
{
$iv = random_bytes($this->ivLen);
$ciphertext = openssl_encrypt($data, $this->algo, $this->key, OPENSSL_RAW_DATA, $iv);
return base64_encode($ciphertext) . ':' . base64_encode($iv);
}
public function decrypt(string $encrypted): string
{
[$ctPart, $ivPart] = explode(':', $encrypted);
$iv = base64_decode($ivPart);
$ciphertext = base64_decode($ctPart);
return openssl_decrypt($ciphertext, $this->algo, $this->key, OPENSSL_RAW_DATA, $iv);
}
}
Example usage:
$aes = new Aes256Cbc("secret encryption password");
$decrypted = $aes->decrypt($someValue);
Note: if AspEncrypt was used without setting an initialization vector, the IV will be sequence of null bytes. This fixed IV could be generated in the above PHP class as follows:
$iv = str_repeat("\0", $this->ivLen);
It depends on which cipher it uses, take a look at mcrypt as long as you know the cipher and key it should be easy to decrypt.
If you know the cipher and mode used by the encryption, the function mcrypt_decrypt can decrypt it.
http://uk3.php.net/manual/en/function.mcrypt-decrypt.php
Trying to write a couple of functions that will encrypt or decrypt a file and am using the class found here to try and accomplish this:
http://www.itnewb.com/v/PHP-Encryption-Decryption-Using-the-MCrypt-Library-libmcrypt
The encryption function below seems to work, in that it appears to encrypt the file and place it in the intended directory. I'm trying to decrypt the file now, and it just dies with the message "Failed to complete decryption" (which is coded in there...) There's nothing in the php error logs, so I'm not sure why it's failing, but as mcrypt is entirely new to me, I'm more than inclined to believe I'm doing something wrong here...
Here are the functions:
//ENCRYPT FILE
function encryptFile() {
global $cryptastic;
$pass = PGPPASS;
$salt = PGPSALT;
$key = $cryptastic->pbkdf2($pass, $salt, 1000, 32) or die("Failed to generate secret key.");
if ($handle = opendir(PATH.'/ftpd')) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
$newfile = PATH.'/encrypted/'.$file.'.txt';
$msg = file_get_contents(PATH.'/ftpd/'.$file);
$encrypted = $cryptastic->encrypt($msg, $key) or die("Failed to complete encryption.");
$nfile = fopen($newfile, 'w');
fwrite($nfile, $encrypted);
fclose($nfile);
unlink(PATH.'/ftpd/'.$file);
}
}
closedir($handle);
}
//DECRYPT FILE
function inFTP() {
global $cryptastic;
$pass = PGPPASS;
$salt = PGPSALT;
$key = $cryptastic->pbkdf2($pass, $salt, 1000, 32) or die("Failed to generate secret key.");
if ($handle = opendir(PATH.'/encrypted')) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
$newfile = PATH.'/decrypted/'.$file;
$msg = PATH.'/encrypted/'.$file;
$decrypted = $cryptastic->decrypt($msg, $key) or die("Failed to complete decryption.");
$nfile = fopen($newfile, 'w');
fwrite($nfile, $decrypted);
fclose($nfile);
//unlink(PATH.'/encrypted/'.$file);
}
}
closedir($handle);
}
//$crypt->decrypt($file);
}
Since mcrypt is abandonware and no longer recommended to be used, here's an example using openssl.
class AES256Encryption
{
public const BLOCK_SIZE = 8;
public const IV_LENGTH = 16;
public const CIPHER = 'AES256';
public static function generateIv(bool $allowLessSecure = false): string
{
$success = false;
$random = openssl_random_pseudo_bytes(openssl_cipher_iv_length(static::CIPHER));
if (!$success) {
if (function_exists('sodium_randombytes_random16')) {
$random = sodium_randombytes_random16();
} else {
try {
$random = random_bytes(static::IV_LENGTH);
}
catch (Exception $e) {
if ($allowLessSecure) {
$permitted_chars = implode(
'',
array_merge(
range('A', 'z'),
range(0, 9),
str_split('~!##$%&*()-=+{};:"<>,.?/\'')
)
);
$random = '';
for ($i = 0; $i < static::IV_LENGTH; $i++) {
$random .= $permitted_chars[mt_rand(0, (static::IV_LENGTH) - 1)];
}
}
else {
throw new RuntimeException('Unable to generate initialization vector (IV)');
}
}
}
}
return $random;
}
protected static function getPaddedText(string $plainText): string
{
$stringLength = strlen($plainText);
if ($stringLength % static::BLOCK_SIZE) {
$plainText = str_pad($plainText, $stringLength + static::BLOCK_SIZE - $stringLength % static::BLOCK_SIZE, "\0");
}
return $plainText;
}
public static function encrypt(string $plainText, string $key, string $iv): string
{
$plainText = static::getPaddedText($plainText);
return base64_encode(openssl_encrypt($plainText, static::CIPHER, $key, OPENSSL_RAW_DATA, $iv));
}
public static function decrypt(string $encryptedText, string $key, string $iv): string
{
return openssl_decrypt(base64_decode($encryptedText), static::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
}
}
$text = '8SViI0Gz4r-p7A15YxkwjOBFuW*#NTtbm{U]D&E=~6yLM+adX'P;h3$,KJ%/eo>}<Rs:2#gZ.9fqn"Cv_^[(H\c!)?`Ql';
$key = 'secretkey';
$iv = AES256Encryption::generateIv();
$encryptedText = AES256Encryption::encrypt($text, $key, $iv);
$decryptedText = AES256Encryption::decrypt($encryptedText, $key, $iv);
printf('Original Text: %s%s', $text, PHP_EOL);
printf('Encrypted: %s%s', $encryptedText, PHP_EOL);
printf('Decrypted: %s%s', $decryptedText, PHP_EOL);
Output:
// Long string with lots of different characters
Original Text: 8SViI0Gz4r-p7A15YxkwjOBFuW*#NTtbm{U]D&E=~6yLM+adX'P;h3$,KJ%/eo>}<Rs:2#gZ.9fqn"Cv_^[(H\c!)?`Ql
Encrypted : rsiF4PMCMyvAp+CTuJrxJYGoV4BSy8Fy+q+FL8m64+Mt5V3o0HS0elRkWXsy+//hPjzNhjmVktxVvMY55Negt4DyLcf2QpH05wUX+adJDe634J/9fWd+nlEFoDutXuhY+/Kep9zUZFDmLmszJaBHWQ==
Decrypted : 8SViI0Gz4r-p7A15YxkwjOBFuW*#NTtbm{U]D&E=~6yLM+adX'P;h3$,KJ%/eo>}<Rs:2#gZ.9fqn"Cv_^[(H\c!)?`Ql
Old Answer
Try this PHP5 class for encryption using mcrypt. In this case it's using AES 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 CIPHER = MCRYPT_RIJNDAEL_128; // Rijndael-128 is AES
const MODE = MCRYPT_MODE_CBC;
/* Cryptographic key of length 16, 24 or 32. NOT a password! */
private $key;
public function __construct($key) {
$this->key = $key;
}
public function encrypt($plaintext) {
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
$ciphertext = mcrypt_encrypt(self::CIPHER, $this->key, $plaintext, self::MODE, $iv);
return base64_encode($iv.$ciphertext);
}
public function decrypt($ciphertext) {
$ciphertext = base64_decode($ciphertext);
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
if (strlen($ciphertext) < $ivSize) {
throw new Exception('Missing initialization vector');
}
$iv = substr($ciphertext, 0, $ivSize);
$ciphertext = substr($ciphertext, $ivSize);
$plaintext = mcrypt_decrypt(self::CIPHER, $this->key, $ciphertext, self::MODE, $iv);
return rtrim($plaintext, "\0");
}
}
Usage:
$key = /* CRYPTOGRAPHIC!!! key */;
$crypt = new Encryption($key);
$encrypted_string = $crypt->encrypt('this is a test');
$decrypted_string = $crypt->decrypt($encrypted_string); // this is a test
Notes:
This class is not safe for use with binary data (which may end in NUL bytes)
This class does not provide authenticated encryption.
While Johns answer is good, using base64 encoding just to fix the binary safety issue is overkill and will make your encrypted files 33% larger than the original. Here is my PHP Implementation of the AES Crypt file format which solves all the above issues transparently.
https://github.com/philios33/PHP-AES-File-Encryption
It is binary safe and includes authenticated encryption. Since it uses the open source aes crypt file format (.aes) it is fully compatible with other .aes software.
https://www.aescrypt.com/
The interface is pretty simple whether you are encrypting or decrypting. You just give it a source file and password.
You should not be using Mcrypt to encrypt/decrypt data. As shown in your question, and in the accepted answer, the data is not authenticated, which means it will fall victim to chosen ciphertext attacks.
Further, a great deal of effort has been done to make sure that developers put together cryptographic primitives correctly. As such, instead of Mcrypt, you should be using libsodium for your PHP projects. libsodium is a fork of NaCl. NaCl/libsodium is written to remove a lot of the cryptographic pitfalls that developers find themselves in, such as timing attacks with verification of MAC tags.
Mcrypt is deprecated in PHP 7.1, and libsodim is the preferred way to handle cryptography in PHP.
Using libsodium in your PHP project is easy, and secure. Scott Arciszewski has written an extensive ebook on using libsodium with PHP at https://paragonie.com/book/pecl-libsodium. It's worth the read for anyone doing PHP cryptography.
CakePHP has a pretty good implementation of rijndael. I'm not posting code directly here because not sure the legal ramifications.
Here are the api docs for the Security::rijndael() method.
If encoding a file, you will want to base64_encode() before calling this method with 'encrypt', and base64_decode() after calling this method with 'decrypt'