C# with AES256 and SHA512 file encryption/decrption - c#

I was working a while ago with AHK (Autohotkey) and I found the crypt.ahk is handy in encrypting/decrypting files. I also decided to use an AES256 with SHA512 and a "Password".
Here you can see a short code from the crypto.ahk file:
if !dllCall("Advapi32\CryptAcquireContextW"
,"Ptr*",hCryptProv ; pointer to a handle of a CSP
,"Uint",0 ; key container name
,"Uint",0 ; 0 = default provider
,"Uint",c.PROV_RSA_AES ; AES type of provider to acquire => 24
,"UInt",c.CRYPT_VERIFYCONTEXT) ; 0xF0000000 <= This option is intended for applications that are using ephemeral keys, or applications that do not require access to persisted private keys, such as applications that perform only hashing, encryption, and digital signature verification.
if !dllCall("Advapi32\CryptCreateHash"
,"Ptr",hCryptProv ; handle to a CSP created by a call to CryptAcquireContext
,"Uint",CUR_PWD_HASH_ALG ; HASH algorithm to use ALG_ID: HashAlg==6 || HashAlg = "SHA512" ?c.CALG_SHA_512
; CALG_SHA_512 := (CryptConst.ALG_CLASS_HASH | CryptConst.ALG_TYPE_ANY | CryptConst.ALG_SID_SHA_512)
; 4<<13 = hex 8000 | 0 | 14 dec => 0x800E = 512 bit sha hashing algorithm
,"Uint",0 ; MAC / HMAC --> For nonkeyed algorithms, this parameter must be set to zero.
,"Uint",0 ; this flag is not used
,"Ptr*",hHash ) ; The address to which the function copies a handle to the new hash object.
;hashing password
passLen := StrPutVar(password, passBuf,0,this.PassEncoding)
if !dllCall("Advapi32\CryptHashData"
,"Ptr",hHash ; Handle of the hash object.
,"Ptr",&passBuf ; "thisIsMyPassword" in Unicode - pointer to a buffer that contains the data to be added to the hash object.
,"Uint",passLen ; 34 - Number of bytes of data to be added.
,"Uint",0 ) ; no flags are used
;getting encryption key from password
if !dllCall("Advapi32\CryptDeriveKey"
,"Ptr",hCryptProv ; A HCRYPTPROV handle of a CSP created by a call to CryptAcquireContext.
,"Uint",CUR_ENC_ALG ; An ALG_ID structure that identifies the symmetric encryption algorithm for which the key is to be generated
,"Ptr",hHash ; A handle to a hash object that has been fed the exact base data.
,"Uint",KEY_LENGHT ; 256 keylength - Specifies the type of key generated. lower 16 bit == 0 -> no flags set upper 16 bit => 256
,"Ptr*",hKey ) ; A pointer to a HCRYPTKEY variable to receive the address of the handle of the newly generated key.
if !dllCall("Advapi32\CryptGetKeyParam"
,"Ptr",hKey ; The handle of the key being queried.
,"Uint",c.KP_BLOCKLEN ; Specifies the type of query being made. For all key types, this parameter can contain one of the following values. If a session key is specified by the hKey parameter, retrieve the block length of the key cipher. The pbData parameter is a pointer to a DWORD value that receives the block length, in bits. For stream ciphers, this value is always zero.
,"Uint*",BlockLen ; A pointer to a buffer that receives the data. The form of this data depends on the value of dwParam.
,"Uint*",dwCount := 4 ; A pointer to a DWORD value that, on entry, contains the size, in bytes, of the buffer pointed to by the pbData parameter. When the function returns, the DWORD value contains the number of bytes stored in the buffer.
,"Uint",0) ; nothing
{foo := "CryptGetKeyParam", err := GetLastError(), err2 := ErrorLevel
GoTO FINITA_LA_COMEDIA
}
if !dllCall(CryptEnc
,"Ptr",hKey ;key ; A handle to the encryption key.
,"Ptr",0 ;hash ; If no hash is to be done, this parameter must be NULL.???
,"Uint",isFinal ;final ; A Boolean value that specifies whether this is the last section in a series being encrypted. Final is set to TRUE for the last or only block
,"Uint",0 ;dwFlags ; no flags
,"Ptr",&ReadBuf ;pbdata ; A pointer to a buffer that contains the plaintext to be encrypted. The plaintext in this buffer is overwritten with the ciphertext created by this function.
,"Uint*",BytesRead ;dwsize ; A pointer to a DWORD value that , on entry, contains the length, in bytes, of the plaintext in the pbData buffer. On exit, this DWORD contains the length, in bytes, of the ciphertext written to the pbData buffer.
,"Uint",ReadBufSize+BlockLen ) ;dwbuf ; Specifies the total size, in bytes, of the input pbData buffer. Note that, depending on the algorithm used, the encrypted text can be larger than the original plaintext. In this case, the pbData buffer needs to be large enough to contain the encrypted text and any padding.
I worked along with many Microsoft Docs and I could say, I have a little understanding. At least, the self-written comments (after the ;) seem to be okay.
create a handle for a CSP with AES
hash object gets the CALG_SHA_512
hash the Password "thisIsMyPassword"
put the hashing and aes together
crypt the file
Now I want to write a C# using System.Security.Cryptography; Programm.
But I can't manage to decrypt the file with AES or RijndaelManaged, because I can't get the SHA512 into my AES256. I always get an error stating that the Size is too big.
I tried it with .Net5.0 and .Net4.8 but no luck.
public static void FileDecrypt(string inputFile, string outputFile, string password)
{
// https://foxlearn.com/windows-forms/how-to-encrypt-and-decrypt-files-using-aes-encryption-algorithm-in-csharp-396.html
using (FileStream fsCrypt = new FileStream(inputFile, FileMode.Open))
{
byte[] passwordBytes = System.Text.Encoding.Unicode.GetBytes(password);
byte[] aesKey = SHA512.Create().ComputeHash(passwordBytes);
byte[] aesIV = { 0 }; // iV is zero
Aes trueAES = Aes.Create();
trueAES.BlockSize = 256;
trueAES.KeySize = 512;
trueAES.Mode = CipherMode.CBC;
trueAES.Padding = PaddingMode.None;
using (CryptoStream cryptoStream = new CryptoStream(fsCrypt, trueAES.CreateDecryptor(), CryptoStreamMode.Read))
{
using (FileStream fsOut = new FileStream(outputFile, FileMode.Create))
{
int read;
byte[] buffer = new byte[10240]; // [1048576];
while ((read = cryptoStream.Read(buffer, 0, buffer.Length)) > 0)
{
fsOut.Write(buffer, 0, read);
}
}
}
I already have found a solution on how to translate the advapi32.dll to C#, but the crypto stuff seems to be deprecated. See MS Site:
This API is deprecated. New and existing software should start using
Cryptography Next Generation APIs. Microsoft may remove this API in
future releases.
Could someone help me out with my code?
Update and solution - 05/09/22
Thanks to Richard I was able to decrypt in c# the previous encrypted advapi32.dll file. My code is now:
public static void FileDecrypt(string inputFile, string outputFile, string password)
{
// https://foxlearn.com/windows-forms/how-to-encrypt-and-decrypt-files-using-aes-encryption-algorithm-in-csharp-396.html
using (FileStream fsCrypt = new FileStream(inputFile, FileMode.Open))
{
byte[] passwordBytes = System.Text.Encoding.Unicode.GetBytes(password);
byte[] aesKey = SHA512.Create().ComputeHash(passwordBytes);
// shorten the 512 bit (64 byte) to 256bit (32 byte)
// just take the first 32 byte of the sha512 key
byte[] shortingAesKey = aesKey.Take(32).ToArray();
trueAES.Key = shortingAesKey;
byte[] aesIV = { 0 }; // iV is zero
Aes trueAES = Aes.Create();
trueAES.BlockSize = 256;
trueAES.KeySize = 512;
trueAES.Mode = CipherMode.CBC;
trueAES.Padding = PaddingMode.None;
using (CryptoStream cryptoStream = new CryptoStream(fsCrypt, trueAES.CreateDecryptor(), CryptoStreamMode.Read))
{
using (FileStream fsOut = new FileStream(outputFile, FileMode.Create))
{
int read;
byte[] buffer = new byte[10240]; // [1048576];
while ((read = cryptoStream.Read(buffer, 0, buffer.Length)) > 0)
{
fsOut.Write(buffer, 0, read);
}
}
}

Related

openssl returns Bad Magic Number [duplicate]

This question already has an answer here:
C# version of OpenSSL EVP_BytesToKey method?
(1 answer)
Closed 6 years ago.
In my Linux machine i have a binary AES encrypted file:
head -c 100 Leela_Turanga.plr
�|�XѨ��>��c��N�Ώڤ�LW�M��t�p5=c.4���ᑸ�#Owl����M�d��>�ٷa�L�r|��ć�ڐ,��:����#�����\
Also i know that the file has been encryped using the password:
h3y_gUyZ
I am able to decrypt this file using .NET's RijndaelManaged class in the following routine:
public static bool decryptFile(string inputFile, string outputFile)
{
string s = "h3y_gUyZ";
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
byte[] bytes = unicodeEncoding.GetBytes(s);
FileStream fileStream = new FileStream(inputFile, FileMode.Open);
RijndaelManaged rijndaelManaged = new RijndaelManaged();
CryptoStream cryptoStream = new CryptoStream(fileStream, rijndaelManaged.CreateDecryptor(bytes, bytes), CryptoStreamMode.Read);
FileStream fileStream2 = new FileStream(outputFile, FileMode.Create);
try
{
int num;
while ((num = cryptoStream.ReadByte()) != -1)
{
fileStream2.WriteByte((byte)num);
}
fileStream2.Close();
cryptoStream.Close();
fileStream.Close();
}
catch
{
fileStream2.Close();
fileStream.Close();
File.Delete(outputFile);
return true;
}
return false;
}
Since im on Linux I embedded this code in a c# program which i run with mono, after i compiled it with mcs.
mcs *.cs -out:mybinary.exe
mono mybinary.exe d Leela_Turanga.plr outputfile.dat
Where the d parameter executes the aforementioned function, Leela_Turanga.plr is the file to decrypt and outputfile.dat is the resulting decryped file.
This works fine: I can decrypt the file correctly, and i am able to say this because human readable text appears in the decrypted file.
Now i want to decrypt the same file with openssl.
First of all i need to get the algorithm parameters, and since the code above works, I modified it to give me these informations:
Key Size
AES operation mode (cbc, ecb, pcbc, cfb...)
Padding (have no idea what that is)
by adding some code:
public static bool decryptFile(string inputFile, string outputFile)
{
string s = "h3y_gUyZ";
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
byte[] bytes = unicodeEncoding.GetBytes(s);
FileStream fileStream = new FileStream(inputFile, FileMode.Open);
RijndaelManaged rijndaelManaged = new RijndaelManaged();
CryptoStream cryptoStream = new CryptoStream(fileStream, rijndaelManaged.CreateDecryptor(bytes, bytes), CryptoStreamMode.Read);
FileStream fileStream2 = new FileStream(outputFile, FileMode.Create);
//=======DEBUG INFO=======
//PRINT ALGORITHM SETTINGS
Console.WriteLine(rijndaelManaged.Mode); //what AES mode are we using?
Console.WriteLine(rijndaelManaged.KeySize); //what is the keysize?
Console.WriteLine(rijndaelManaged.Padding); //what is the padding?
//========================
try
{
int num;
while ((num = cryptoStream.ReadByte()) != -1)
{
fileStream2.WriteByte((byte)num);
}
fileStream2.Close();
cryptoStream.Close();
fileStream.Close();
}
catch
{
fileStream2.Close();
fileStream.Close();
File.Delete(outputFile);
return true;
}
return false;
}
What it comes out is that RijndaelManaged is working in CBC mode, with a 256 bit key and PKCS7 Padding (again no idea if that is important).
Now i can try using openssl to decrypt:
openssl enc -aes-256-cbc -d -in Leela_Turanga.plr -out out.bin
Then the password prompt appears, I enter the pass, and a "bad magic number" error is returned"
enter aes-256-cbc decryption password:
bad magic number
And i get no decrypted file.
Why does openssl say that? I have searched on the internet but I haven't found my answer.
Also:
Since in c# strings are encoded in UTF-16 (hence 16 bits in a char), and the key is "h3y_gUyZ" which are 8 chars, shouldn't the key be 16 x 8 = 128 bits wide? instead of the 256 rijndaelManaged.KeySize returns.
Why does openssl say that?
OpenSSL uses it's own key derivation routine called EVP_BytesToKey, which takes a salt. This salt is prefixed with a 8 byte magic: Salted__ in ASCII encoding. Passwords should not be directly used as keys, so OpenSSL converts them to keys first.
You can instead provide a key using -K and then the hexadecimal representation of your string (read on for the encoding). You will also need to provide the IV (in your case the same bytes).
Since in C# strings are encoded in UTF-16 (hence 16 bits in a char), and the key is "h3y_gUyZ" which are 8 chars, shouldn't the key be 16 x 8 = 128 bits wide instead of the 256 rijndaelManaged.KeySize returns?
Yes, probably you asked the Rijndael class for the key size before initializing it with the key. And note that .NET uses UTF-16LE (little-endian) as the whole ecosystem is in (stupid) little-endian.
PKCS#7 is a standard that contains a padding scheme. It is both used by .NET and OpenSSL, so that's fine. It is required as ECB and CBC mode require the input size of the cipher is N times the block size of the cipher. So PKCS#7 adds 1 to 16 bytes (the block size of AES) valued \x01 to \x10. Unpadding removes these.

How decrypt string in c# was encrypted in iOS using Rijndael

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.

interoperability of encryption between C++ and C# with Crypto

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==).

AES _Encryption in Mysql , Decryption in C#.Net

Mysql :
SELECT AES_ENCRYPT('Test','pass')
AES_ENCRYPT() and AES_DECRYPT() enable encryption and decryption of data using the official AES (Advanced Encryption Standard) algorithm, previously known as “Rijndael.” Encoding with a 128-bit key length is used, but you can extend it up to 256 bits by modifying the source. We chose 128 bits because it is much faster and it is secure enough for most purposes.
http://dev.mysql.com/doc/refman/5.5/en/encryption-functions.html#function_aes-encrypt
I was trying to convert that Encrypted string into Decryped Strig in C#.net but i don't get the results as i expect.
http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndael.aspx#Y0
C#
static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
In this method I pass ciphertext,Key value which i usedfrom Mysql and
Rijndael.Create().IV for byte[] IV
I use the code but i don't get expected result.
Review the code and comment Idk where made a mistake
What you are doing is following a road of pain. Either decrypt/encrypt on MySQL and use an encrypted connection to the database (if that matters) or encrypt/decrypt on your .NET application, storing the encrypted data in a suitable column.
Mixing AES implementations is prone to mistakes and things can break more easily if you change versions of .NET or MySQL.
Now, to know what exactly is wrong we need to know if the IV is compatible between MySQL and .NET, or else find out what is MySQL's implementation IV and supply that.
And the other potential source of problems is how you have generated the byte arrays (we are not seeing that in your example). You have to consider character encoding issues in generating the arrays if the key is textual.
In the comments of this MySQL docs link there is information about the missing parameters.
After a long hours, I found a solution to this issue.
Couple of FYI's:
MySQL as a default for AES_Encrypt uses 128 bit, with ECB mode, which does not require an IV.
What padding mode they use is not specified, but they do say they pad it. For padding I use PaddingMode.Zeros.
In C#, use AesManaged, not RijndaelManaged since that is not recommended anymore.
If your Key is longer than 128 bits (16 bytes), then use a function below to create the correct key size, since the default MySQL AES algorithm uses 128 bit keys.
Make sure you play around with the correct Encoding and know exactly what type of character encoding you will receive back when translating the bytes to characters.
For more info go here: https://forums.mysql.com/read.php?38,193084,195959#msg-195959
Code:
public static string DecryptAESStringFromBytes(byte[] encryptedText, byte[] key)
{
// Check arguments.
if ((encryptedText == null || encryptedText.Length <= 0) || (key == null || key.Length <= 0))
{
throw new ArgumentNullException("Missing arguments");
}
string decryptedText = null;
// Create an AES object with the specified key and IV.
using (AesManaged aesFactory = new AesManaged())
{
aesFactory.KeySize = 128;
aesFactory.Key = AESCreateKey(key, aesFactory.KeySize / 8);
aesFactory.IV = new byte[16];
aesFactory.BlockSize = 128;
aesFactory.Mode = CipherMode.ECB;
aesFactory.Padding = PaddingMode.Zeros;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesFactory.CreateDecryptor();
// Create the streams used for decryption.
using (MemoryStream stream = new MemoryStream())
{
using (CryptoStream decryptStream = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))
{
decryptStream.Write(encryptedText, 0, encryptedText.Length);
}
decryptedText = Encoding.Default.GetString(stream.ToArray());
}
}
return decryptedText.Trim();
}
public static byte[] AESCreateKey(byte[] key, int keyLength)
{
// Create the real key with the given key length.
byte[] realkey = new byte[keyLength];
// XOR each byte of the Key given with the real key until there's nothing left.
// This allows for keys longer than our Key Length and pads short keys to the required length.
for (int i = 0; i < key.Length; i++)
{
realkey[i % keyLength] ^= key[i];
}
return realkey;
}
Here is some working code for achieving the same encryption via C# as MySQL:
public byte[] AESEncrypt(byte[] plaintext, byte[] key) {
/*
* Block Length: 128bit
* Block Mode: ECB
* Data Padding: Padded by bytes which Asc() equal for number of padded bytes (done automagically)
* Key Padding: 0x00 padded to multiple of 16 bytes
* IV: None
*/
RijndaelManaged aes = new RijndaelManaged();
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Key = key;
ICryptoTransform encryptor = aes.CreateEncryptor();
MemoryStream mem = new MemoryStream();
CryptoStream cryptStream = new CryptoStream(mem, encryptor,
CryptoStreamMode.Write);
cryptStream.Write(plaintext, 0, plaintext.Length);
cryptStream.FlushFinalBlock();
byte[] cypher = mem.ToArray();
cryptStream.Close();
cryptStream = null;
encryptor.Dispose();
aes = null;
return cypher;
}
For details see MySQL Bug # 16713
EDIT:
Since the above is relying on officially non-documented information (though it is working) I would recommend to avoid it and use one of the options described in the answer from Vinko Vrsalovic .
If you run SELECT AES_ENCRYPT('Test','pass')
your are sending the pass over the network unencrypted so any one can unencrypted the data.
The AES_ENCRYPT is used to store data so if the database gets hacked your data is safe, not to transmit data.
if you want data encryption over the net work connect to your mysql server using the ssl socket

Decrypting RijndaelManaged Encrypted strings with CryptDecrypt

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.

Categories