How do I setup my IV in codeigniter 3 encryption library?
I have encryption code below which was running smoothly in Codeigniter 2 with PHP 5.6,
function encrypt($data, $secret)
{
//Generate a key from a hash
$key = md5(utf8_encode($secret), true);
$data2 = utf8_encode($data);
$iv = utf8_encode("jvz8bUAx");
//Take first 8 bytes of $key and append them to the end of $key.
$key .= substr($key, 0, 8);
//Pad for PKCS7
$blockSize = mcrypt_get_block_size('tripledes', 'cbc');
//Encrypt data
$encData = mcrypt_encrypt('tripledes', $key, $data2, MCRYPT_MODE_CBC, $iv);
return urlencode(base64_encode($encData));
}
When I upgraded to CI 3 with PHP 7.1 mcrypt was deprecated already. So, I wanted to recreate the function in CI 3 using encryption library, but I cannot get the correct encrypted strings.
$this->load->library('encryption');
$key = md5(utf8_encode($secret), true);
$key .= substr($key, 0, 8);
$iv = utf8_encode("jvz8bUAx");
$amount = 1100;
$json = array(
'Amount' => $amount
);
$data = json_encode($json);
$params = array(
'driver' => 'mcrypt',
'cipher' => 'tripledes',
'mode' => 'cbc',
'key' => $key,
'hmac' => false
);
$ciphertext = $this->encryption->encrypt($data, $params);
$ciphertext = urlencode(base64_encode($ciphertext));
In the CI How it is works
Generate a random initialization vector (IV).
The library generates the IV for you and then prepends it for you to the resulting ciphertext. During the decryption, the IV is extracted from the ciphertext.
Since, by default, you cannot control the IV, the ciphertext will be different. If you want to really decrypt the old library encryption with the new one you have to prepend the IV as in the CI 3.
Related
I was using "mcrypt-*" for decoding the response in previous PHP 5.6 version but now in PHP 7.2 version as it is deprecated I am using openSSL method. But it is not working properly hopefully I am missing something.
$value="###lllljG5ZOibDGtlL gcQLAtTQUnCJ/bE2glWsL1WKVPdC22c9GtGe/Npx9Uv9IYaszOAVXB4T9s7Hsss/2XpZ9oisx5M4jeV7RK2S/JrBt2E4GEcDGwuJs6NhkKV8hdOcU tmkJLxO3OJ OgVbqrT6a4v5RE7w eP zvQwZyAR5cYCKUYomou9mL/pvfLbe RrBe5ZnMQmUrD6cwUxEE/inikMvIb4K7HI fVPid N B3iPnIYQna6/v9W5A0kslBj6BBDjVXJabwmCSDVxbArm0GDNseWoQAEa4BMxYitqP6cVTxL5Kri8xbAKCW5/unnYnudkHQjNJWW7LuiwDxsBqwQv8D/R/Ff/joFW6q0 muI16/CfIoFnYAyAJWNlKCX9";
$value = urldecode($value);
$value = str_replace(" ", "+", $value);
$abc = triple_decrypt($value);
print_r($abc);
PHP 5.6 working fine
function triple_decrypt($input){
$key = "thisis87658748639testkey";
$input = base64_decode($input);
$td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_ECB, "");
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
mcrypt_generic_init($td, $key, $iv);
$pwd = trim(mdecrypt_generic($td, $input), "\x00..\x0F");
mcrypt_generic_end($td);
return $pwd;
}
PHP 7.2
function triple_decrypt($input){
$key = "thisis87658748639testkey";
$cipher = "des-ede3";
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$pwd = openssl_decrypt($input, $cipher, $key, $options=0, $iv);
return $pwd;
}
openssl uses PKCS7-padding and mcrypt Zero-padding [0][1][2]. To decrypt the ciphertext with openssl, openssl's padding must be disabled and mcrypt's Zero-padding bytes must be removed:
function triple_decrypt($input){
$key = "thisis87658748639testkey";
$cipher = "des-ede3";
$decrypted = openssl_decrypt($input, $cipher, $key, $options=OPENSSL_ZERO_PADDING); // Disable openssl's PKCS7-padding
$unpadded = trim($decrypted, "\x00..\x0F"); // Remove mcrypt's Zero-padding bytes
return $unpadded;
}
However, note the following with regard to a reimplementation of encryption and decryption: ECB is an insecure mode [3]. Instead, CBC or even better GCM should be used [4][5]. Instead of Triple-DES the modern and faster todays standard AES is recommended [6]. Zero-padding is unreliable, PKCS7-padding should be applied instead.
Furthermore, the mcrypt code is to some extent inconsistent:
The ECB mode doesn't use an IV (this is also the reason why openssl_cipher_iv_length returns 0 in the openssl code [7]). mcrypt_generic_init ignores the IV in case of the ECB mode [8], so it's not used in the mcrypt code and therefore not needed in the openssl code.
And if a mode would be used that requires an IV, then the following would have to be considered: The IV is always needed for encryption and decryption. Therefore, a random IV is generated (and used) during encryption and then passed on to the recipient together with the ciphertext, where it's used for decryption. Since the IV isn't secret, it's usually prefixed to the ciphertext. The generation of a random IV during decryption therefore makes no sense.
You can do using openssl()
function encryptIt($q) {
$cryptKey = 'YourProjectname'; //any string
$encryptionMethod = "AES-256-CBC";
$secretHash = "25c6c7rr35b9979b151f0205cd13b0vv"; // any hash
//To encrypt
$qEncoded = openssl_encrypt($q, $encryptionMethod, $secretHash);
return $qEncoded;
}
function decryptIt($q) {
$cryptKey = 'YourProjectname'; //any string
$encryptionMethod = "AES-256-CBC";
$secretHash = "25c6c7rr35b9979b151f0205cd13b0vv"; // any hash
//To Decrypt
$qDecoded = openssl_decrypt($q, $encryptionMethod, $secretHash);
return $qDecoded;
}
$encryptedstring = encryptIt('TEST');
echo "<br/>";
echo decryptIt($encryptedstring);
I have given a task to integrate our company system to external system via API. When I read about the API, it stated that the data should be encrypted using MD5 and Triple Des Encryption, so I made some research about how to do it.
// Here's the credentials to be used for encryption.
$key = 'dEvu4MHkqz7mRgeqmB1mQEXi';
$iv = "avz9bUNx";
The data should be formatted as JSON String before encryption.
$params = array(
'Number' => '+11177109886' // example
);
$text = json_encode($params);
After that I made some codes to encrypt it.
function apiEncode($text, $key, $iv)
{
// to append string with trailing characters as for PKCS7 padding scheme
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$padding = $block - (strlen($text) % $block);
$text .= str_repeat(chr($padding), $padding);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv);
return base64_encode($crypttext);
}
The error I got is.
Warning: mcrypt_encrypt(): Received initialization vector of size 0,
but size 32 is required for this encryption mode in
/Applications/XAMPP/xamppfiles/htdocs/dapsapi/rdremit.php on line 20
I have updated my php version to 7.1.
I had functions where i encrypt data using mcrypt.
Now this function is deprecated.
How can i decrypt the data anyway withoud going back to older versions of php.
This is the code i used:
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");
}
With Constants:
const CIPHER = MCRYPT_RIJNDAEL_128; // Rijndael-128 is AES
const MODE = MCRYPT_MODE_CBC;
I saw that it was recommended to use OpenSSL. That is what i will use from now on. But how can i decrypt the older data using this method?
Thanks
Edit:
I know i can use OpenSSL as alternative.
Thats what i am doing for the content from now on.
But i need to decrypt my mcrypted code from my old contents.
*Edit request #symcbean
Tried to decrypt with OpenSSL like this:
public function decrypt($ciphertext) {
$ciphertext = base64_decode($ciphertext);
if (!function_exists("openssl_decrypt")) {
throw new Exception("aesDecrypt needs openssl php module.");
}
$key = $this->key;
$method = 'AES-256-CBC';
$ivSize = openssl_cipher_iv_length($method);
$iv = substr($ciphertext,0,$ivSize);
$data = substr($ciphertext,$ivSize);
$clear = openssl_decrypt ($data, $method, $key, 'OPENSSL_RAW_DATA'|'OPENSSL_ZERO_PADDING', $iv);
return $clear;
}
Important thing to note is that mcrypt_encrypt zero-pads input data if it's not a multiple of the blocksize. This leads to ambiguous results if the data itself has trailing zeroes.
openssl_decrypt doesn't remove the zero-padding automatically, so you're left only with the possibility of trimming the trailing nulls.
Here's a trivial example:
$data = "Lorem ipsum";
$key = "1234567890abcdef";
$iv = "1234567890abcdef";
$encrypted = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
echo bin2hex($encrypted) . "\n";
$decrypted = openssl_decrypt(
$encrypted, "AES-128-CBC", $key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
echo var_export($decrypted, true) . "\n";
$result = rtrim($decrypted, "\0");
echo var_export($result, true) . "\n";
Output:
70168f2d5751b3d3bf36b7e6b8ec5843
'Lorem ipsum' . "\0" . '' . "\0" . '' . "\0" . '' . "\0" . '' . "\0" . ''
'Lorem ipsum'
I solved it.
Don't know if its the right way (guess not)
But connected remotely on a server with a lower php version.
Decrypted all the content and encrypted with OpenSSL.
Thanks for the suggestions!
I also had some problems decrypting data encrypted with mcrypt_encrypt with openssl_decrypt. The following small test encrypts a string with mcrypt and openssl (with added zero padding and without) and decrypts all strings with both methods. This example uses ECB mode but you can easily change this to CBC by adding an IV if needed.
// Setup key and test data
$key = hash("sha256", 'test', true);
$data = 'Hello World';
$enc = $dec = [];
// Encrypt with MCRYPT_RIJNDAEL_128 method
$enc['RIJ'] = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_ECB));
// Encrypt with OpenSSL equivalent AES-256
$enc['AES'] = base64_encode(openssl_encrypt($data, 'aes-256-ecb', $key, OPENSSL_RAW_DATA));
// Encrypt with OpenSSL equivalent AES-256 and added zero padding
if (strlen($data) % 8) $data = str_pad($data, strlen($data) + 8 - strlen($data) % 8, "\0");
$enc['AES0'] = base64_encode(openssl_encrypt($data, 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING));
// Decrypt all strings with MCRYPT_RIJNDAEL_128
$dec['mRIJ'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['RIJ']), MCRYPT_MODE_ECB));
$dec['mAES'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['AES']), MCRYPT_MODE_ECB));
$dec['mAES0'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['AES0']), MCRYPT_MODE_ECB));
// Decrypt all strings with OpenSSL equivalent AES-256
$dec['oRIJ'] = bin2hex(openssl_decrypt(base64_decode($enc['RIJ']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING));
$dec['oAES'] = bin2hex(openssl_decrypt(base64_decode($enc['AES']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA));
$dec['oAES0'] = bin2hex(openssl_decrypt(base64_decode($enc['AES0']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING));
// Print results
print_r($enc);
var_dump($dec);
The print_r and var_dump output is the following:
Array
(
[RIJ] => YcvcTwAMLUMBCZXu5XqoEw==
[AES] => +AXMBwkWlgM1YDieGgekSg==
[AES0] => YcvcTwAMLUMBCZXu5XqoEw==
)
array(6) {
["mRIJ"]=>
string(32) "48656c6c6f20576f726c640000000000"
["mAES"]=>
string(32) "48656c6c6f20576f726c640505050505"
["mAES0"]=>
string(32) "48656c6c6f20576f726c640000000000"
["oRIJ"]=>
string(32) "48656c6c6f20576f726c640000000000"
["oAES"]=>
string(22) "48656c6c6f20576f726c64"
["oAES0"]=>
string(32) "48656c6c6f20576f726c640000000000"
}
If you need the same encrypted string with the openssl methods as you had with mcrypt, you'll have add the zero padding manually to the string (AES0 in the example). This way you'll get the exact same encrypted and decrypted strings as before. For some additional information about the zero padding, you should look at Joe's answer here: php: mcrypt_encrypt to openssl_encrypt, and OPENSSL_ZERO_PADDING problems
If you don't want to manually add the zero padding to all new messages, you'll need different flags for decrypting the old mcrypt-encrypted messages and the new messages encrypted with openssl. For the old messages you'll have to use the OPENSSL_ZERO_PADDING flag ($dec['oRIJ'] in the example), whereas you must not use it for the openssl encrypted messages ($dec['oAES'] in the example). In my case I used this approach, because the default behaviour of openssl seems more correct to me as the mcrypt one - if you encrypt a string with 11 bytes you get a string with 11 bytes back after decrypting it. As you can see in the example, this is not the case with mcrypt or with openssl and the added zero padding. In these cases you would have to remove the trailing zeros manually to get the original data back.
I am doing AES encryption on ios End, and i the base64 encode that string and send over to php end. On the php end, i have following code:
<?php
$key = 'a16byteslongkey!';
$data = base64_decode('LsCH4nvvGPKN67v94Ig9BweQgOk9rtDdK7ZugeJkTS8=');
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = substr($data, 0, $iv_size);
function aes256_cbc_decrypt($key, $data, $iv) {
if(32 !== strlen($key)) $key = hash('SHA256', $key, true);
if(16 !== strlen($iv)) $iv = hash('MD5', $iv, true);
$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_ECB, $iv);
$padding = ord($data[strlen($data) - 1]);
return substr($data, 0, -$padding);
}
$result = aes256_cbc_decrypt($key,$data,$iv);
var_dump($result);
?>
But when i run this code i get this "anil.mnd#gmail.cA���u�" . I should have got anil.mnd#gmail.com. I get only first 16 characters correct.
I am new to encryption so not have much idea what is wrong.
I am new to encryption so not have much idea what is wrong.
If you're new and want something that "just works", use defuse/php-encryption instead of trying to write it yourself.
If you're up for the challenge, however, keep reading:
Your code is unreadable. Let's add some whitespace.
$key = 'a16byteslongkey!';
$data = base64_decode('LsCH4nvvGPKN67v94Ig9BweQgOk9rtDdK7ZugeJkTS8=');
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = substr($data, 0, $iv_size);
function aes256_cbc_decrypt($key, $data, $iv)
{
if (32 !== strlen($key)) {
$key = hash('SHA256', $key, true);
}
if (16 !== strlen($iv)) {
$iv = hash('MD5', $iv, true);
}
$data = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$data,
MCRYPT_MODE_ECB,
$iv
);
$padding = ord($data[strlen($data) - 1]);
return substr($data, 0, -$padding);
}
$result = aes256_cbc_decrypt($key,$data,$iv);
var_dump($result);
Specific problems:
You're using MCRYPT_MODE_ECB for a function named aes256_cbc (have you seen the penguin?)
When I switch that out, I get invalid data.
Your encryption method is also probably broken, since changing your IV to "\x00\x00"... makes it decrypt.
Specific recommendations:
Please, please, PLEASE consider using well-studied cryptography code instead of writing it yourself.
strlen() and substr() are brittle. See: function overloading.
Use a real key derivation function, not a hash function.
Your IV (and keys, for that matter) should be generated from a cryptographically secure pseudo-random number generator, such as random_bytes().
Use authenticated encryption.
I have some encryption code that has been written in Perl (also a code snippet in PHP) - but I just can't get a version written in VB.NET to work with the third party.
Example in Perl
package Sitemaker::API::Crypt;
use warnings;
use strict;
use base qw( Exporter );
use Crypt::CBC;
use MIME::Base64;
our #EXPORT = qw(
encrypt
decrypt
);
=head2 encrypt( $key, $iv, $arg1[,$arg2][,...] )
This encrypts the argumenst (which must all be values, not references) with
the key (which must be 16 characters long)
=cut
sub encrypt {
my ( $key, $iv, #args ) = #_;
my $args = join( '|', #args );
my $cipher = Crypt::CBC->new(
-cipher => 'Rijndael',
-blocksize => 16,
-keysize => 16,
-header => 'none',
-padding => 'standard',
-iv => $iv,
-literal_key => 1,
-key => $key,
);
my $binary_token = $cipher->encrypt( $args );
return encode_base64( $iv . $binary_token );
}
1;
Example in PHP
/**
* #param string $plaintext
* #return string encrypted token
*
* Relies on the Mcrypt module
*/
function encrypt( $plaintext, $apiKey)
{
// Initialize mcrypt module (AES, 128-bit key, CBC)
$handle = mcrypt_module_open( MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = mcrypt_create_iv( mcrypt_enc_get_iv_size( $handle ), MCRYPT_DEV_URANDOM );
// PKCS#5 padding
$blocksize = mcrypt_enc_get_block_size( $handle );
$padsize = $blocksize - ( strlen( $plaintext ) % $blocksize );
$plaintext .= str_repeat( chr( $padsize ), $padsize );
// encrypt
mcrypt_generic_init( $handle, $apiKey, $iv );
$ciphertext = mcrypt_generic( $handle, $plaintext );
// clean up
mcrypt_generic_deinit( $handle );
mcrypt_module_close( $handle );
return base64_encode( $iv. $ciphertext );
}
So I tried to recreate the same in VB.NET, but I don't think it's working, as I post to the service and just get errors back. The encryption method in VB.NET is...
Public Function EncrpytIt(ByVal Key As String, ByVal IV As String, ByVal arrParams As String()) As String
Dim plainTextBytes As Byte()
plainTextBytes = Encoding.ASCII.GetBytes(Join(arrParams, "~"))
Dim outputBytes As Byte()
Dim symmetricKey As New System.Security.Cryptography.RijndaelManaged()
With symmetricKey
.Key = Encoding.ASCII.GetBytes(Key)
.IV = Encoding.ASCII.GetBytes(IV)
.Mode = CipherMode.CBC
.BlockSize = 128
.KeySize = 128
.Padding = PaddingMode.PKCS7
End With
Dim encryptor As ICryptoTransform = symmetricKey.CreateEncryptor(symmetricKey.Key, symmetricKey.IV)
Using msEncrypt As New MemoryStream()
Using csEncrypt As New CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)
csEncrypt.Write(plainTextBytes, 0, plainTextBytes.Length)
csEncrypt.FlushFinalBlock()
End Using
outputBytes = msEncrypt.ToArray
End Using
Return IV & Convert.ToBase64String(outputBytes)
End Function
Is that behaving in the same way, or do I have to alter the settings in the vb.net code?
The first thing that I see is that in the cases of PHP and Perl, you base 64 encode the concatenated IV string with the encrypted data, whereas in VB you return the concatenated string of the IV and the base 64 encoded output of the encryption.
You need to change the return statement to this:
Return Convert.ToBase64String(Encoding.ASCII.GetBytes(IV) & outputBytes)
Also, there seems to be a bit of inconsistency between the padding schemes used - at least between the PHP and the VB.NET version. Based on the comments in the PHP sample - that is using PKCS #5 and your Visual Basic code is using PKCS #7. I'm not sure what 'standard' means in the context of the Perl.