PHP Encrypt and Windows Decrypt - php

I 'm stuck. It seems that AES encryption done by PHP cannot be decrypted in windows.
PHP code:
$encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128,"12345678", "test", MCRYPT_MODE_CBC));
Windows Code:
"s" has the string which is created by the above response after converting back from base64.
bool Decrypt(char* s,char* key,char* dest)
{
// Create the crypto provider context.
HCRYPTPROV hProvider = NULL;
if (!CryptAcquireContext(&hProvider,
NULL, // pszContainer = no named container
MS_ENH_RSA_AES_PROV, // pszProvider = default provider
PROV_RSA_AES,
0))
return false;
// Construct the blob necessary for the key generation.
aes128keyBlob aes_blob128;
aes_blob128.header.bType = PLAINTEXTKEYBLOB;
aes_blob128.header.bVersion = CUR_BLOB_VERSION;
aes_blob128.header.reserved = 0;
aes_blob128.header.aiKeyAlg = CALG_AES_128;
aes_blob128.keySize = 16;
memcpy(aes_blob128.bytes, key, 16);
HCRYPTKEY hKey = NULL;
if (!CryptImportKey(hProvider,
(BYTE*)(&aes_blob128),
sizeof(aes_blob128),
NULL, //
0, //
&hKey)) {
...
}
// Set Mode
DWORD dwMode = CRYPT_MODE_CBC;
CryptSetKeyParam( hKey, KP_MODE, (BYTE*)&dwMode, 0 );
DWORD length = 16;
BOOL X = CryptDecrypt(hKey,
NULL, // hHash = no hash
TRUE, // Final
0,
(BYTE*)s,
&length);
//int le = GetLastError();
memcpy(dest,s,16);
CryptDestroyKey(hKey);
CryptReleaseContext(hProvider, 0);
}
What could be wrong?

The information you provided is not enough to say for certain but I think that your problem is the key length.
In PHP code you pass "12345678" as a key. And AES128 has a key length of 128bit or 16 bytes.
PHP pads the remaining with zero bytes, as stated in the documentation on mcrypt_encrypt.
In the C++ code you pass only the pointer to your key buffer to Decrypt function. But then you copy 16 bytes from it to the key BLOB:
aes_blob128.keySize = 16;
memcpy(aes_blob128.bytes, key, 16);
Then if you call your function like:
char dest[16];
bool result = Decrypt(string_from_php,"12345678",dest);
than the 8 bytes that happen to reside in memory after the "12345678" constant will be copied to the key blob and passed to CryptImportKey as an actual key. Thus the key in C and in PHP code would be actually different and the decryption will fail due to padding error.

PHP MCRYPT functions are a bit different than the windows decryption functions.
Because the mcrypt function takes the key of any length and converts it to the length required by the algorithm by adding \0 at the end of key string.
Please note to create a key with the specified length required for the encryption by the algorithm on both sides.
use md5 on the key and then convert it to the length required for the algorithm.

