I need to port this code (console application in c#):
Decrypt PHP encrypted string in C#
to WinRT code. I have:
class Crypto{const string Key = "lkirwf897+22#bbtrm8814z5qq=498j5";
const string IV = "741952hheeyy66#cs!9hjv887mxx7#8y";
public static string Encrypt(string login)
{
var input = CryptographicBuffer.ConvertStringToBinary(login, BinaryStringEncoding.Utf8);
var key1 = CryptographicBuffer.ConvertStringToBinary(Key, BinaryStringEncoding.Utf8);
var iv = CryptographicBuffer.ConvertStringToBinary(IV, BinaryStringEncoding.Utf8);
var encryptor = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
var key2 = encryptor.CreateSymmetricKey(key1);
var encrypted = CryptographicEngine.Encrypt(key2, input, iv);
return CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, encrypted);
}
public static string Decrypt(string LoginToDecode)
{
var input = CryptographicBuffer.ConvertStringToBinary(LoginToDecode, BinaryStringEncoding.Utf8);
var key1 = CryptographicBuffer.ConvertStringToBinary(Key, BinaryStringEncoding.Utf8);
var iv = CryptographicBuffer.ConvertStringToBinary(IV, BinaryStringEncoding.Utf8);
var decryptor = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
var key2 = decryptor.CreateSymmetricKey(key1);
var decrypted = CryptographicEngine.Decrypt(key2, input, iv);
return CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, decrypted);
}}
I have an error in the last line of the Encrypt function:
Additional information: No mapping for the Unicode character exists in the target multi-byte code page.
I would like to get the same result like in topic above. This solution can be correct without error?
UPDATE
I removed the error. Code is:
class Crypto{const string Key = "lkirwf897+22#bbtrm8814z5qq=498j5";
const string IV = "741952hheeyy66#cs!9hjv887mxx7#8y";
public static string Encrypt(string login)
{
var input = CryptographicBuffer.ConvertStringToBinary(login, BinaryStringEncoding.Utf8);
var key1 = CryptographicBuffer.ConvertStringToBinary(Key, BinaryStringEncoding.Utf8);
var iv = CryptographicBuffer.ConvertStringToBinary(IV, BinaryStringEncoding.Utf8);
var encryptor = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
var key2 = encryptor.CreateSymmetricKey(key1);
var encrypted = CryptographicEngine.Encrypt(key2, input, iv);
return CryptographicBuffer.EncodeToBase64String(encrypted);
}
public static string Decrypt(string LoginToDecode)
{
var input = CryptographicBuffer.DecodeFromBase64String(LoginToDecode);
var key1 = CryptographicBuffer.ConvertStringToBinary(Key, BinaryStringEncoding.Utf8);
var iv = CryptographicBuffer.ConvertStringToBinary(IV, BinaryStringEncoding.Utf8);
var decryptor = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
var key2 = decryptor.CreateSymmetricKey(key1);
var decrypted = CryptographicEngine.Decrypt(key2, input, iv);
return CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, decrypted);
}
}}
But the result of this code it's not the same like from topic above. I need this because I have to communicate with php server.
UPDATE#2
Ok, I learned that in WinRT can I use only 128AES. So, I changed PHP script:
function decryptRJ128($key,$iv,$string_to_decrypt){
$string_to_decrypt = base64_decode($string_to_decrypt);
$rtn = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $string_to_decrypt, MCRYPT_MODE_CBC, $iv);
$rtn = rtrim($rtn, "\0\4");
return($rtn);}
function encryptRJ128($key,$iv,$string_to_encrypt){ $rtn = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string_to_encrypt, MCRYPT_MODE_CBC, $iv); $rtn = base64_encode($rtn); return($rtn);}
And for
password = "Asd"
Key = "lkirwf897+22#bbt" it's must be 16bit
IV = "741952hheeyy66#c"
I have in c# "eSy8m8ygN7rtC80DMdGOUQ==". I need this in PHP.
Well, here's my solution.
In C# code I need to merge two byte array before encode it into base64.
/* PHP */
$key = '12345678abcdefgh';
$text = "d9e978bc7ca9179f0d51e91521d41d8d";
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size);
$iv = '87654321hgfedcba'; //Just to test
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
$encrypted = base64_encode($iv.$encrypted);
$decryptedd = base64_decode($encrypted);
$iv_dec = substr($decryptedd, 0, $iv_size);
$decrypted = substr($decryptedd, $iv_size);
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $decrypted, MCRYPT_MODE_CBC, $iv_dec);
echo 'Key: '.$key.'<br />';
echo 'Text: '.$text.'<br />';
echo 'IV: '.$iv.' (Size: '.$iv_size.')<br />';
echo 'Encrypted: '.$encrypted.'<br />';
echo 'IV decrypted: '.$iv_dec.'<br />';
echo 'Decrypted: '.$decrypted.'<br />';
And here is C# code:
var input = CryptographicBuffer.ConvertStringToBinary("d9e978bc7ca9179f0d51e91521d41d8d", BinaryStringEncoding.Utf8);
var key = CryptographicBuffer.ConvertStringToBinary("12345678abcdefgh", BinaryStringEncoding.Utf8);
var iv = CryptographicBuffer.ConvertStringToBinary("87654321hgfedcba", BinaryStringEncoding.Utf8);
var encryptor = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbc);
byte[] a = iv.ToArray();
byte[] b = CryptographicEngine.Encrypt(encryptor.CreateSymmetricKey(key), input, iv).ToArray();
byte[] c = new byte[a.Length + b.Length];
Buffer.BlockCopy(a, 0, c, 0, a.Length);
Buffer.BlockCopy(b, 0, c, a.Length, b.Length);
var result = Convert.ToBase64String(c);
Related
I have following nodejs code to encrypt RSA:
const encryptWithRSA = (PublicKey, selData) => {
let encrypted = crypto.publicEncrypt(
{
key: -----BEGIN PUBLIC KEY-----\n${PublicKey}\n-----END PUBLIC KEY-----,
padding: crypto.constants.RSA_PKCS1_PADDING,
},
Buffer.from(selData)
);
return encrypted.toString("base64");
};
Then I tried to convert this code block to C# :
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
public static string Encrypt(string publickey, string data)
{
string key = publickey;
Asn1Object obj = Asn1Object.FromByteArray(Convert.FromBase64String(key));
DerSequence publicKeySequence = (DerSequence)obj;
DerBitString encodedPublicKey = (DerBitString)publicKeySequence[1];
DerSequence publicKey = (DerSequence)Asn1Object.FromByteArray(encodedPublicKey.GetBytes());
DerInteger modulus = (DerInteger)publicKey[0];
DerInteger exponent = (DerInteger)publicKey[1];
RsaKeyParameters keyParameters = new RsaKeyParameters(false, modulus.PositiveValue, exponent.PositiveValue);
RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyParameters);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(parameters);
byte[] dataToEncrypt = Encoding.UTF8.GetBytes(data);
byte[] encryptedData = rsa.Encrypt(dataToEncrypt, RSAEncryptionPadding.Pkcs1);
return Convert.ToBase64String(encryptedData);
}
Two code return 2 different results. Anyone can show me what wrong? Thanks!
I'm trying to encrypt some (cookie) data in C# and then decrypt it in PHP. I have chosen to use Rijndael encryption. I've almost got it working, except only part of the text is decrypted! I started working from this example: Decrypt PHP encrypted string in C#
Here's the text (JSON) that I am encrypting (sensitive information removed):
{"DisplayName":"xxx", "Username": "yyy", "EmailAddress":"zzz"}
So I login to the C# app which creates/encodes the cookie from stored Key and IV and then redirects to the PHP app which is supposed to decrypt/read the cookie. When I decrypt the cookie, it comes out like this:
{"DisplayName":"xxx","F�A ;��HP=D�������4��z����ť���k�#E���R�j�5�\�t. t�D��"
UPDATE: i've gotten a little bit further and this is now the result
string(96) "{"DisplayName":"xxx","Username":"yyy","EmailAddress"�)ق��-�J��k/VV-v� �9�B`7^"
As you can see, it starts decrypting it, but then gets messed up...
When Decrypt the string it comes out correct (with padding, which I have a function to remove padding), but if I change the test string by one character I get garbage again:
B�nHL�Ek �¿?�UΣlO����OЏ�M��NO/�f.M���Lƾ�CC�Y>F��~�qd�+
Here's the c# code I use to generate the random Key and IV:
UPDATE: I'm just using static key/IV for now, here they are:
Key: lkirwf897+22#bbtrm8814z5qq=498j5
IV: 741952hheeyy66#cs!9hjv887mxx7#8y
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.BlockSize = 256;
symmetricKey.KeySize = 256;
symmetricKey.Padding = PaddingMode.Zeros;
symmetricKey.Mode = CipherMode.CBC;
string key = Convert.ToBase64String(symmetricKey.Key);
string IV = Convert.ToBase64String(symmetricKey.IV);
I then save the key and IV to a database to be retrieved later for encoding/decoding.
This is the full encryption class:
public static class Encryption
{
public static string Encrypt(string prm_text_to_encrypt, string prm_key, string prm_iv)
{
var sToEncrypt = prm_text_to_encrypt;
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
//FeedbackSize = 256
};
var key = Encoding.ASCII.GetBytes(prm_key);
var IV = Encoding.ASCII.GetBytes(prm_iv);
//var key = Convert.FromBase64String(prm_key);
//var IV = Convert.FromBase64String(prm_iv);
var encryptor = rj.CreateEncryptor(key, IV);
var msEncrypt = new MemoryStream();
var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
var toEncrypt = Encoding.ASCII.GetBytes(sToEncrypt);
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
csEncrypt.FlushFinalBlock();
var encrypted = msEncrypt.ToArray();
return (Convert.ToBase64String(encrypted));
}
public static string Decrypt(string prm_text_to_decrypt, string prm_key, string prm_iv)
{
var sEncryptedString = prm_text_to_decrypt;
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
//FeedbackSize = 256
};
var key = Encoding.ASCII.GetBytes(prm_key);
var IV = Encoding.ASCII.GetBytes(prm_iv);
//var key = Convert.FromBase64String(prm_key);
//var IV = Convert.FromBase64String(prm_iv);
var decryptor = rj.CreateDecryptor(key, IV);
var sEncrypted = Convert.FromBase64String(sEncryptedString);
var fromEncrypt = new byte[sEncrypted.Length];
var msDecrypt = new MemoryStream(sEncrypted);
var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
return (Encoding.ASCII.GetString(fromEncrypt));
}
public static void GenerateKeyIV(out string key, out string IV)
{
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
//FeedbackSize = 256
};
rj.GenerateKey();
rj.GenerateIV();
key = Convert.ToBase64String(rj.Key);
IV = Convert.ToBase64String(rj.IV);
}
}
Here's the PHP code I am using to decrypt the data:
function decryptRJ256($key,$iv,$string_to_decrypt)
{
$string_to_decrypt = base64_decode($string_to_decrypt);
$rtn = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $string_to_decrypt, MCRYPT_MODE_CBC, $iv);
//$rtn = rtrim($rtn, "\0\4");
$rtn = unpad($rtn);
return($rtn);
}
function unpad($value)
{
$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
//apply pkcs7 padding removal
$packing = ord($value[strlen($value) - 1]);
if($packing && $packing < $blockSize){
for($P = strlen($value) - 1; $P >= strlen($value) - $packing; $P--){
if(ord($value{$P}) != $packing){
$packing = 0;
}//end if
}//end for
}//end if
return substr($value, 0, strlen($value) - $packing);
}
$ky = 'lkirwf897+22#bbtrm8814z5qq=498j5'; // 32 * 8 = 256 bit key
$iv = '741952hheeyy66#cs!9hjv887mxx7#8y'; // 32 * 8 = 256 bit iv
$enc = $_COOKIE["MyCookie"];
$dtext = decryptRJ256($ky, $iv, $enc);
var_dump($dtext);
I am a little unsure about this part, because all of the example code I've seen simply passes in the base64 encoded string directly to the decryptor, but in my example, I have to base64_decode it before I pass it otherwise I get the error that the key and IV are not the correct length.
UPDATE: I'm using ASCII keys in the format needed by PHP. If I generate keys from the RijndaelManaged class they dont work on the PHP side, but I can use keys that are known to work on PHP side and use them in the RijndaelManaged C# side.
Please let me know if I left out any pertinent information. TIA!
For posterity I'm placing the fully completed solution here.
C# Encryption Class
public static class Encryption
{
public static string Encrypt(string prm_text_to_encrypt, string prm_key, string prm_iv)
{
var sToEncrypt = prm_text_to_encrypt;
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
};
var key = Convert.FromBase64String(prm_key);
var IV = Convert.FromBase64String(prm_iv);
var encryptor = rj.CreateEncryptor(key, IV);
var msEncrypt = new MemoryStream();
var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
var toEncrypt = Encoding.ASCII.GetBytes(sToEncrypt);
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
csEncrypt.FlushFinalBlock();
var encrypted = msEncrypt.ToArray();
return (Convert.ToBase64String(encrypted));
}
public static string Decrypt(string prm_text_to_decrypt, string prm_key, string prm_iv)
{
var sEncryptedString = prm_text_to_decrypt;
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
};
var key = Convert.FromBase64String(prm_key);
var IV = Convert.FromBase64String(prm_iv);
var decryptor = rj.CreateDecryptor(key, IV);
var sEncrypted = Convert.FromBase64String(sEncryptedString);
var fromEncrypt = new byte[sEncrypted.Length];
var msDecrypt = new MemoryStream(sEncrypted);
var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
return (Encoding.ASCII.GetString(fromEncrypt));
}
public static void GenerateKeyIV(out string key, out string IV)
{
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
};
rj.GenerateKey();
rj.GenerateIV();
key = Convert.ToBase64String(rj.Key);
IV = Convert.ToBase64String(rj.IV);
}
}
PHP Decryption Snippet
<?php
function decryptRJ256($key,$iv,$encrypted)
{
//PHP strips "+" and replaces with " ", but we need "+" so add it back in...
$encrypted = str_replace(' ', '+', $encrypted);
//get all the bits
$key = base64_decode($key);
$iv = base64_decode($iv);
$encrypted = base64_decode($encrypted);
$rtn = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
$rtn = unpad($rtn);
return($rtn);
}
//removes PKCS7 padding
function unpad($value)
{
$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$packing = ord($value[strlen($value) - 1]);
if($packing && $packing < $blockSize)
{
for($P = strlen($value) - 1; $P >= strlen($value) - $packing; $P--)
{
if(ord($value{$P}) != $packing)
{
$packing = 0;
}
}
}
return substr($value, 0, strlen($value) - $packing);
}
?>
<pre>
<?php
$enc = $_COOKIE["MyCookie"];
$ky = ""; //INSERT THE KEY GENERATED BY THE C# CLASS HERE
$iv = ""; //INSERT THE IV GENERATED BY THE C# CLASS HERE
$json_user = json_decode(decryptRJ256($ky, $iv, $enc), true);
var_dump($json_user);
?>
Since the string is partially OK, but there is gibberish at the end it would suggest a padding problem within the encryption which expects exact blocks of 256 bytes. I suggest setting the padding as PKCS7 (PaddingMode.PKCS7) instead of Zeros on the C# side which PHP will understand without issues (as it's the default mode on that parser).
Edit: Oops, I did not notice that you had the following in your PHP:
$enc = $_COOKIE["MyCookie"];
This is the caveat. PHP is likely not getting the encrypted data as-is and is running some urldecode sanitizing. You should print this variable to see that it really matches what is being sent from the C# code.
Edit2:
Convert the whitespaces to missing + characters from the cookie by adding this:
str_replace(' ', '+', $enc);
I have this code in C#
var dataBytes = Encoding.UTF8.GetBytes('clear text');
var symmetric = SymmetricAlgorithm.Create("TripleDes");
symmetric.Mode = CipherMode.ECB;
symmetric.Padding = PaddingMode.PKCS7;
var encryptor = symmetric.CreateEncryptor(Convert.FromBase64String('myKey'), new byte[8]);
var signedData = Convert.ToBase64String(encryptor.TransformFinalBlock(dataBytes, 0, dataBytes.Length));
now i need convert c# code to php code.
regard
I use this function and worked for me
function encryptData($plainText,$key)
{
$byte = mb_convert_encoding($key, 'ASCII');
$desKey = base64_decode($key);
$data = mb_convert_encoding($plainText, 'ASCII');
// add PKCS#7 padding
$blocksize = mcrypt_get_block_size('tripledes', 'ecb');
$paddingSize = $blocksize - (strlen($data) % $blocksize);
$data .= str_repeat(chr($paddingSize), $paddingSize);
// encrypt password
$encData = mcrypt_encrypt('tripledes', $desKey, $data, 'ecb');
return base64_encode($encData);
}
I need to replicate the following C# method to encrypt some text from Javascript. Currently I am using Crypto JS, but the output from JS is not equals to the C# output.
const string EncryptKey = "hello";
private static String getHexStringFromArray(byte[] arr) {
StringBuilder sBuilder = new StringBuilder();
for (int i = 0; i < arr.Length; i++) {
sBuilder.Append(arr[i].ToString("x2"));
}
return sBuilder.ToString();
}
public void Encrypt(string toEncrypt, bool useHashing) {
byte[] keyArray;
byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
string key = EncryptKey;
if (useHashing) {
MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
hashmd5.Clear();
} else
keyArray = UTF8Encoding.UTF8.GetBytes(key);
Console.WriteLine("hexadecimal key: " + getHexStringFromArray(keyArray));
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = keyArray;
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tdes.CreateEncryptor();
byte[] resultArray =
cTransform.TransformFinalBlock(toEncryptArray, 0,
toEncryptArray.Length);
tdes.Clear();
Console.WriteLine("hexadecimal encrypted: " + getHexStringFromArray(resultArray));
//Return the encrypted data into unreadable string format
string test = Convert.ToBase64String(resultArray, 0, resultArray.Length);
Console.WriteLine("Output: " + test);
}
The output for Encrypt("password", true) is:
hexadecimal key: 5d41402abc4b2a76b9719d911017c592
hexadecimal encrypted: 069c44845e907b346b9d82d1d553f391
Output: BpxEhF6QezRrnYLR1VPzkQ==
Now, the Javascript implementation (please, ignore the global variables):
window.text = "password";
window.key = "hello";
var useHashing = true;
if (useHashing){
key = CryptoJS.MD5(key).toString();
}
window.options = {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
};
window.textWordArray = CryptoJS.enc.Utf8.parse(text);
window.keyHex = CryptoJS.enc.Hex.parse(key);
console.log('hexadecimal key: ' + keyHex);
window.encrypted = CryptoJS.TripleDES.encrypt(textWordArray, keyHex, options);
var base64String = encrypted.toString();
console.log('base64: ' + base64String);
window.decrypted = CryptoJS.TripleDES.decrypt( {
ciphertext: CryptoJS.enc.Base64.parse(base64String)
}, keyHex, options);
console.log('decrypted: ' + decrypted.toString(CryptoJS.enc.Utf8));
Produces this result:
hexadecimal key: 5d41402abc4b2a76b9719d911017c592
base64: BK5f0AhEuUl9pYEy2Mliyw==
Which is different from the C# implementation.
Here you can find the Javascript code.
Any help?
TripleDES requires 24-byte key (k1 + k2 + k3). Your key is only 16-byte. And .NET auto completes with k3 = k1. But Javascript does not, k3 = 0. Please modify the key:
if (useHashing){
key = CryptoJS.MD5(key).toString();
var k1 = key.substring(0, 16);
key = key + k1;
}
Here is Decrypter using 3DES-ECB of Forge js.
Since I couldn't find forge js solution adding one so others can use it.
var md = forge.md.md5.create();
md.update(window.key);
var key = md.digest().getBytes();
//3DES-ECB requires 24byte of data and key returned from md5 is 16byte
var k1 = key.substring(0, 8);
var key1 = key + key1;
var input = forge.util.createBuffer(forge.util.decode64(window.text));
var decTer = forge.cipher.createDecipher('3DES-ECB', key1);
decTer.start();
decTer.update(input);
return decTer.output.getBytes();
var message = "information";
var key = "t8g5av9Z0IsZ77tyox9H19Rb"; //length=22
var iv = "OjgLqBur"; //length=22
let cipher = CryptoJS.TripleDES.encrypt(message, CryptoJS.enc.Utf8.parse(key), {
iv: CryptoJS.enc.Utf8.parse(iv),
mode: CryptoJS.mode.CBC
});
let decrypt = CryptoJS.TripleDES.decrypt(cipher, CryptoJS.enc.Utf8.parse(key), {
iv: CryptoJS.enc.Utf8.parse(iv)
});
console.log(cipher.toString());
console.log(decrypt.toString(CryptoJS.enc.Utf8));
I'm trying to encrypt some (cookie) data in C# and then decrypt it in PHP. I have chosen to use Rijndael encryption. I've almost got it working, except only part of the text is decrypted! I started working from this example: Decrypt PHP encrypted string in C#
Here's the text (JSON) that I am encrypting (sensitive information removed):
{"DisplayName":"xxx", "Username": "yyy", "EmailAddress":"zzz"}
So I login to the C# app which creates/encodes the cookie from stored Key and IV and then redirects to the PHP app which is supposed to decrypt/read the cookie. When I decrypt the cookie, it comes out like this:
{"DisplayName":"xxx","F�A ;��HP=D�������4��z����ť���k�#E���R�j�5�\�t. t�D��"
UPDATE: i've gotten a little bit further and this is now the result
string(96) "{"DisplayName":"xxx","Username":"yyy","EmailAddress"�)ق��-�J��k/VV-v� �9�B`7^"
As you can see, it starts decrypting it, but then gets messed up...
When Decrypt the string it comes out correct (with padding, which I have a function to remove padding), but if I change the test string by one character I get garbage again:
B�nHL�Ek �¿?�UΣlO����OЏ�M��NO/�f.M���Lƾ�CC�Y>F��~�qd�+
Here's the c# code I use to generate the random Key and IV:
UPDATE: I'm just using static key/IV for now, here they are:
Key: lkirwf897+22#bbtrm8814z5qq=498j5
IV: 741952hheeyy66#cs!9hjv887mxx7#8y
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.BlockSize = 256;
symmetricKey.KeySize = 256;
symmetricKey.Padding = PaddingMode.Zeros;
symmetricKey.Mode = CipherMode.CBC;
string key = Convert.ToBase64String(symmetricKey.Key);
string IV = Convert.ToBase64String(symmetricKey.IV);
I then save the key and IV to a database to be retrieved later for encoding/decoding.
This is the full encryption class:
public static class Encryption
{
public static string Encrypt(string prm_text_to_encrypt, string prm_key, string prm_iv)
{
var sToEncrypt = prm_text_to_encrypt;
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
//FeedbackSize = 256
};
var key = Encoding.ASCII.GetBytes(prm_key);
var IV = Encoding.ASCII.GetBytes(prm_iv);
//var key = Convert.FromBase64String(prm_key);
//var IV = Convert.FromBase64String(prm_iv);
var encryptor = rj.CreateEncryptor(key, IV);
var msEncrypt = new MemoryStream();
var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
var toEncrypt = Encoding.ASCII.GetBytes(sToEncrypt);
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
csEncrypt.FlushFinalBlock();
var encrypted = msEncrypt.ToArray();
return (Convert.ToBase64String(encrypted));
}
public static string Decrypt(string prm_text_to_decrypt, string prm_key, string prm_iv)
{
var sEncryptedString = prm_text_to_decrypt;
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
//FeedbackSize = 256
};
var key = Encoding.ASCII.GetBytes(prm_key);
var IV = Encoding.ASCII.GetBytes(prm_iv);
//var key = Convert.FromBase64String(prm_key);
//var IV = Convert.FromBase64String(prm_iv);
var decryptor = rj.CreateDecryptor(key, IV);
var sEncrypted = Convert.FromBase64String(sEncryptedString);
var fromEncrypt = new byte[sEncrypted.Length];
var msDecrypt = new MemoryStream(sEncrypted);
var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
return (Encoding.ASCII.GetString(fromEncrypt));
}
public static void GenerateKeyIV(out string key, out string IV)
{
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
//FeedbackSize = 256
};
rj.GenerateKey();
rj.GenerateIV();
key = Convert.ToBase64String(rj.Key);
IV = Convert.ToBase64String(rj.IV);
}
}
Here's the PHP code I am using to decrypt the data:
function decryptRJ256($key,$iv,$string_to_decrypt)
{
$string_to_decrypt = base64_decode($string_to_decrypt);
$rtn = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $string_to_decrypt, MCRYPT_MODE_CBC, $iv);
//$rtn = rtrim($rtn, "\0\4");
$rtn = unpad($rtn);
return($rtn);
}
function unpad($value)
{
$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
//apply pkcs7 padding removal
$packing = ord($value[strlen($value) - 1]);
if($packing && $packing < $blockSize){
for($P = strlen($value) - 1; $P >= strlen($value) - $packing; $P--){
if(ord($value{$P}) != $packing){
$packing = 0;
}//end if
}//end for
}//end if
return substr($value, 0, strlen($value) - $packing);
}
$ky = 'lkirwf897+22#bbtrm8814z5qq=498j5'; // 32 * 8 = 256 bit key
$iv = '741952hheeyy66#cs!9hjv887mxx7#8y'; // 32 * 8 = 256 bit iv
$enc = $_COOKIE["MyCookie"];
$dtext = decryptRJ256($ky, $iv, $enc);
var_dump($dtext);
I am a little unsure about this part, because all of the example code I've seen simply passes in the base64 encoded string directly to the decryptor, but in my example, I have to base64_decode it before I pass it otherwise I get the error that the key and IV are not the correct length.
UPDATE: I'm using ASCII keys in the format needed by PHP. If I generate keys from the RijndaelManaged class they dont work on the PHP side, but I can use keys that are known to work on PHP side and use them in the RijndaelManaged C# side.
Please let me know if I left out any pertinent information. TIA!
For posterity I'm placing the fully completed solution here.
C# Encryption Class
public static class Encryption
{
public static string Encrypt(string prm_text_to_encrypt, string prm_key, string prm_iv)
{
var sToEncrypt = prm_text_to_encrypt;
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
};
var key = Convert.FromBase64String(prm_key);
var IV = Convert.FromBase64String(prm_iv);
var encryptor = rj.CreateEncryptor(key, IV);
var msEncrypt = new MemoryStream();
var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
var toEncrypt = Encoding.ASCII.GetBytes(sToEncrypt);
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
csEncrypt.FlushFinalBlock();
var encrypted = msEncrypt.ToArray();
return (Convert.ToBase64String(encrypted));
}
public static string Decrypt(string prm_text_to_decrypt, string prm_key, string prm_iv)
{
var sEncryptedString = prm_text_to_decrypt;
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
};
var key = Convert.FromBase64String(prm_key);
var IV = Convert.FromBase64String(prm_iv);
var decryptor = rj.CreateDecryptor(key, IV);
var sEncrypted = Convert.FromBase64String(sEncryptedString);
var fromEncrypt = new byte[sEncrypted.Length];
var msDecrypt = new MemoryStream(sEncrypted);
var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
return (Encoding.ASCII.GetString(fromEncrypt));
}
public static void GenerateKeyIV(out string key, out string IV)
{
var rj = new RijndaelManaged()
{
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256,
};
rj.GenerateKey();
rj.GenerateIV();
key = Convert.ToBase64String(rj.Key);
IV = Convert.ToBase64String(rj.IV);
}
}
PHP Decryption Snippet
<?php
function decryptRJ256($key,$iv,$encrypted)
{
//PHP strips "+" and replaces with " ", but we need "+" so add it back in...
$encrypted = str_replace(' ', '+', $encrypted);
//get all the bits
$key = base64_decode($key);
$iv = base64_decode($iv);
$encrypted = base64_decode($encrypted);
$rtn = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
$rtn = unpad($rtn);
return($rtn);
}
//removes PKCS7 padding
function unpad($value)
{
$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$packing = ord($value[strlen($value) - 1]);
if($packing && $packing < $blockSize)
{
for($P = strlen($value) - 1; $P >= strlen($value) - $packing; $P--)
{
if(ord($value{$P}) != $packing)
{
$packing = 0;
}
}
}
return substr($value, 0, strlen($value) - $packing);
}
?>
<pre>
<?php
$enc = $_COOKIE["MyCookie"];
$ky = ""; //INSERT THE KEY GENERATED BY THE C# CLASS HERE
$iv = ""; //INSERT THE IV GENERATED BY THE C# CLASS HERE
$json_user = json_decode(decryptRJ256($ky, $iv, $enc), true);
var_dump($json_user);
?>
Since the string is partially OK, but there is gibberish at the end it would suggest a padding problem within the encryption which expects exact blocks of 256 bytes. I suggest setting the padding as PKCS7 (PaddingMode.PKCS7) instead of Zeros on the C# side which PHP will understand without issues (as it's the default mode on that parser).
Edit: Oops, I did not notice that you had the following in your PHP:
$enc = $_COOKIE["MyCookie"];
This is the caveat. PHP is likely not getting the encrypted data as-is and is running some urldecode sanitizing. You should print this variable to see that it really matches what is being sent from the C# code.
Edit2:
Convert the whitespaces to missing + characters from the cookie by adding this:
str_replace(' ', '+', $enc);