Took the vectors from this site http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors#aes-ecb-128
In javascript (sjcl) have the same result
var key = [0x2b7e1516,0x28aed2a6,0xabf71588,0x09cf4f3c];
var test = [0x6bc1bee2,0x2e409f96,0xe93d7e11,0x7393172a];
aes = new sjcl.cipher.aes(key);
r = aes.encrypt(test);
console.log(r);
But I can not reach it in the C#
[TestMethod]
public void EncryptIntsToInts()
{
Int32[] key = { unchecked((Int32)0x2b7e1516), 0x28aed2a6, unchecked((Int32)0xabf71588), 0x09cf4f3c };
Int32[] test = { 0x6bc1bee2,0x2e409f96,unchecked((Int32)0xe93d7e11),0x7393172a };
Int32[] answer = { 0x3ad77bb4, 0x0d7a3660, unchecked((Int32)0xa89ecaf3), 0x2466ef97 };
var r = AES.EncryptIntsToInts(test, key.ToByteArray());
Assert.IsTrue(r.SequenceEqual(answer));
}
static byte[] zeroIV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
public static Int32[] EncryptIntsToInts(Int32[] input, byte[] key)
{
// Check arguments.
if (input == null || input.Length <= 0)
throw new ArgumentNullException("input");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("key");
// Declare the RijndaelManaged object
// used to encrypt the data.
RijndaelManaged aesAlg = null;
byte[] bResult;
try
{
aesAlg = new RijndaelManaged
{
Key = key,
Mode = CipherMode.ECB,
Padding = PaddingMode.None,
KeySize = 128,
BlockSize = 128,
IV = zeroIV
};
ICryptoTransform encryptor = aesAlg.CreateEncryptor();
byte[] bInput = new byte[input.Length * sizeof(int)];
Buffer.BlockCopy(input, 0, bInput, 0, bInput.Length);
bResult = encryptor.TransformFinalBlock(bInput, 0, input.Length);
}
finally
{
if (aesAlg != null)
aesAlg.Clear();
}
int[] iResult = new int[bResult.Length / sizeof(int)];
Buffer.BlockCopy(bResult, 0, iResult, 0, bResult.Length);
return iResult;
}
What is my error?
========================================================
Start edit
New code in which right order of the bytes, but it does not work
[TestMethod]
public void EncryptIntsToInts()
{
byte[] key = "2b7e151628aed2a6abf7158809cf4f3c".HEX2Bytes();
byte[] test = "6bc1bee22e409f96e93d7e117393172a".HEX2Bytes();
byte[] answer = "3ad77bb40d7a3660a89ecaf32466ef97".HEX2Bytes();
RijndaelManaged aesAlg = new RijndaelManaged
{
Key = key,
Mode = CipherMode.ECB,
Padding = PaddingMode.PKCS7,
KeySize = 128,
BlockSize = 128,
IV = zeroIV
};
ICryptoTransform encryptor = aesAlg.CreateEncryptor();
var r = encryptor.TransformFinalBlock(test, 0, test.Length);
Assert.IsTrue(r.SequenceEqual(answer));
}
public static byte[] HEX2Bytes(this string hex)
{
if (hex.Length%2 != 0)
{
throw new ArgumentException(String.Format(CultureInfo.InvariantCulture,
"The binary key cannot have an odd number of digits: {0}", hex));
}
byte[] HexAsBytes = new byte[hex.Length/2];
for (int index = 0; index < HexAsBytes.Length; index++)
{
string byteValue = hex.Substring(index*2, 2);
HexAsBytes[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
}
return HexAsBytes;
}
static byte[] zeroIV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
Right code (just add a try / using):
[TestMethod]
public void EncryptIntsToInts()
{
byte[] key = "2b7e151628aed2a6abf7158809cf4f3c".HEX2Bytes();
byte[] test = "6bc1bee22e409f96e93d7e117393172a".HEX2Bytes();
byte[] answer = "3ad77bb40d7a3660a89ecaf32466ef97".HEX2Bytes();
var r = AES.Encrypt(test, key);
Assert.IsTrue(answer.SequenceEqual(r));
}
public static byte[] Encrypt(byte[] input, byte[] key)
{
var aesAlg = new AesManaged
{
KeySize = 128,
Key = key,
BlockSize = 128,
Mode = CipherMode.ECB,
Padding = PaddingMode.Zeros,
IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
return encryptor.TransformFinalBlock(input, 0, input.Length);
}
You use 32 bit integers to define the key. When you transform them to bytes, you use native endianness, which typically is little endian. So your key is 16157e2b a6... and not 2b7e1516 28....
I wouldn't use ints to represent a key in the first place. But if you really want to, write a big endian conversion function.
I also strongly recommend against ECB mode. You could use CBC together with HMAC (in an encrypt then mac construction), or use a third party lib to implement GCM.
Related
I want calculate the MAC with S-MAC to sign the plain with single DES plus final triple DES in Secure Channel. I tried as follows but is not worked.
Can anyone help me? Thanks.
byte[] mac_iv = ToHexBytes("0000000000000000");
byte[] mac_key = ToHexBytes("C6713F31B8DC1F8905DFECB4065CB81E"); // S-MAC
byte[] mac_plain = BytesAppend(ToHexBytes("8482000010"), ToHexBytes("1122334455667788"));
byte[] mac_cipher = DES_MAC8_ISO9797_M2_ALG3_Encrypt(mac_iv, mac_key, mac_plain);
Debug.Print("\nmac_cipher: " + ToHexString(mac_cipher));
//
private byte[] DES_MAC8_ISO9797_M2_ALG3_Encrypt(byte[] iv, byte[] key, byte[] plain)
{
try
{
// split the 16 byte key into key A and key B
var key1 = new byte[8];
Buffer.BlockCopy(key, 0, key1, 0, key1.Length);
var key2 = new byte[8];
Buffer.BlockCopy(key, 8, key2, 0, key2.Length);
// init DES CBC encryption with key A and an all-zero IV of 8 bytes
DES des = new DESCryptoServiceProvider();
des.Mode = CipherMode.CBC;
des.Padding = PaddingMode.None;
MemoryStream streamOut = new MemoryStream();
CryptoStream streamCrypto = new CryptoStream(streamOut, des.CreateEncryptor(key1, iv), CryptoStreamMode.Write);
// iterate over all full blocks within the message & for each block perform CBC encryption,
// throwing away the result (using the same cipher instance, you need to keep the state after all)
int fullBlocks = plain.Length / 8;
for (int i = 0; i < fullBlocks; i++) {
int off = i * 8;
byte[] block = new byte[off + 8];
Buffer.BlockCopy(plain, off, block, off, block.Length);
streamCrypto.Write(block, 0, block.Length);
streamCrypto.FlushFinalBlock();
}
// create a final block and copy the left over bytes from the message into it
byte[] final_block = new byte[8];
int left = plain.Length % 8;
Buffer.BlockCopy(plain, left, final_block, left, final_block.Length);
// at the next position add the initial padding indicator byte
// ???
// finalize the CBC encryption by encrypting the final block, and keep the result
streamCrypto.Write(final_block, 0, final_block.Length);
streamCrypto.FlushFinalBlock();
byte[] res = streamOut.ToArray();
// perform DES ECB decryption over the result with key B, replacing the result
des.Mode = CipherMode.ECB;
streamCrypto = new CryptoStream(streamOut, des.CreateDecryptor(key2, iv), CryptoStreamMode.Write);
streamCrypto.Write(res, 0, res.Length);
res = streamOut.ToArray();
// peform DES ECB encryption over the result with key A, replacing the result
des.Mode = CipherMode.ECB;
streamCrypto = new CryptoStream(streamOut, des.CreateDecryptor(key1, iv), CryptoStreamMode.Write);
streamCrypto.Write(res, 0, res.Length);
res = streamOut.ToArray();
return res;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return new byte[0];
}
I resolved my problem with answer from #Jeff2022 in C# Implementation of Retail MAC Calculation (ISOIEC 9797-1 MAC algorithm 3) Thanks.
private static byte[] getCC_MACNbytes(string Key_MAC, byte[] eIFD, string Init_Vec)
{
byte[] Kmac = StringToByteArray(Key_MAC);
// Split the 16 byte MAC key into two keys
byte[] key1 = new byte[8];
Array.Copy(Kmac, 0, key1, 0, 8);
byte[] key2 = new byte[8];
Array.Copy(Kmac, 8, key2, 0, 8);
DES des1 = DES.Create();
des1.BlockSize = 64;
des1.Key = key1;
des1.Mode = CipherMode.CBC;
des1.Padding = PaddingMode.None;
des1.IV = new byte[8];
DES des2 = DES.Create();
des2.BlockSize = 64;
des2.Key = key2;
des2.Mode = CipherMode.CBC;
des2.Padding = PaddingMode.None;
des2.IV = new byte[8];
// Padd the data with Padding Method 2 (Bit Padding)
System.IO.MemoryStream out_Renamed = new System.IO.MemoryStream();
out_Renamed.Write(eIFD, 0, eIFD.Length);
out_Renamed.WriteByte((byte)(0x80));
while (out_Renamed.Length % 8 != 0)
{
out_Renamed.WriteByte((byte)0x00);
}
byte[] eIfd_padded = out_Renamed.ToArray();
int N_bytes = eIfd_padded.Length/8; // Number of Bytes
byte[] d1 = new byte[8];
byte[] dN = new byte[8];
byte[] hN = new byte[8];
byte[] intN = new byte[8];
// MAC Algorithm 3
// Initial Transformation 1
Array.Copy(eIfd_padded, 0, d1, 0, 8);
hN = des1.CreateEncryptor().TransformFinalBlock(d1, 0, 8);
// Split the blocks
// Iteration on the rest of blocks
for (int j = 1; j<N_bytes; j++)
{
Array.Copy(eIfd_padded, (8*j), dN, 0, 8);
// XOR
for (int i = 0; i < 8; i++)
intN[i] = (byte)(hN[i] ^ dN[i]);
// Encrypt
hN = des1.CreateEncryptor().TransformFinalBlock(intN, 0, 8);
}
// Output Transformation 3
byte[] hNdecrypt = des2.CreateDecryptor().TransformFinalBlock(hN, 0, 8);
byte[] mIfd = des1.CreateEncryptor().TransformFinalBlock(hNdecrypt, 0, 8);
// Get check Sum CC
return mIfd;
}
The C# code below outputs 0123456789012345678901234567890123456789:
static void Main(string[] args)
{
byte[] salt = Encoding.ASCII.GetBytes("saltycrack");
Console.WriteLine(Decrypt("NpN45cs2+bVLtEgp6ZmBmmHLH89aFdHIbDjhstTK0Vb1VMr7jBsliKD9siQGGxGS", "pass", salt));
}
public static string Decrypt(string encryptedText, string encryptionPassword, byte[] salt)
{
var algorithm = GetAlgorithm(encryptionPassword, salt);
using (ICryptoTransform decryptor = algorithm.CreateDecryptor(algorithm.Key, algorithm.IV))
{
byte[] encryptedBytes = Convert.FromBase64String(encryptedText);
return Encoding.UTF8.GetString(InMemoryCrypt(encryptedBytes, decryptor));
}
}
private static byte[] InMemoryCrypt(byte[] data, ICryptoTransform transform)
{
MemoryStream memory = new MemoryStream();
using (Stream stream = new CryptoStream(memory, transform, CryptoStreamMode.Write))
{
stream.Write(data, 0, data.Length);
}
return memory.ToArray();
}
private static RijndaelManaged GetAlgorithm(string encryptionPassword, byte[] salt)
{
var key = new Rfc2898DeriveBytes(encryptionPassword, salt);
var algorithm = new RijndaelManaged();
algorithm.Key = key.GetBytes(algorithm.KeySize / 8);
algorithm.IV = key.GetBytes(algorithm.BlockSize / 8);
return algorithm;
}
I want to do the same decryption using Microsoft CNG in C++:
int main(int argc, char** argv)
{
auto salt = std::string("saltycrack");
auto pass = std::string("pass");
std::string cipher_text = base64_decode("NpN45cs2+bVLtEgp6ZmBmmHLH89aFdHIbDjhstTK0Vb1VMr7jBsliKD9siQGGxGS");
NTSTATUS bcryptResult = 0;
BCRYPT_ALG_HANDLE alg = 0;
assert(BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&alg, BCRYPT_AES_ALGORITHM, 0, 0)));
BCRYPT_ALG_HANDLE prf = NULL;
assert(BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&prf, BCRYPT_SHA1_ALGORITHM, nullptr, BCRYPT_ALG_HANDLE_HMAC_FLAG)));
auto key_len = 32;
ULONGLONG iter = 1000;
ULONG kbh_len = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + key_len;
BCRYPT_KEY_DATA_BLOB_HEADER* kbh = (BCRYPT_KEY_DATA_BLOB_HEADER*)malloc(kbh_len);
assert(BCRYPT_SUCCESS(BCryptDeriveKeyPBKDF2(prf, (PUCHAR)pass.data(), pass.size(), (PUCHAR)salt.data(), salt.size(), iter, (uint8_t*)(kbh + 1), key_len, 0)));
kbh->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC;
kbh->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
kbh->cbKeyData = key_len;
DWORD key_obj_len;
ULONG got;
assert(BCRYPT_SUCCESS(BCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (PBYTE)&key_obj_len, sizeof(DWORD), &got, 0)));
uint8_t* key_obj = new uint8_t[key_obj_len];
BCRYPT_KEY_HANDLE key = 0;
assert(BCRYPT_SUCCESS(BCryptImportKey(alg, NULL, BCRYPT_KEY_DATA_BLOB, &key, key_obj, key_obj_len, (PUCHAR)kbh, kbh_len, 0)));
{
std::wstring mode = BCRYPT_CHAIN_MODE_CBC;
BYTE* ptr = reinterpret_cast<BYTE*>(const_cast<wchar_t*>(mode.data()));
ULONG size = static_cast<ULONG>(sizeof(wchar_t) * (mode.size() + 1));
assert(BCRYPT_SUCCESS(BCryptSetProperty(alg, BCRYPT_CHAINING_MODE, ptr, size, 0)));
}
DWORD bytes_done = 0;
DWORD block_len = 0;
assert(BCRYPT_SUCCESS(BCryptGetProperty(alg, BCRYPT_BLOCK_LENGTH, (BYTE*)&block_len, sizeof(block_len), &bytes_done, 0)));
auto iv = std::vector<uint8_t>(block_len);
ULONG bufferlen = 0;
assert(BCRYPT_SUCCESS(BCryptDecrypt(key, (PUCHAR)cipher_text.data(), cipher_text.size(), 0, (PUCHAR)iv.data(), iv.size(), 0, 0, &bufferlen, BCRYPT_BLOCK_PADDING)));
auto plaintext = std::string(bufferlen, '\0');
assert(BCRYPT_SUCCESS(BCryptDecrypt(key, (PUCHAR)cipher_text.data(), cipher_text.size(), 0, (PUCHAR)iv.data(), iv.size(), (PUCHAR)plaintext.data(), plaintext.size(), &bufferlen, BCRYPT_BLOCK_PADDING)));
std::cout << plaintext << "\n";
return 0;
}
The C++ program outputs #♂b⌡Cεk╘┴╟AÑ#├0⌠678901234567890123456789.
It looks like decryption partially succeeded which doesn't make sense.
Do I need to decode the decrypted data or is there data corruption somewhere?
I want to encrypt an XML file and decrypt the same in a different machine in c#. Please help......
I have already tried with RSA algorithm and RijndaelManaged key but not able to decrypt the file in different machine.
From the problem description, you're probably looking into symmetric encryption algorithm. This is something that should get you started:
public class Key {
public string Password { get; set; }
public byte[] Salt { get; set; }
public string Vector { get; set; }
public PaddingMode? Padding { get; set; }
}
public class AesCryptor {
private const int HASH_SIZE = 32;
private ICryptoTransform m_Decryptor;
private ICryptoTransform m_Encryptor;
private TimeSpan m_Validity;
// Methods
public AesCryptor(Key key, TimeSpan validity) {
if (key == null) {
throw new ArgumentNullException("key");
}
m_Validity = validity;
using (AesManaged aes = new AesManaged()) {
aes.Mode = CipherMode.CBC;
aes.Key = new Rfc2898DeriveBytes(key.Password, key.Salt).GetBytes(aes.KeySize / 8);
aes.IV = Encoding.ASCII.GetBytes(key.Vector);
if (key.Padding.HasValue) {
aes.Padding = key.Padding.Value;
}
m_Encryptor = aes.CreateEncryptor();
m_Decryptor = aes.CreateDecryptor();
}
}
private static bool CompareArray(byte[] first, byte[] second) {
if (first.Length != second.Length) {
return false;
}
for (int i = 0; i < first.Length; i++) {
if (first[i] != second[i]) {
return false;
}
}
return true;
}
public byte[] Decrypt(byte[] encryptedData) {
byte[] hash;
if (encryptedData == null) {
throw new ArgumentNullException("encryptedData");
}
byte[] buffer = m_Decryptor.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
using (SHA256 sha = SHA256.Create()) {
hash = sha.ComputeHash(buffer, 0, buffer.Length - HASH_SIZE);
}
byte[] orginalHash = new byte[HASH_SIZE];
Buffer.BlockCopy(buffer, buffer.Length - HASH_SIZE, orginalHash, 0, HASH_SIZE);
if (!CompareArray(orginalHash, hash)) {
throw new Exception("Hash match failure.");
}
if (m_Validity != TimeSpan.Zero) {
DateTime timestamp = new DateTime(BitConverter.ToInt64(buffer, (buffer.Length - HASH_SIZE) - 8));
TimeSpan delta = (TimeSpan)(DateTime.Now - timestamp);
if (delta > m_Validity) {
throw new Exception("Timestamp too old.");
}
}
byte[] result = new byte[(buffer.Length - HASH_SIZE) - 8];
Buffer.BlockCopy(buffer, 0, result, 0, (buffer.Length - HASH_SIZE) - 8);
return result;
}
public byte[] Encrypt(byte[] data) {
byte[] hash;
if (data == null) {
throw new ArgumentNullException("data");
}
byte[] buffer = new byte[(data.Length + 8) + HASH_SIZE];
Buffer.BlockCopy(data, 0, buffer, 0, data.Length);
Buffer.BlockCopy(BitConverter.GetBytes(DateTime.Now.Ticks), 0, buffer, data.Length, 8);
using (SHA256 sha = SHA256.Create()) {
hash = sha.ComputeHash(buffer, 0, buffer.Length - HASH_SIZE);
}
Buffer.BlockCopy(hash, 0, buffer, buffer.Length - HASH_SIZE, HASH_SIZE);
return m_Encryptor.TransformFinalBlock(buffer, 0, buffer.Length);
}
}
Usage:
var myKey = new Key {
Password = "whatever,nevermind#$",
Vector = "Q^!#g1353hdhjs*-",
Salt = new byte[] { 0x33, 0x4d, 0x56, 0x4f, 1, 0x22, 15, 0x7f }
};
var cryptor = new AesCryptor(myKey, TimeSpan.Zero);
// this will encrypt c:\test.xml into byte array
byte[] encrypted = cryptor.Encrypt(File.ReadAllBytes(#"c:\test.xml"));
// this will persist encrypted byte array into file c:\test-encrypted.bin
File.WriteAllBytes(#"c:\test-encrypted.bin", encrypted);
// this will load & decrypt bytes from file c:\test-encrypted.bin
byte[] decrypted = cryptor.Decrypt(File.ReadAllBytes(#"c:\test-encrypted.bin"));
// this will write decrypted bytes into c:\test-decrypted.xml
File.WriteAllBytes(#"c:\test-decrypted.xml", decrypted);
I want to make a program in C# that can open KeePass 1.x kdb files. I downloaded sources and trying to port password database reading functionality. Database contents is encrypted. Encryption key is obtained the following way:
User enters password;
SHA256 hash of password is calculated and split in two 128-bit halves;
Several rounds of AES is applied to each half of hash using key from database header;
Halves are concatenated back;
Result is salted with salt from database header;
SHA256 hash of step 4 result is calculated. That is the encryption key.
I'm stuck on step 3. KeePass uses CNG for AES. Simplified source (for half of hash, other half had the same applied to it):
BCRYPT_ALG_HANDLE hAes = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
BYTE pbKey32[32] = <encryption key>;
BYTE pbData16[16] = <half of hash from step 2>;
BCryptOpenAlgorithmProvider(&hAes, BCRYPT_AES_ALGORITHM, NULL, 0);
DWORD dwKeyObjLen = 0;
ULONG uResult = 0;
BCryptGetProperty(hAes, BCRYPT_OBJECT_LENGTH, (PUCHAR)&dwKeyObjLen, sizeof(DWORD), &uResult, 0);
BCryptSetProperty(hAes, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_ECB, static_cast<ULONG>((wcslen(BCRYPT_CHAIN_MODE_ECB) + 1) * sizeof(wchar_t)), 0);
BCRYPT_KEY_DATA_BLOB_32 keyBlob;
ZeroMemory(&keyBlob, sizeof(BCRYPT_KEY_DATA_BLOB_32));
keyBlob.dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC;
keyBlob.dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
keyBlob.cbKeyData = 32;
memcpy(keyBlob.pbData, pbKey32, 32);
pKeyObj = new UCHAR[dwKeyObjLen];
BCryptImportKey(hAes, NULL, BCRYPT_KEY_DATA_BLOB, &hKey, pKeyObj.get(), dwKeyObjLen, (PUCHAR)&keyBlob, sizeof(BCRYPT_KEY_DATA_BLOB_32), 0);
for (int i = 0; i < rounds; ++i)
{
BCryptEncrypt(hKey, pbData16, 16, NULL, NULL, 0, pbData16, 16, &uResult, 0);
}
So, as far as I understand, it uses AES algorithm with ECB chaining mode and it passes NULL and 0 as 5th and 6th argument of BCryptEncrypt function meaning it will not use initialization vector.
Now, how do I do the same in C#? I wrote following function to do one round of transformation (based on MSDN sample):
public static byte[] KeyTransform(byte[] buffer, byte[] key)
{
Aes aes = Aes.Create();
aes.Key = key;
aes.BlockSize = 128;
aes.KeySize = key.Length * 8;
aes.Mode = CipherMode.ECB;
//aes.IV = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
ICryptoTransform ct = aes.CreateEncryptor();
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, ct, CryptoStreamMode.Write))
{
using (BinaryWriter bw = new BinaryWriter(cs))
{
bw.Write(buffer);
}
cs.Flush();
}
return ms.ToArray();
}
}
Then I compare buffers after one round of AES applied in original and in my code. My code produces different results from original. How do I fix it?
By the way, no matter if I specify IV or not my code produces different result every time (so I believe IV is always generated and used). If I try to set aes.IV to null it throws exception saying that I can't set it to null.
It seems that initializing ICryptoTransform like this:
ICryptoTransform ct = aes.CreateEncryptor(key, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
does the trick.
The only thing that worries me is that resulting memory stream has 32 bytes instead of 16. But if I drop last 16 of them it produces what I need.
I would like to be able to perform application-level encryption in ASP.NET, producing an array of bytes that would then be saved to a MySQL blob column. I would then like it to be an option that, if you have the encryption key, you would be able to decrypt it using MySQL's AES_DECRYPT() function. This seems like it should be possible, since AES_DECRYPT is an implementation of AES/Rijndael.
The MySQL AES_ENCRYPT/DECRYPT functions simply take a key and the string to encrypt/decrypt as parameters. The examples i've seen for encryption in ASP.NET/C#, however, involve also specifying values for Key and IV (initialization vector). How do these affect the final, encrypted byte array, and how can they be taken into account when decrypting with AES_DECRYPT)_?
You can do that by setting RijndaelManaged to use ECB mode.
However, ECB mode is not secure and should be avoided.
In general, a database is a very bad place to perform encryption.
If you are able to encrypt your data in the database, that implies that you have both the ciphertext and the key in the same place; this defeats the purpose of encryption.
You should keep the key as far away from ciphertext storage as possible; using any sort of SQL encryption function is usually indicative of a fundamental design flaw in your encryption strategy which can have disastrous consequences.
Encryption
In Mysql use HEX(AES_ENCRYPT('unencryptedString', 'Password'))
Example
UPDATE `secrets` SET `value`=HEX(AES_ENCRYPT('unencryptedString', 'Password')) WHERE `Id` = 2;
you will see in the database there is a value similar to this D4B5E4CAD92FFB73FCAEB5ED3B31E9EDD8FA7440E9E3F582FE5A9237DB8EE013
Now the equivalent code in C# is (Original Source:link)
public static String AES_encrypt(String Input, string key)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 128;
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;
aes.Key = mkey(key);
aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
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);
cs.FlushFinalBlock();
}
xBuff = ms.ToArray();
}
return xBuff.ToHexString();
}
Helper methods and extensions that used
Refernce Link
private static byte[] mkey(string skey)
{
byte[] key = Encoding.UTF8.GetBytes(skey);
byte[] k = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < key.Length; i++)
{
k[i % 16] = (byte)(k[i % 16] ^ key[i]);
}
return k;
}
Reference Link
public static class ByteArrayExtensions
{
public static string ToHexString(this byte[] ba)
{
return BitConverter.ToString(ba).Replace("-", "");
}
}
Decryption
in Mysql use CAST(AES_DECRYPT(UNHEX(c.value), 'Password') as char)
Example
SELECT c.*,CAST(AES_DECRYPT(UNHEX(c.`value`), 'Password') as char) FROM `secrets` as c where `Id` = 2;
Equivalent code in C# is
public static String AES_decrypt(String Input, string key)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 128;
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;
aes.Key = mkey(key);
aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var decrypt = aes.CreateDecryptor();
byte[] encryptedStr = Input.FromHex2ByteArray();
string Plain_Text;
using (var ms = new MemoryStream(encryptedStr))
{
using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Read))
{
using (StreamReader reader = new StreamReader(cs))
{
Plain_Text = reader.ReadToEnd();
}
}
}
return Plain_Text;
}
Helper methods and extensions that used
Reference Link
private static byte[] mkey(string skey)
{
byte[] key = Encoding.UTF8.GetBytes(skey);
byte[] k = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < key.Length; i++)
{
k[i % 16] = (byte)(k[i % 16] ^ key[i]);
}
return k;
}
Reference Link
public static byte[] FromHex2ByteArray(this string hex)
{
if (hex.Length % 2 == 1)
throw new Exception("The binary key cannot have an odd number of digits");
byte[] arr = new byte[hex.Length >> 1];
for (int i = 0; i < hex.Length >> 1; ++i)
{
arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1])));
}
return arr;
}
private static int GetHexVal(char hex)
{
int val = (int)hex;
//For uppercase A-F letters:
//return val - (val < 58 ? 48 : 55);
//For lowercase a-f letters:
//return val - (val < 58 ? 48 : 87);
//Or the two combined, but a bit slower:
return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
}