See below URL
Encrypt in PHP, Decrypt in C# (WP7 / Silverlight) using AES / Rijndael
http://pumka.net/2009/12/16/rsa-encryption-cplusplus-delphi-cryptoapi-php-openssl-2/
http://www.developer.nokia.com/Community/Wiki/Encrypt-Decrypt_contacts_database_entries_using_Symbian_C%2B%2B
Read it
I droped the MD5 crap out of PHP and C#, and they are now working properly.
Just in case you dropped here looking for the same answer, here is a sample code. Don't forget to make your own key and iv (although those bellow will work, is not recommended to use!)
PHP:
function encrypt128($message) {
$vector = "0000000000000000";
$key = "00000000000000000000000000000000";
$block = mcrypt_get_block_size('rijndael_128', 'cbc');
$pad = $block - (strlen($message) % $block);
$message .= str_repeat(chr($pad), $pad);
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', '');
mcrypt_generic_init($cipher, $key, $vector);
$result = mcrypt_generic($cipher, $message);
mcrypt_generic_deinit($cipher);
return base64_encode($result);
}
C++
Encrypt-Decrypt contacts database entries using Symbian C++
http://www.developer.nokia.com/Community/Wiki/Encrypt-Decrypt_contacts_database_entries_using_Symbian_C%2B%2B
Headers Required:
#include <cntdb.h> // CContactDatabse,
#include <cntitem.h> //CContactItem,CContactItemFieldSet
http://www.developer.nokia.com/Community/Wiki/Encrypt-Decrypt_contacts_database_entries_using_Symbian_C%2B%2B
Encrypt Contact Fields
void CEncryptContactContainer::EncryptAll()
{
CContactDatabase *contactDB = CContactDatabase::OpenL();
CleanupStack::PushL(contactDB);
TContactIter iter(*contactDB);
TContactItemId aContactId;
//Developer can take Heap based descriptor for large/unknown size of contact items.
TBuf16<70> aValue;
const CContactIdArray* contactArray = contactDB->SortedItemsL();
TInt cnt=contactArray->Count();
for(TInt i=0;i<cnt;i++)
{
CContactItem* contactItem=NULL;
contactItem= contactDB->OpenContactL((*contactArray)[i]);
CleanupStack::PushL(contactItem);
CContactItemFieldSet& fieldSet= contactItem->CardFields();
TInt fieldCount=fieldSet.Count(); // This will give number of contact fields.
for(TInt index=0; index < fieldCount; index++)
{
CContactItemField& field = fieldSet[index];
const CContentType& type = field.ContentType();
if(!(type.ContainsFieldType(KUidContactFieldBirthday)))
{
TPtrC name = contactItem->CardFields()[index].TextStorage()->Text();
aValue.Copy(name);
Encrypt(aValue); // Call real encyption here
contactItem->CardFields()[index].TextStorage()->SetTextL(aValue);
}
} //Inner for loop ends here
contactDB->CommitContactL(*contactItem);
CleanupStack::PopAndDestroy(contactItem);
} //Outer for loop ends here
CleanupStack::PopAndDestroy(contactDB);
}
void CEncryptContactContainer:: Encrypt (TDes& aValue)
{
for(TInt iCount=0; iCount< aValue.Length();iCount++)
{
aValue[iCount]+=3;
}
}
Decrypt Contact Fields
void CEncryptContactContainer::DecryptAll()
{
CContactDatabase *contactDB = CContactDatabase::OpenL();
CleanupStack::PushL(contactDB);
TContactIter iter(*contactDB);
TContactItemId aContactId;
TBuf16<70> aValue;
const CContactIdArray* contactArray = contactDB->SortedItemsL();
TInt cnt=contactArray->Count();
for(TInt i=0;i<cnt;i++)
{
CContactItem* contactItem=NULL;
contactItem= contactDB->OpenContactL((*contactArray)[i]);
CleanupStack::PushL(contactItem);
CContactItemFieldSet& fieldSet= contactItem->CardFields();
TInt fieldCount=fieldSet.Count(); // This will give number of contact fields.
for(TInt index=0; index < fieldCount; index++)
{
CContactItemField& field = fieldSet[index];
const CContentType& type = field.ContentType();
if(!(type.ContainsFieldType(KUidContactFieldBirthday)))
{
TPtrC name = contactItem->CardFields()[index].TextStorage()->Text();
aValue.Copy(name);
Decrypt(aValue);
contactItem->CardFields()[index].TextStorage()->SetTextL(aValue);
}
} //Inner for loop ends here
contactDB->CommitContactL(*contactItem);
CleanupStack::PopAndDestroy(contactItem);
} //Outer for loop ends here
CleanupStack::PopAndDestroy(contactDB);
}
void CEncryptContactContainer:: Decrypt (TDes& aValue)
{
for(TInt iCount=0; iCount< aValue.Length();iCount++)
{
aValue[iCount]-=3;
}
}
C#:
byte[] cripted = EncryptStringToBytes("Test", System.Text.Encoding.UTF8.GetBytes("00000000000000000000000000000000"), System.Text.Encoding.UTF8.GetBytes("0000000000000000"));

