I wish to encrypt a string in PHP in the same way as ColdFusion.
The ColdFusion code (key was generated using GenerateSecretKey('AES') ):
<cfset encryptedData = encrypt('text', 'CJsP3yDcEM9RIwme53rAUQ==')>
Which results in (the result below should have two spaces at the end):
$8X"%DP
The closest I could come up with (forgive me - I know very little of encryption):
$cipher = 'AES128'; // assumed the default algorithm in CF is AES so this made sense
$ivlen = openssl_cipher_iv_length($cipher); // These two lines are because the AES ciphers demand an init vector
$iv = openssl_random_pseudo_bytes($ivlen);
$key = 'CJsP3yDcEM9RIwme53rAUQ==';
$data = openssl_encrypt($data, $cipher, $key, $options=0, $iv);
echo $data;exit;
But this didn't work as the initialization vector will be different each time.
Related
I have an encrypted value, which I know has been encrypted via the following obsolete php function:
$encrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, trim($encryptedValue), MCRYPT_MODE_CBC, $iv);
I'm trying to decrypt this value using openssl_decrypt with this function :
$decryptedValue = openssl_decrypt("QTu07uBvWSJHmN7gqGIaJg==", 'aes-256-cbc', $key, $options = 0, $iv);
I know that the encryptedValue should return the value '1000' but the function don't work (return false)
What I did wrong ? Is the AES mode incorrect or something like that ?
I also tried this :
$encryptedValue = "QTu07uBvWSJHmN7gqGIaJg=="; // = "1000"
if (strlen($encryptedValue) % 8) {
$encryptedValue = str_pad($encryptedValue, strlen($encryptedValue) + 8 - strlen($encryptedValue) % 8, "\0");
}
$decryptedValue = openssl_decrypt($encryptedValue, 'aes-256-cbc', $key, $options = 0, $iv);
dd($decryptedValue);
But this function still return false with the dump.
I hope you've found a better solution in the months past, as this seems outdated, but for the sake of answering the question:
The correct cipher to use with OpenSSL depends on the keysize from your original code using mcrypt. Both AES-128 and AES-256 are variants of Rijndael-128, they just differ in key size. If you have a 128-bit (16-byte) key, then you have AES-128; if it's larger than that (and ideally exactly 256 bits), then you have AES-256.
Then, seeing that your cipherText is Base64-encoded, you need to either base64_decode() it before passing to openssl_decrypt() OR don't use OPENSSL_RAW_DATA - the only thing this flag does is to tell the function to not perform Base64 decoding itself.
And finally, yes, mcrypt will apply zero-padding, but that extra step you tried is just unnecessarily adding it again, just use OPENSSL_ZERO_PADDING while decrypting. So, you end up with something like this:
$cipher = (mb_strlen($key, '8bit') <= 8) ? 'aes-128-cbc' : 'aes-256-cbc';
$plainText = openssl_decrypt($encryptedValue, $cipher, $key, OPENSSL_ZERO_PADDING, $iv);
There are other possible variables, like the key also being encoded or not, the IV being prepended or appended to the cipherText already, etc, but with the information that you provided, this should be all you need to recover the data.
this is my php code in PHP7.0.30
$key = strtoupper(md5('799ae002c7e940ef8a890b3a428f8f458e3f7c39d1cc2bf24390f0c46cf932c8'));
$text ='name=王星星&mobile=15212345678&idNumber=620402198709215456&bankName=招商银行&bankNum=6214830100799652';
$plaintext = $text;
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
//PKCS5Padding
$padding = $size - strlen($plaintext) % $size;
$plaintext .= str_repeat(chr($padding), $padding);
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = str_repeat("\0", $size);
/* Intialize encryption */
mcrypt_generic_init($module, base64_decode($key), $iv);
/* Encrypt data */
$encrypted = mcrypt_generic($module, $plaintext);
/* Terminate encryption handler */
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
var_dump(base64_encode($encrypted));
/* openssl_encrypt */
$encrypted = openssl_encrypt($plaintext, 'AES-256-CBC',base64_decode($key), OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,$iv);
var_dump(base64_encode($encrypted));
mcrypt_generic output: 8LZZEXRhAfeeQOxF1iI9GpBcA2hSCelrUq2OimhSgZly6RfRonzGiE32vHh/JkdK+X5N5hFBMKz+iOmWAbgL9BIu2GIAxBIXCOusxFU4eDJ/5uy7F9vR9EE5NqOAiHBZhTP3pzMtEc0fLAzg8Tsngg==
but openssl_encrypt output:
g/YBzu+SGy9jfR+DIVY2S0iGM2O0QEs+J3IEv7bNAoz7+3iX9FboJZT0h+OH6uUeQBoSsD+eAga69U5C86Ibcp5Q2ay1FzfDFG/eGBtUaAJxRBwhxiNeBxPw2jBar2fR42wZUZOGTjlT5Ujgz+s/Iw==
I don't know how to convert mcrypt_generic to openssl_encrypt , thanks!
You need a 256 bit key. Your current "key" is 32 bytes which is 256 bits.
You problem is that your are decoding the key. When you decode the key you are reducing the key's size to 24 bytes or 192 bits.
So you have two options.
Increase the key size by another 8 bytes before you base64_encode() it.
OR
Just remove the base64_decode() functions.
Secondly, I hope you are using the md5 for testing purposes and not for your application. The md5 hash is not suited to give you a hash that is designed for modern cryptography.
You should use something like $key = openssl_random_pseudo_bytes($size); to generate a key with.
I would also like to point you in the direction of the Libsodium library. It is now native on the latest version of PHP.
UPDATED
If your goal is to have AES encryption with a 256 bit key like your code suggest, then you will have to do what I stated above. If that is not one of your requirements then the only thing you have to do is change the AES-256-CBC to AES-192-CBC in the openssl_encrypt() function like so.
$encrypted = openssl_encrypt($plaintext, 'AES-192-CBC', base64_decode($key), OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
One thing to point out is that the "128" in the name MCRYPT_RIJNDAEL_128 refers to the block size not the key size, where as the "256" in AES-256-CBC actually refers to the key size. Which is why the AES-192-CBC will work for your current key size of 192 bits. AES encryption uses a standard of a 128 bit block size, so that is compatible the MCRYPT_RIJNDAEL_128 block size.
Cheers
I have a php file which is as follow:
$encryption_encoded_key = 'c7e1wJFz+PBwQix80D1MbIwwOmOceZOzFGoidzDkF5g=';
function my_encrypt($data, $key) {
$encryption_key = base64_decode($key);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cfb'));
$encrypted = openssl_encrypt($data, 'aes-256-cfb', $encryption_key, 1, $iv);
// The $iv is just as important as the key for decrypting, so save it with encrypted data using a unique separator (::)
return base64_encode($encrypted . '::' . $iv);
}
function my_decrypt($data, $key) {
// Remove the base64 encoding from key
$encryption_key = base64_decode($key);
// To decrypt, split the encrypted data from IV - unique separator used was "::"
list($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cfb', $encryption_key, 1, $iv);
}
$data = 'USER_ID||NAME||EMAIL||MOBILE';
$data_encrypted = my_encrypt($data, $encryption_encoded_key);
echo $data_encrypted;
$data_decrypted = my_decrypt($data_encrypted, $encryption_encoded_key);
echo "Decrypted string: ". $data_decrypted;
This works fine, and is able to encrypt/decrypt with the encryption key, now i also have a python file:
import hashlib
import base64
from Crypto.Cipher import AES
from Crypto import Random
encryption_encoded_key = 'c7e1wJFz+PBwQix80D1MbIwwOmOceZOzFGoidzDkF5g='
def my_encrypt(data, key):
#Remove the base64 encoding from key
encryption_key = base64.b64decode(key)
#Generate an initialization vector
bs = AES.block_size
iv = Random.new().read(bs)
cipher = AES.new(encryption_key, AES.MODE_CFB, iv)
#Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
encrypted = cipher.encrypt(data)
#The iv is just as important as the key for decrypting, so save it with encrypted data using a unique separator (::)
return base64.b64encode(encrypted + '::' + iv)
def my_decrypt(data, key):
#Remove the base64 encoding from key
encryption_key = base64.b64decode(key)
#To decrypt, split the encrypted data from IV - unique separator used was "::"
encrypted_data, iv = base64.b64decode(data).split('::')
cipher = AES.new(encryption_key, AES.MODE_CFB, iv)
return cipher.decrypt(encrypted_data)
data = 'USER_ID||NAME||EMAIL||MOBILE'
print "Actual string: %s" %(data)
data_encrypted = my_encrypt(data, encryption_encoded_key)
print data_encrypted
data_decrypted = my_decrypt(data_encrypted, encryption_encoded_key)
print "Decrypted string: %s" %(data_decrypted)
This also works fine when i try to use this from python, it is able to encrypt/decrypt input string,
I want to encrypt using php file and decrypt the output in python, both should use AES 256 Encryption using CFB mode, what am i doing wrong ?
To use CFB mode you need to specify a segment size for it. OpenSSL has aes-256-cfb (which is 128 bit), aes-256-cfb1 (i.e. 1-bit) and aes-256-cfb8 (8 bit) (and similar modes for AES-128 and 192). So you are using 128 bit cfb in your php code.
The Python library accepts a segment_size argument to AES.new, but the default is 8, so you are using different modes in the two versions.
To get the Python code to decrypt the output of the PHP code, add a segment size of 128 to the cipher object:
cipher = AES.new(encryption_key, AES.MODE_CFB, iv, segment_size=128)
(N.B. this is using the newer PyCryptodome fork of PyCrypto. PyCrypto has a bug here and won’t work.)
Alternatively, you can get the PHP code to use CFB-8 by setting the cipher (don’t change both, obviously):
$encrypted = openssl_encrypt($data, 'aes-256-cfb8', $encryption_key, 1, $iv);
I've to implement an aes256 encryption in PHP with the same key an iv from an existing .NET application to encrypt passwords identical (same ciphertext). I've to use the same IV (16 Byte) and Key (32 Byte) as the .NET Application. My problem is, that I don't understand how I've to convert the key and IV from .NET Application to php strings (the php mycrypt_encrypt function, which I'd like to use for the php aes encryption, need IV and key as strings parameters), because php doesn't have byte arrays. For example the IV and key are following .NET arrays
byte[] iv = {128,44,74,135,31,23,0,133,22,13,118,17,187,113,33,111};
byte[] key = {100,123,214,125,109,19,10,229,118,31,44,157,36,10,0,103,15,16,101,126,0,122,122,86,119,29,140,213,27,129,119,50};
Now I'd like to use these key and iv to encrypt in PHP an password with
mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $paddedPlainText, MCRYPT_MODE_CBC, $iv).
How I've the convert the byte arrays from .NET to strings so that I can use them in the PHP mcrypt_encrypt function?
I tried a base64 encode for the .NET Byte Arrays ( Convert.ToBase64String(key), Convert.ToBase64String(iv)).The following PHP I already wrote:
function pkcs7pad($plaintext, $blocksize = 16)
{
$padsize = $blocksize - (strlen($plaintext) % $blocksize);
return $plaintext . str_repeat(chr($padsize), $padsize);
}
public function testPW()
{
$key = base64_decode('[HERE IS MY KEY base64 encoded]');
$iv = base64_decode('[HERE IS MY IV base64 encoded]');
$plaintext = 'test1234';
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $this->pkcs7pad($plaintext), MCRYPT_MODE_CBC, $iv);
return base64_encode($ciphertext);
}
But the returned cipher of testPW isn't the same as the one of the .NET Application. The returned Cipher has also a smaller length than the .NET one.
I'm working on a cross language project wrapping a ruby/Sinatra API in PHP to be consumed by another team. None of the information exposed by the API is sensitive, but we would prefer it not be easily accessible to a casual observer guessing the URL.
private function generateSliceIDToken($key){
$currentEpoch = time();
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);
$encryptedBytes = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
$currentEpoch.**Passcode**,
MCRYPT_MODE_CBC, $iv
);
$ivAndEncryptedBytes = $iv . $encryptedBytes;
return urlencode(urlencode(base64_encode($ivAndEncryptedBytes)));
The code above Encrypts a password and time stamp using mcrypt's RIJNDAEL implementation and encodes it to send off to the ruby API
if identifier.validate_token Base64.decode64(URI.unescape( URI.unescape(params[:token])))
Sinatra grabs it and decodes it
def validate_token(token)
cipher = OpenSSL::Cipher::AES.new(128, 'CBC')
cipher.decrypt
cipher.key = **key**
cipher.iv = token[0,16]
plain = cipher.update(token[16..-1]) + cipher.final
return plain[10,8] == **Passcode**
end
and passes it along to be decrypted
The problem is, the decryption fails with a 'Bad Decrypt' Error
I was lead to believe Mcrypt's RIJNDAEL and Cipher's AES were compatible, but is this assumption incorrect? Any help I can get one this would be most helpful.
I was lead to believe Mcrypt's RIJNDAEL and Cipher's AES were compatible, but is this assumption incorrect?
You need to slightly tweak data being encoded to make it AES compatible. Data must be right padded, with character and amount depending of its current width:
$encode = $currentEpoch.'**Passcode**';
$len = strlen($encode);
$pad = 16 - ($len % 16);
$encode .= str_repeat(chr($pad), $pad);
Also remember to have $key exactly 16 characters long. If it is shorter, ruby throws CipherError, while php pads key with null bytes. If it is longer, ruby uses only first 16 character but php pads it again, and uses last 16 characters.