We have the following simple encryption method in c#, we want write the same method in php7+
method in C#
using System.Security.Cryptography;
public string Encrypt(string strText, string encKey)
{
byte[] newEncKey = new byte[encKey.Length];
for (int i = 0; i < encKey.Length; i++)
newEncKey[i] = (byte)encKey[i];
try
{
TripleDESCryptoServiceProvider provider = new TripleDESCryptoServiceProvider();
byte[] bytes = Encoding.UTF8.GetBytes(strText);
MemoryStream stream = new MemoryStream();
CryptoStream stream2 = new CryptoStream(stream, provider.CreateEncryptor(newEncKey, newEncKey), CryptoStreamMode.Write);
stream2.Write(bytes, 0, bytes.Length);
stream2.FlushFinalBlock();
return Convert.ToBase64String(stream.ToArray());
}
catch (Exception)
{
return string.Empty;
}
}
We want get the same encryption text with the same key in c# and php7+,
We tried to use the following code in php
public function encryptData($key, $plainText)
{
$byte = mb_convert_encoding($key, 'ASCII');
$desKey = md5(utf8_encode($byte), true);
$desKey .= substr($desKey,0,8);
$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);
}
, but encryption text using this method not match the encryption text in C# method.
Example :
C#:
String key = "234576385746hfgr";
String text = "hello";
Console.WriteLine(Encrypt(text, key));
// output: Hg1qjUsdwNM=
PHP:
$key = "234576385746hfgr";
$text = "hello";
echo encryptData($key, $text);
// output: SRdeQHyKJF8=
How to get the same encryption text in php7+, we want convert C# code to PHP!
In the C# code, the mode is not explicitly specified, so that the CBC mode is used by default. In the PHP code, however, the ECB mode is applied, which must be changed into the CBC mode.
In the C# code newEncKey is used as key, which is 16 bytes in size and is implicitly extended to 24 bytes (3 times the TripleDES block size of 8 bytes) by appending the first 8 bytes at the end (this corresponds to 2TDEA). This must be done explicitly in the PHP code. Also remove the MD5 digest, as it's not used in C# code:
$byte = mb_convert_encoding($key, 'ASCII');
//$desKey = md5(utf8_encode($byte), true);
$desKey = $byte . substr($byte, 0, 8);
In the C# code newEncKey is also used as IV, of which implicitly only the first 8 bytes are applied (1 times the TripleDES block size). In the PHP code the shortening of the IV must be done explicitely, e.g. with
$desIV = substr($byte, 0, 8);
The IV must be passed as 5th parameter in mcrypt_encrypt:
$encData = mcrypt_encrypt('tripledes', $desKey, $data, 'cbc', $desIV);
The C# code uses PKCS7 padding by default, so this is consistent with the padding of the PHP code.
With these changes, the PHP code produces the same result as the C# code.
For security reasons, key/IV pairs may not be used more than once. The logic used here (generation of the IV from the key) therefore requires a new key for each encryption. Furthermore mcrypt_encrypt is deprecated, a better alternative is openssl_encrypt, which also uses PKCS7 padding by default.
Related
I am trying to convert c# application into php but I stuck at a place where C# provides Security class for Encryption and decryption based on RIJNDAEL algo. I am trying to convert into php.
Note: I am Using php 7.2 so mcrypt is deprecated for this version.
C# code
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace pharmarackencryption
{
class Program
{
private const string initVector = "aw90rela942f65u2";
// This constant is used to determine the keysize of the encryption algorithm.
private const int keysize = 256;
public static string Encrypt(string plainText, string passPhrase = "testing")
{
byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
public static string Decrypt(string cipherText, string passPhrase = "testing")
{
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
static void Main(string[] args)
{
Program p = new Program();
string enc_password = Encrypt("437217");
string dec_password = Decrypt("C9xJGa03dRQx9ePm0nLnHg==");
Console.WriteLine(enc_password);
Console.WriteLine(dec_password);
}
}
}
Encryption : C9xJGa03dRQx9ePm0nLnHg==
I found some what similar code in php like
PHP code:
<?php
// key/iv in ASCII binary data, $str base64
function decrypt_stuff($key, $str, $iv) {
// $plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($str), MCRYPT_MODE_CBC, $iv);
$plaintext_dec = openssl_decrypt(base64_decode($str), "aes-256-cbc", $key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);
return $plaintext_dec;
}
// key/iv in ascii binary data, $str ascii
function encrypt_stuff($key, $str, $iv) {
// $ciphertext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $str, MCRYPT_MODE_CBC, $iv));
if (($l = (strlen($str) & 15)) > 0) { $str .= str_repeat(chr(0), 16 - $l); }
$ciphertext = base64_encode(openssl_encrypt($str, "aes-256-cbc", $key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv));
return $ciphertext;
}
echo encrypt_stuff("testing","437217","aw90rela942f65u2");
//Result : LTbhEHjFgfa5PDJQXJEdKQ==
Both are going same thing but still result is different
Your PHP code produces different results because you're not using the same key. In your C# code you're using PasswordDeriveBytes to create the key, which is Microsoft's implementation of PBKDF1.
PHP doesn't support PBKDF1, but we could write a function that is compatible with C#. The code below is inspired from this great answer by Maarten Bodewes, translated to PHP.
function passwordDeriveBytes($password, $salt, $iterations = 100, $len = 32) {
$key = $password . $salt;
for($i = 0; $i < $iterations; $i++) {
$key = sha1($key, true);
}
if (strlen($key) < $len) {
$hx = passwordDeriveBytes($password, $salt, $iterations - 1, 20);
$counter = 0;
while (strlen($key) < $len) {
$counter += 1;
$key .= sha1($counter . $hx, true);
}
}
return substr($key, 0, $len);
}
Also, you're bese64 encoding and padding your data manually. You're preforming zero byte padding, but in your C# code you're using PKCS7 (the default and preferred) padding. It's best to let openssl pad and encode your data.
function encrypt_stuff($key, $str, $iv) {
return openssl_encrypt($str, "aes-256-cbc", $key, 0, $iv);
}
function decrypt_stuff($key, $str, $iv) {
return openssl_decrypt($str, "aes-256-cbc", $key, 0, $iv);
}
Using the key derived from passwordDeriveBytes, this PHP code produces the same results as your C# code.
$key = passwordDeriveBytes("testing", null);
$enc = encrypt_stuff($key,"437217","aw90rela942f65u2");
echo $enc;
//C9xJGa03dRQx9ePm0nLnHg==
However, I don't recommend using this code for the following reasons.
It's best to use PBKDF2 for your key. You can use Rfc2898DeriveBytes in C#:
Rfc2898DeriveBytes kdf = new Rfc2898DeriveBytes(password, salt, iterations);
byte[] key = kdf.GetBytes(32);
and hash_pbkdf2 in PHP:
$key = hash_pbkdf2("sha1", $password, $salt, $iterations, 32, true);
The salt should be at least 8 bytes long, and the number of iterations should be at least 10,000.
You're not using a salt. You should use a random salt for each password, it makes your keys stronger.
You're using a static IV. The IV should be unique and unpredictable. You can create a random IV with RNGCryptoServiceProvider:
byte[] iv = new byte[16];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(iv);
and openssl_random_pseudo_bytes:
$iv = openssl_random_pseudo_bytes(16);
You could use this code for the salt too.
You're not using authenticated encryption. If you're using PHP 7 you could use GCM, but unfortunately .NET doesn't provide any AEAD algorithms. However, you could use bouncycastle, if you choose to use authenticated encryption.
But your encryption code should produce different results every time you use it, because you should be using a random IV. You can decrypt the ciphertext correctly if you have the IV. The IV doesn't have to be secret; you could store it next to the ciphertext.
If you were using mcrypt with MCRYPT_RIJNDAEL_256, you could still make your PHP code compatible with C#, as RijndaelManaged supports 256 bit block size.
symmetricKey.BlockSize = 256;
However you shouldn't use mcrypt because it's not maintained, and it's deprecated in PHP 7.
I am not familiar with the C# end of this question. However I will point out some information that may be relevant to your problem.
The descriptions in the RIJNDAEL functions refer to the block size, not the key size. For example MCRYPT_RIJNDAEL_256 states that you are using a block size of 256. It is not specifying a key size.
For the openssl AES functions you are specifying a key size. As an example aes-256-cbc specifies you are using a 256 bit key.
The block size for all the AES functions is a 128 bit. So you can use MCRYPT_RIJNDAEL_128 which matches the AES block size. You can use a 256 bit key with both the MCRYPT_RIJNDAEL_128 and the aes-256-cbc as they are both using the 128 bit block size.
So for these reasons aes-256-cbc can not be used with MCRYPT_RIJNDAEL_256. They simply are not the same thing.
This one is redundant. If you are using the aes-256-cbcyou need to make sure that you are using a 256 bit key. Obviously make sure that your are using the same key to encrypt and decrypt. Make sure your IV is the correct IV. I would make them both static for testing. Once you get it working tinker around with adding the IV to the cipher string on encrypt and separating the IV from the cipher text on decrypt
Use openssl_random_pseudo_bytes() to generate your 256 bit(32 byte)key. You can also use openssl_random_pseudo_bytes() to generate your IV.
On a side note. I would highly recommend using the LibSodium library. It is now native on the latest versions of PHP and has a C# library as well. You can find it on Github easy enough.
Once you get this going I would look at learning how to Authenticate/Verify your encryption. Here is a good starting link for that. Authentication Read
Hope that helps.
It is not recommended to store passwords at the database using an reversible algo, passwords should be stored using expensive hashes. You should consider switching your password storage to a hash like Argon2.
Check this: http://php.net/manual/en/function.password-hash.php
mcrypt has been moved. not removed. you may try installing this
sudo apt-get -y install gcc make autoconf libc-dev pkg-config
sudo apt-get -y install php7.2-dev
sudo apt-get -y install libmcrypt-dev
sudo pecl install mcrypt-1.0.1
PS: not tested
I'm trying to encrypt and decrypt the string using objective c and C#. both are working fine in native code, but when I was try to decrypt string in c# was encrypted in iOS. I get some error.
This was the code I used in the objective c
- (NSData *)AES256EncryptWithKey:(NSString *)key Data: (NSData *) data
{
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [data length];
NSData *iv = [#"abcdefghijklmnopqrstuvwxyz123456" dataUsingEncoding:NSUTF8StringEncoding];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
[iv bytes] /* initialization vector (optional) */,
[data bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess)
{
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
In want to know how to decrypt in C#, I give blocksize is 256, ivsize to 32 and used "RijndaelManaged()". I'm not using salt & password.
Error: something like "Padding is invalid and cannot be removed."
I tried to set padding too like PKCS7, none, zero but nothing help to decrypt.
can any one help this?
Edit:
My C# code here
public string DecryptString(string encrypted)
{
string result = null;
_encoder = new UTF8Encoding();
if (!string.IsNullOrWhiteSpace(encrypted) && (encrypted.Length >= 32))
{
var messageBytes = Convert.FromBase64String(encrypted);
using (var rm = new RijndaelManaged())
{
rm.BlockSize = _blockSize;
rm.Key = _encoder.GetBytes("mykey_here");
rm.IV = _encoder.GetBytes("abcdefghijklmnopqrstuvwxyz123456"); ;
rm.Padding = PaddingMode.Zeros;
var decryptor = rm.CreateDecryptor(rm.Key, messageBytes.Take(_ivSize).ToArray());
result = _encoder.GetString(Transform(messageBytes.Skip(_ivSize).ToArray(), decryptor));
}
}
return result;
}
protected byte[] Transform(byte[] buffer, ICryptoTransform transform)
{
byte[] result;
using (var stream = new MemoryStream())
using (var cs = new CryptoStream(stream, transform, CryptoStreamMode.Write))
{
cs.Write(buffer, 0, buffer.Length);
cs.FlushFinalBlock();
result = stream.ToArray();
}
return result;
}
iOS (Common Crypto) explicitly specifies all encryption parameters, the C# code implicitly determines many parameters. These implicit parameters while simplifying usage are problematic when trying to achieve interoperability.
The C# class RijndaelManaged allows explicitly specifying parameter, change your code to use these, in particular BlockSize (128), KeySize (128), Mode (CipherMode.CBC) and Padding (PaddingMode.PKCS7). The defaults for mode and Padding are OK. See RijndaelManaged Documentation
AES and Rijndael are not the same, in particular AES uses only a block size of 128 bits (16 bytes) and Rijndael allows several block sizes. So one needs to specify a block size of 128 bits for Rijndael. Thus the iv is also 128 bits (16 bytes).
Both support encryption keys of 128, 192 and 256 bytes.
You would probably be better off using the AESManaged class than the RijndaelManaged class. See AesManaged Documentation
The C# side expects the data to be Base64 encoded, the iOS side does not show that encoding operation, make sure that is being done on the iOS side.
Since you are using an iv make sure you are using CBC mode on both sides. In Common Crypto CBC mode is the default, make sure CBC mode is being used on the C# side.
Make sure the C# side is using PKCS#7 or PKCS#5 padding, they are equivalent. It appears that PKCS#7 is the default on the C# side so this should be OK.
It is best to use a key of exactly the size specified and not rely on default padding. In Common Crypto the key size is explicitly specified and null padded if the supplied key is to short. The C# looks like it is determining the key size by the supplied key, in this case the key is 10 bytes so the decryption key probably defaults to 128 bits and the key is being internally padded with nulls. On iOS you are explicitly specifying a key size of 256 bits. This is a mis-match that needs to be fixed. Supply a key that is the exact size specified on the iOS side.
Finally there is the iv, the C# code expects the iv to be prepended to the encrypted data but the iOS code is not providing that. The solution is to change the iOS code to prepend the iv to the encrypted code. Change the iv to be 16 bytes, the AES block size.
Finally provide hex dumps of the test data in, data out, iv and key just prior to and after the encryption call if you need more help.
I am trying to encrypt a string in C++ with Crypto++ lib in a Qt project and decrypt the same in C# in a web application. Here is my code.
C++ Code, using Crypto++ lib
std::string Crypter::encrypt(const std::string& str_in, const std::string& key, const std::string& iv)
{
std::string str_out;
CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption encryption((byte*)key.c_str(), key.length(), (byte*)iv.c_str());
qDebug() << encryption.DefaultKeyLength();
qDebug() << encryption.DefaultIVLength();
CryptoPP::StringSource encryptor(str_in, true,
new CryptoPP::StreamTransformationFilter(encryption,
new CryptoPP::Base64Encoder(
new CryptoPP::StringSink(str_out),
false // do not append a newline
)
)
);
return str_out;
}
Calling the function here
std::string str = "123456789012345";
std::string key = "01234567891234560123456789123456"; // 32 bytes
std::string iv = "0123456789123456"; // 16 bytes
std::string str_encrypted = c->encrypt(str, key, iv);
std::string str_decrypted = c->decrypt(str_encrypted, key, iv);
std::cout << "str_encrypted: " << str_encrypted << std::endl;
std::cout << "str_decrypted: " << str_decrypted << std::endl;
This code produces following result
Plain text: "123456789012345"
Encrypted value (base64): 3Qo/6hWctRiID3txA9nC
The same code I have written in C# here
private void button1_Click(object sender, EventArgs e)
{
string strOutput = Encrypt("123456789012345");
Debug.WriteLine("Encrypted value is: " + strOutput);
}
private string Encrypt(string clearText)
{
byte[] clearBytes = Encoding.ASCII.GetBytes(clearText + "\0");
using (Aes encryptor = Aes.Create("AES"))
{
encryptor.BlockSize = 128;
encryptor.KeySize = 128;
encryptor.Mode = CipherMode.CFB;
encryptor.Key = Encoding.ASCII.GetBytes("01234567891234560123456789123456");
encryptor.IV = Encoding.ASCII.GetBytes("0123456789123456");
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
byte[] bt = ms.ToArray();
clearText = Convert.ToBase64String(bt);
}
}
return clearText;
}
Which produces following result
Encrypted value is: 3YklwM2vG20ZmkOT029jTTL7FlSZHrh0RfvaT1FFa2k=
Can someone please suggest me what am I missing ? What is the correct way to get similar output from both languages.
My objective here is to encrypt a value in C++ and decrypt the same in C#.
Edit
I did certain changes.
Replaced Hello world with 123456789012345
Changed the encoding from utf to Ascii
Added a null byte at the end of C# string
Change the mode to CFB
I have also edited the original result with the new result
Unfortunately, after doing this also, both the strings are not matching.
I have ensured that both the inputs are same.
Your C++ code is in terms of std::string. That is most likely holding text encoded under an ANSI code page. When you pass it into that CryptoPP::StringSource I expect it works upon the bytes of that text directly without transforming it to any other encoding.
Your C# is passing the result of Encoding.Unicode.GetBytes. That means the encryption is working upon the bytes of UTF-16 encoded data.
Since the encodings are differerent, the byte representations are different. Then since the bytes are different, the encrypted result is different.
You need to get both pieces of code working under the same scheme.
If ANSI (or even just ASCII) characters are all that you want to deal with (which is probably the case given your C++ code), then you could modify the C# code to use Encoding.Default.GetBytes (or possibly Encoding.ASCII.GetBytes) to get the bytes of the clearText.
EDIT
Looking further, your C++ code is using CryptoPP::CFB_Mode while your C# code is using encryptor.Mode = CipherMode.CBC;. Those modes need to match otherwise the algorithm will be applied differently.
You may need to go over other properties, such as padding, to ensure both are working under the same scheme.
There appear to be two underlying issues. The following code will produce the same output as the CryptoCC library (3Qo/6hWctRiID3txA9nC):
byte[] clearBytes = Encoding.ASCII.GetBytes(clearText);
using (var encryptor = RijndaelManaged.Create())
{
encryptor.KeySize = 128;
encryptor.Padding = PaddingMode.Zeros;
encryptor.Mode = CipherMode.CFB;
encryptor.Key = Encoding.ASCII.GetBytes("01234567891234560123456789123456");
encryptor.IV = Encoding.ASCII.GetBytes("0123456789123456");
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
Array.Copy(ms.ToArray(), clearBytes, clearBytes.Length);
clearText = Convert.ToBase64String(clearBytes);
}
}
return clearText;
Likewise, the following Crypto++ implementation will provide the value .NET returned in your example (3YklwM2vG20ZmkOT029j).
std::string encrypt(const std::string& str_in, const std::string& key, const std::string& iv)
{
std::string str_out;
CryptoPP::AES::Encryption e1((byte*)key.c_str(), key.length());
// use feedback size of 1 byte.
CryptoPP::CFB_Mode_ExternalCipher::Encryption encryption(e1, (byte*)iv.c_str(), 1);
CryptoPP::StringSource encryptor(str_in, true,
new CryptoPP::StreamTransformationFilter(encryption,
new CryptoPP::Base64Encoder(
new CryptoPP::StringSink(str_out),
false // do not append a newline
)
)
);
return str_out;
}
A few notes:
It's not necessary to append a trailing zero to the string.
The Crypto++ implementation does not allow padding in Cipher Feedback (CFB) mode. The .NET implementation requires padding; however, the excess data can be truncated manually (as is done in the .NET example above). (See http://social.msdn.microsoft.com/Forums/vstudio/en-US/a1be5f49-5f0f-4f5f-b01c-af46fdc71915/des-encryption-cfb-mode).
See this post on the implications of using AES in place of Rijndael as the CSP. In particular, the following warning applies to CFB mode:
Essentially, if you want to use RijndaelManaged as AES you need to make sure that:
The block size is set to 128 bits
You are not using CFB mode, or if you are the feedback size is also 128 bits
In this case, using CFB mode introduces additional complications. Note that this is a consequence of using CFB; if you use Cipher Block Chaining (CBC) mode, both Aes and Rijndael return the same result as Crypto++ for the given key and value (IwffxivpwdSuS9BV0KeyCg==).
PHP code
define('SECRET', 'Your key here');
$data = 'test';
$enc = mcrypt_cbc(MCRYPT_TRIPLEDES, SECRET, $data, MCRYPT_ENCRYPT, '12345678');
$url .= urlencode($password);
C# code
byte[] key = ec.GetBytes("Your key here");
byte[] iv = ec.GetBytes("12345678");
byte[] data = ec.GetBytes("test");
byte[] enc = new byte[0];
TripleDES tdes = TripleDES.Create();
tdes.IV = iv;
tdes.Key = key;
tdes.Mode = CipherMode.CBC;
tdes.Padding = PaddingMode.Zeros;
ICryptoTransform ict = tdes.CreateEncryptor();
enc = ict.TransformFinalBlock(data, 0, data.Length);
string szEnc = HttpContext.Current.Server.UrlEncode(
Encoding.ASCII.GetString(enc)
);
My problem: The value of $url in PHP and szEnc in c# is not same.
Question: what wrong in my c# code?
A lot of things can go wrong - but I've seen quite a lot of encoding (i.e. non cryptographic) issue when dealing with string and byte[].
Never assume they will convert into anything, including ASCII.
Encoding.ASCII.GetString(enc)
If you have unprintable characters, NUL... then this will not be part of the returned string and won't be url-encoded. This is ask true for PHP but it does not means it follows the same rule in every case.
Also I can't tell you what code like:
ec.GetBytes("Your key here");
will do ?!? If you're using an Unicode encoder then it won't give you the same as an ASCII encoder.
Beside encoding also check that the PaddingMode you use match the one used by PHP.
Ok I'm trying to use the Win32 Crypto API in C++ to decrypt a string encrypted in C# (.NET 2) with the RijndaelManaged Class. But I'm having no luck at all i get jibberish or a bad data Win32 error code. All my keys, IV and salt match, I've looked in the watch for both test apps. I've spent all say looking at it and I'm officialy stuck.
Anyway here is the C#
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(GetPassPhrase(), salt, 1000);
RijndaelManaged rijndael = new RijndaelManaged();
rijndael.BlockSize = 128;
rijndael.KeySize = 256;
rijndael.Mode = CipherMode.CBC;
rijndael.Key = pdb.GetBytes(m_KeySize);
rijndael.IV = GetIV(iv);
ICryptoTransform encryptor = rijndael.CreateEncryptor();
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
Byte[] encryptedBytes = null;
Byte[] toBeEncrypted = UnicodeEncoding.Unicode.GetBytes(value);
csEncrypt.Write(toBeEncrypted, 0, toBeEncrypted.Length);
csEncrypt.FlushFinalBlock();
encryptedBytes = msEncrypt.ToArray();
The C++ to decrypt it is:
keyBlob.hdr.bType = PLAINTEXTKEYBLOB;
keyBlob.hdr.bVersion = CUR_BLOB_VERSION;
keyBlob.hdr.reserved = 0;
keyBlob.hdr.aiKeyAlg = CALG_AES_256;
keyBlob.cbKeySize = KEY_SIZE;
keyBlob.rgbKeyData = &byKey[0];
if ( CryptImportKey( hProv, (const LPBYTE) &keyBlob, sizeof(BLOBHEADER) + sizeof(DWORD) + KEY_SIZE, 0, CRYPT_EXPORTABLE, &hKey ) )
{
if ( CryptSetKeyParam( hKey, KP_IV, (const BYTE *) &byIV, 0))
{
DWORD dwLen = iDestLen;
if ( CryptDecrypt( hKey, 0, TRUE, 0, pbyData, &dwLen))
{
if ( dwLen < (DWORD) *plOutSize)
{
memcpy_s(pbyOutput, *plOutSize, pbyData, dwLen);
*plOutSize = dwLen;
bRet = TRUE;
}
}
else
{
// Log
DWORD dwErr = ::GetLastError();
int y =0;
}
}
}
I'm calling CryptAcquireContext successfully and my C++ is executing fine. Can anyone spot the error in my ways. It's starting to depress me know :(
Ok my fault, I didn't include the Struct def for the keyblob in the C++ and it turns out you need a contigous block of data for the key with the header but I was using the MSDN example that had a pointer to the key data. Which is wrong!
I see that you are using CBC chaining mode to encrypt the plain text.
Are you sure you are using the same chaining mode to decrypt the cypher text?
(I am sorry. I am not able to understand that from the code)
There are a handful of things you should check, since some of the code (declarations etc) are missing:
Block size - this usually should be the same as key size, I think it might even be the default since you dont specify it on C++ side. Set it to 256 on C# side, I guess it best that you explicitly specify it in C++ too.
Padding - the managed classes have PKCS7 as their default padding, I think its the default for cryptoAPI functions too, but I'm not sure.
I assume that GetPassPhrase, GetIV etc give you he same keys you're using on the C++ side?
It's not clear how the encrypted data is passed between the programs, is it possible there is some kind of translation error? E.g. base64, URL encode, etc.