Related

Decrypt crypt-js encrypted string in PHP

I have encrypted mutiple strings one-by-one in using crypto-js in react.'
For encryption I used -
encryptAES = (text, key) => {
return CryptoJS.AES.encrypt(text, key).toString();
};
For decryption, I used function like following -
decryptAES = (encryptedBase64, key) => {
const decrypted = CryptoJS.AES.decrypt(encryptedBase64, key);
if (decrypted) {
try {
console.log(decrypted);
const str = decrypted.toString(CryptoJS.enc.Utf8);
if (str.length > 0) {
return str;
} else {
return 'error 1';
}
} catch (e) {
return 'error 2';
}
}
return 'error 3';
};
I have uploaded a working sample project of this encryption - decryption here.
For e.g., if I encrypt "I live in India" using key - "earth", it would output as - "U2FsdGVkX1+cBvU9yH5fIGVmliJYPXsv4AIosUGH4tA=", and similary it would decrypt successfully with the correct key.
Now I have multiple encrypted strings stored in my database, but now require them to store un-encrypted, so I wanted to decrypt them in PHP. I can decrypt them in js using the function mentioned above but I am unable to figure out how to do so in PHP. I have tried this github repository but I couldn't customize it for my use case.
For decryption, salt and ciphertext must first be determined. To do this, the encrypted data of the CryptoJS code must be Base64 decoded. The salt are the second 8 bytes of the Base64 decoded data, followed by the actual ciphertext (the first 8 bytes are the ASCII encoding of Salted__ and can be ignored).
After determining the salt, key and IV are to be derived with EVP_BytesToKey(). You can find various PHP implementations on the web, e.g. here. Note that CryptoJS uses MD5 as digest, so the digest in the linked code must be modified accordingly.
Once key and IV have been determined, the actual ciphertext can be decrypted.
All together:
<?php
// Separate salt and actual ciphertext
$ctOpenSSL = base64_decode("U2FsdGVkX1+cBvU9yH5fIGVmliJYPXsv4AIosUGH4tA=");
$salt = substr($ctOpenSSL, 8, 8);
$ciphertext = substr($ctOpenSSL, 16);
// Derive key and IV
$keyIv = EVP_BytesToKey($salt, "earth");
$key = substr($keyIv, 0, 32);
$iv = substr($keyIv, 32, 16);
// Decrypt
$decrypted = openssl_decrypt($ciphertext, "aes-256-cbc", $key, OPENSSL_RAW_DATA, $iv);
print($decrypted . PHP_EOL); // I live in India
function EVP_BytesToKey($salt, $password) {
$bytes = '';
$last = '';
while(strlen($bytes) < 48) {
$last = hash('md5', $last . $password . $salt, true);
$bytes.= $last;
}
return $bytes;
}
?>

Decrypt Access token using PHP for C# Encryption

