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.
Related
I am currently working on encryption and decryption. I have encrypted my api key using https://medium.com/#amitasaurus/encrypting-decrypting-a-string-with-aes-js-1d9efa4d66d7 like below
var api_key = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
var d = new Date();
var n = d.getTime();
var final_key = api_key+'/'+n;
var encrypted = CryptoJS.AES.encrypt('encryption', final_key);
var encrypted_key = encrypted.toString();
and passed the encrypted key to the server side. I used
<?php
$key = pack("H*", "0123456789abcdef0123456789abcdef");
$iv = pack("H*", "abcdef9876543210abcdef9876543210");
$encrypted = base64_decode('U2FsdGVkX19gHSzwsrc5H9K6rqDYr2E8oYoVNSp8INU=');
$decrypt_string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
echo $decrypt_string;
?>
for decrypting the encrypted string. When i print decrypted string , it is like this ���9Һ��دa<��5*| աT�;��놻��V�[�}��ID-�}��硵�
Any suggestions to print as decoded string?
mcryptdefaults to zero padding. That means that, no matter what kind of ciphertext and key combination you are using, that the unpadding will not fail. Instead, it just returns invalid, randomized plaintext.
CryptoJS by default uses OpenSSL key derivation from a given password. Your decryption will return randomized plaintext as long as you cannot mimic the final AES key value that is generated by CryptoJS.
Modern modes such as GCM include an authentication tag with the ciphertext so that the validity of the ciphertext / key combination is ensured, or a verification error will be generated. Note that CBC mode is absolutely not secure when directly used for transport mode security.
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.
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'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.