I have an API URL with specific access token which was encrypted with C#(Below Code) and I want to Decrypt it using PHP post request by passing access token to parameters. Can anyone help me out to solve this problem.
Thanks in Advance!!
C# Code for Encryption:
private String AES_encrypt(String Input)
{
var aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 256;
aes.Padding = PaddingMode.PKCS7;
aes.Key =Convert.FromBase64String("QdZx1B0ZIcLK7DPNRK09wc/rjP4WnxtE");
aes.IV = Convert.FromBase64String("hBSE4tn6e/5c3YVKFZ54Iisi4MiDyCO0HJO+WZBeXoY=");
var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] xBuff = null;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
{
byte[] xXml = Encoding.UTF8.GetBytes(Input);
cs.Write(xXml, 0, xXml.Length);
}
xBuff = ms.ToArray();
}
String Output = Convert.ToBase64String(xBuff);
return Output;
}
So far I tried to decrypt it with the below code
function strippadding($string)
{
$slast = ord(substr($string, -1));
$slastc = chr($slast);
$pcheck = substr($string, -$slast);
if(preg_match("/$slastc{".$slast."}/", $string)){
$string = substr($string, 0, strlen($string)-$slast);
return $string;
} else {
return false;
}
}
function decrypt($string)
{
$key = base64_decode("DZR");
$iv = base64_decode("Shravan");
$string = base64_decode($string);
return strippadding(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $string, MCRYPT_MODE_CBC, $iv));
}
Fill out the items below:
Use this key and iv that are below.
key = QdZx1B0ZIcLK7DPNRK09wc/rjP4WnxtE
iv= hBSE4tn6e/5c3YVKFZ54Iisi4MiDyCO0HJO+WZBeXoY=
Run some text through your AES_encrypt() function and whatever comes out paste on the next line.
encrypted text = put your encrypted text here.
$xXml = openssl_decrypt(
$Output, #openssl_decrypt works with base64 encoded data
'AES-256-CBC',
base64_decode("QdZx1B0ZIcLK7DPNRK09wc/rjP4WnxtE"), #key
OPENSSL_RAW_DATA,
base64_decode("hBSE4tn6e/5c3YVKFZ54Iisi4MiDyCO0HJO+WZBeXoY=") #IV
);
Now $xXml is the binary form of the input string in UTF-8 encoded.
And make sure openssl is included in your PHP build.
You have not provided me with any encrypted text to be able to test this with.
Here is what I think you need to do:
In your C# code you need to change the block size to 128 bits:
aes.BlockSize = 128;
In your C# code your IV needs to be 128 bits or 16 bytes long. It needs to equal your selected block size.
So for now this needs to be your IV:
IV = HWeR102dxMjRHZlxTqL2aA==
Your key is set for 256 bits: So here is a 256 bit key:
Key = aZUEBKSsYRKA6CGQbwFwvIS8rUnW7YA2hVMNHnnf844=
C# has functions that will automatically generate a cryptographically strong string for you of a certain length. I suggest you find these functions and learn how to use them so you can generate your own keys and IVs.
Now for the PHP portion.
You should use the OpenSSL library instead of the Mcrypt library. Mcrypt is deprecated and is no longer supported. So here is an OpenSSL solution.
Since the block size is now 128 bits and the key size is 256 bits it will now be compatible with the openssl library's AES-256-CBC function.
$key = 'aZUEBKSsYRKA6CGQbwFwvIS8rUnW7YA2hVMNHnnf844='; //256 bit key.
$iv = 'HWeR102dxMjRHZlxTqL2aA=='; //128 bit IV length. The same as the block size that is set in the C#.
function decrypt($string, $key, $iv){
$cipherText = base64_decode($string); //We are going to use raw data.
return openssl_decrypt($cipherText, 'AES-256-CBC', base64_decode($key), OPENSSL_RAW_DATA, base64_decode($iv));
//Note that I did not specify no padding in the function. By default it is PKCS#7 which is what is set in the C# code.
}
The best I can tell this should work for you. This assumption is predicated on the fact that your AES_encrypt() is working correctly and that you have OpenSSL on your machine. Which you probably do.
Hope it helps!

Sagepay Error - 3045 : The Currency field is missing

Please do not mark it as duplicate. I have already gone through almost all the previous post for same issue, but was not able to tackle my error.
I am trying to upgrade the Sagepay Protocol from v2.23 to v3.00
I have also deployed PHP kit provided by Sagepay and getting the same error.
Below is the AES encryption I am using to make it compatible for v3.00 in my includes.php
Any guidance would be much appreciated.
<?
/* Base 64 Encoding function **
** PHP does it natively but just for consistency and ease of maintenance, let's declare our own function **/
function base64Encode($plain) {
// Initialise output variable
$output = "";
// Do encoding
$output = base64_encode($plain);
// Return the result
return $output;
}
/* Base 64 decoding function **
** PHP does it natively but just for consistency and ease of maintenance, let's declare our own function **/
function base64Decode($scrambled) {
// Initialise output variable
$output = "";
// Fix plus to space conversion issue
$scrambled = str_replace(" ","+",$scrambled);
// Do encoding
$output = base64_decode($scrambled);
// Return the result
return $output;
}
/* The SimpleXor encryption algorithm **
** NOTE: This is a placeholder really. Future releases of Form will use AES or TwoFish. Proper encryption **
** This simple function and the Base64 will deter script kiddies and prevent the "View Source" type tampering **
** It won't stop a half decent hacker though, but the most they could do is change the amount field to something **
** else, so provided the vendor checks the reports and compares amounts, there is no harm done. It's still **
** more secure than the other PSPs who don't both encrypting their forms at all */
function simpleXor($InString, $Key) {
// Initialise key array
$KeyList = array();
// Initialise out variable
$output = "";
// Convert $Key into array of ASCII values
for($i = 0; $i < strlen($Key); $i++){
$KeyList[$i] = ord(substr($Key, $i, 1));
}
// Step through string a character at a time
for($i = 0; $i < strlen($InString); $i++) {
// Get ASCII code from string, get ASCII code from key (loop through with MOD), XOR the two, get the character from the result
// % is MOD (modulus), ^ is XOR
$output.= chr(ord(substr($InString, $i, 1)) ^ ($KeyList[$i % strlen($Key)]));
}
// Return the result
return $output;
}
//** Wrapper function do encrypt an encode based on strEncryptionType setting **
function encryptAndEncode($strPost) {
global $strEncryptionType
,$strEncryptionPassword;
if ($strEncryptionType=="XOR")
{
//** XOR encryption with Base64 encoding **
return base64Encode(simpleXor($strPost,$strEncryptionPassword));
}
else
{
//** AES encryption, CBC blocking with PKCS5 padding then HEX encoding - DEFAULT **
//** use initialization vector (IV) set from $strEncryptionPassword
$strIV = $strEncryptionPassword;
//** add PKCS5 padding to the text to be encypted
$strPost = addPKCS5Padding($strPost);
//** perform encryption with PHP's MCRYPT module
$strCrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $strEncryptionPassword, $strPost, MCRYPT_MODE_CBC, $strIV);
//** perform hex encoding and return
return "#" . bin2hex($strCrypt);
}
}
//** Wrapper function do decode then decrypt based on header of the encrypted field **
function decodeAndDecrypt($strPost) {
global $strEncryptionPassword;
if (substr($strPost,0,1)=="#")
{
//** HEX decoding then AES decryption, CBC blocking with PKCS5 padding - DEFAULT **
//** use initialization vector (IV) set from $strEncryptionPassword
$strIV = $strEncryptionPassword;
//** remove the first char which is # to flag this is AES encrypted
$strPost = substr($strPost,1);
//** HEX decoding
$strPost = pack('H*', $strPost);
//** perform decryption with PHP's MCRYPT module
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $strEncryptionPassword, $strPost, MCRYPT_MODE_CBC, $strIV);
}
else
{
//** Base 64 decoding plus XOR decryption **
return simpleXor(base64Decode($strPost),$strEncryptionPassword);
}
}
//** PHP's mcrypt does not have built in PKCS5 Padding, so we use this
function addPKCS5Padding($input)
{
$blocksize = 16;
$padding = "";
// Pad input to an even block size boundary
$padlength = $blocksize - (strlen($input) % $blocksize);
for($i = 1; $i <= $padlength; $i++) {
$padding .= chr($padlength);
}
return $input . $padding;
}
/*************
function pkcs5_pad($text, $blocksize)
{
$pad = $blocksize - (strlen($text) % $blocksize);
//echo "<br/>Padding:".str_repeat(chr($pad), $pad)."<";
return $text . str_repeat(chr($pad), $pad);
}
function encryptFieldData($input)
{
$key = "[mykey]";
$iv = $key;
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, "", MCRYPT_MODE_CBC, "");
if (mcrypt_generic_init($cipher, $key, $iv) != -1)
{
$cipherText = mcrypt_generic($cipher,$input );
mcrypt_generic_deinit($cipher);
$enc = bin2hex($cipherText);
}
return $enc;
}
$str = "Currency=GBP";
$datapadded = pkcs5_pad($str,16);
$cryptpadded = "#" . encryptFieldData($datapadded);
*************************/
?>
The problem was with my server mcrypt PHP extension. It wasn't install/enable on the server. I must have consider this before making all test attempts.
Thank you all for the efforts towards my issue.
Have you checked your encryption password is correct? 3045 is the first error to be thrown if the password is wrong. This is not so nuts as it sounds - encryption passwords are different between test and live.....

How to implement a concat KDF for use with JOSE/JWE/JWT

I'm attempting to write code to decrypt a JWE token in PHP, as the existing libraries don't support the algorithm I need (A128CBC+HS256, it's a deprecated algorithm).
My issue is I can't understand how to generate the content encryption key which uses a "Concatenation Key
Derivation Function" (see section 5.8.1 here: http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf). The symbols and explanation of the function goes over my head.
I'm getting my values based on JOSE JSON web algorithms draft 06.
So far, the relevant portion of my code looks like this:
// Derive CBC encryption & integrity keys
$shaSize = 256;
$encryptionKeySize = $shaSize / 2;
$integrityKeySize = $shaSize;
// Calculate the key derivation using Concat KDF for the content
// encryption key
$encryptionSegments = [
$masterKey, // Z
$encryptionKeySize, // keydatalen
$this->packInt32sBe($encryptionKeySize) . utf8_encode('A128CBC+HS256'), // AlgorithmID
$this->packInt32sBe(0), // PartyUInfo
$this->packInt32sBe(0), // PartyUInfo
'Encryption', // SuppPubInfo
$this->packInt32sBe(1), // SuppPrivInfo
];
// Calculate the SHA256 digest
$cek = hex2bin(hash('sha256', implode('', $encryptionSegments)));
Possibly relevant, my function for getting a big endian integer:
public function packInt32sBe($n)
{
if (pack('L', 1) === pack('N', 1)) {
return pack('l', $n);
}
return strrev(pack('l', $n));
}
The only variable not shown here is $masterKey which is the decrypted content master key.
I did end up solving this. Not sure if it'll ever help anyone else, but just in case:
// Derive CBC encryption & integrity keys
$shaSize = 256;
$encryptionKeySize = $shaSize / 2;
$integrityKeySize = $shaSize;
// Calculate the key derivation using Concat KDF for the content
// encryption key
$encryptionSegments = [
$this->packInt32sBe(1),
$cmk, // Z
$this->packInt32sBe($encryptionKeySize) . utf8_encode('A128CBC+HS256'), // AlgorithmID
$this->packInt32sBe(0), // PartyUInfo
$this->packInt32sBe(0), // PartyUInfo
'Encryption', // SuppPubInfo
];
// Calculate the SHA256 digest, and then get the first 16 bytes of it
$cek = substr(hex2bin(hash('sha256', implode('', $encryptionSegments))), 0, 16);
The only unknown variable here is $cmk which is my content master key, aka the "Z" value. In this specific case I got the master key by decrypting it from an XBOX One token request.
Here is my own implementation according to the same specification, but draft #39:
<?php
class ConcatKDF
{
public static function generate($Z, $encryption_algorithm, $encryption_key_size, $apu = "", $apv = "")
{
$encryption_segments = array(
self::toInt32Bits(1), // Round number 1
$Z, // Z (shared secret)
self::toInt32Bits(strlen($encryption_algorithm)).$encryption_algorithm, // Size of algorithm and algorithm
self::toInt32Bits(strlen($apu)).$apu, // PartyUInfo
self::toInt32Bits(strlen($apv)).$apv, // PartyVInfo
self::toInt32Bits($encryption_key_size), // SuppPubInfo (the encryption key size)
"", // SuppPrivInfo
);
return substr(hex2bin(hash('sha256', implode('', $encryption_segments))), 0, $encryption_key_size/8);
}
private static function toInt32Bits($value)
{
return hex2bin(str_pad(dechex($value), 8, "0", STR_PAD_LEFT));
}
}
The use is very simple:
ConcatKDF::generate("The shared key here", 'A128CBC+HS256', 128);
If you have apu and apv parameters:
ConcatKDF::generate("Another shared key here", 'A128GCM', 128, "Alice", "Bob");

AES128 Encryption using PHP mcrypt is not outputting same as ASP.net

I have a PHP application that needs to encrypt a challenge string that is consumed by an ASP.net web service however the output of my PHP implementation is not properly decrypted by .net. Exactly as in this question relating to iOS and .net: AES128 bit encryption string is not similar as on .net
Why are the outputs different and what can I do to my PHP to output the same as .net?
My PHP code looks like this:
$key = '128bit-16bytekey';
$value = '128bit-16byteval';
function fnEncrypt($value, $key)
{
$ivsize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($ivsize);
return base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key, $value,
MCRYPT_MODE_ECB,$iv
));
}
.net like this
public static string EncryptData(string plainText)
{
string encryptionKey = AppConstants.AES_ENCRYPTDECRYPT_KEY;
// Convert our plaintext into a byte array.
// Let us assume that plaintext contains UTF8-encoded characters.
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
// Use the password to generate pseudo-random bytes for the encryption
// key. Specify the size of the key in bytes (instead of bits).
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] keyBytes = new byte[16];
byte[] tempKey = encoding.GetBytes(encryptionKey);
for (int i = 0; i < keyBytes.Length; i++)
{
if (i < tempKey.Length)
{
keyBytes[i] = tempKey[i];
}
else
{
keyBytes[i] = 0;
}
}
// Create uninitialized Rijndael encryption object.
RijndaelManaged symmetricKey = new RijndaelManaged();
//AesManaged symmetricKey = new AesManaged();
//symmetricKey.Padding = PaddingMode.PKCS7;
// It is reasonable to set encryption mode to Cipher Block Chaining
// (CBC). Use default options for other symmetric key parameters.
symmetricKey.Mode = CipherMode.ECB;
// Generate encryptor from the existing key bytes and initialization
// vector. Key size will be defined based on the number of the key
// bytes.
//ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes,initVectorBytes);
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, null);
// Define memory stream which will be used to hold encrypted data.
MemoryStream memoryStream = new MemoryStream();
// Define cryptographic stream (always use Write mode for encryption).
CryptoStream cryptoStream = new CryptoStream(memoryStream,
encryptor,
CryptoStreamMode.Write);
// Start encrypting.
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
// Finish encrypting.
cryptoStream.FlushFinalBlock();
// Convert our encrypted data from a memory stream into a byte array.
byte[] cipherTextBytes = memoryStream.ToArray();
// Close both streams.
memoryStream.Close();
cryptoStream.Close();
// Convert encrypted data into a base64-encoded string.
string cipherText = Convert.ToBase64String(cipherTextBytes);
// Return encrypted string.
return cipherText;
}
Sample outputs
PHP : Q54nP/tXq2rDTUwWw4ckkg==
.net : Q54nP/tXq2rDTUwWw4ckkpSt9CQiIzsg2xsQEndcqc8=
PHP : DQZdAB/lABXVOOoCdNM6HQ==
.net : DQZdAB/lABXVOOoCdNM6HZSt9CQiIzsg2xsQEndcqc8=
As in the question ref'd above the right hand side of the .net output are always the same and left side are consistent between the two implementations. I am pretty sure the IV is irrelevant and that it is something to do with how padding is handled in mcrypt.
If I decrypt either of the outputs in PHP it returns the same correct result.
Can anyone shed any light? I am unable to change the .net app. Thanks!
This happens due to the padding that is applied in .net. You can overcome this by adding the following line to your .net code:
symmetricKey.Padding = PaddingMode.None;
Or you can change the PHP code to get the same encrypted string:
// Add the lines below to your fnEncrypt function
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$len = strlen($value);
$padding = $block - ($len % $block);
$value .= str_repeat(chr($padding),$padding);
A similar issue is described in one of the comments from PHP manual pages: http://www.php.net//manual/en/function.mcrypt-encrypt.php#47973

Categories