I know MAC is 4 first byte of last block encryption, and found this CMAC explanation here but it's kinda hard to understand. And maybe there are already some CMAC AES questions but I'm sorry I can't understand it well.
Anyone can explain how to calculate CMAC? and if necessary with some example code in C#. Thanks
First you need to derive two subkeys from your AES key. The algorithm is described well in RFC4493, but I will include some code samples here for reference.
For this, you will need the AESEncrypt function, which you can write using dotNet AesCryptoServiceProvider:
byte[] AESEncrypt(byte[] key, byte[] iv, byte[] data)
{
using (MemoryStream ms = new MemoryStream())
{
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
}
}
And something to shift arrays left by one bit:
byte[] Rol(byte[] b)
{
byte[] r = new byte[b.Length];
byte carry = 0;
for (int i = b.Length - 1; i >= 0; i--)
{
ushort u = (ushort)(b[i] << 1);
r[i] = (byte)((u & 0xff) + carry);
carry = (byte)((u & 0xff00) >> 8);
}
return r;
}
Now just the implementation of the algorithm in RFC4493 remains. I commented the logic from the RFC for easier understanding.
byte[] AESCMAC(byte[] key, byte[] data)
{
// SubKey generation
// step 1, AES-128 with key K is applied to an all-zero input block.
byte[] L = AESEncrypt(key, new byte[16], new byte[16]);
// step 2, K1 is derived through the following operation:
byte[] FirstSubkey = Rol(L); //If the most significant bit of L is equal to 0, K1 is the left-shift of L by 1 bit.
if ((L[0] & 0x80) == 0x80)
FirstSubkey[15] ^= 0x87; // Otherwise, K1 is the exclusive-OR of const_Rb and the left-shift of L by 1 bit.
// step 3, K2 is derived through the following operation:
byte[] SecondSubkey = Rol(FirstSubkey); // If the most significant bit of K1 is equal to 0, K2 is the left-shift of K1 by 1 bit.
if ((FirstSubkey[0] & 0x80) == 0x80)
SecondSubkey[15] ^= 0x87; // Otherwise, K2 is the exclusive-OR of const_Rb and the left-shift of K1 by 1 bit.
// MAC computing
if (((data.Length != 0) && (data.Length % 16 == 0)) == true)
{
// If the size of the input message block is equal to a positive multiple of the block size (namely, 128 bits),
// the last block shall be exclusive-OR'ed with K1 before processing
for (int j = 0; j < FirstSubkey.Length; j++)
data[data.Length - 16 + j] ^= FirstSubkey[j];
}
else
{
// Otherwise, the last block shall be padded with 10^i
byte[] padding = new byte[16 - data.Length % 16];
padding[0] = 0x80;
data = data.Concat<byte>(padding.AsEnumerable()).ToArray();
// and exclusive-OR'ed with K2
for (int j = 0; j < SecondSubkey.Length; j++)
data[data.Length - 16 + j] ^= SecondSubkey[j];
}
// The result of the previous process will be the input of the last encryption.
byte[] encResult = AESEncrypt(key, new byte[16], data);
byte[] HashValue = new byte[16];
Array.Copy(encResult, encResult.Length - HashValue.Length, HashValue, 0, HashValue.Length);
return HashValue;
}
Good luck!
Related
I am trying to decrypt a ciphersaber encrypted hexadecimal message using an IV mixing round of 20 with the key MyKey.
The messages is:
bad85d9e7f5aff959b6b332b44af2cc554d8a6eb
I am doing this in pure C# and it should return the message: Hola Mundo
using System;
using System.Text;
public class Program
{
public static void Main(string[] args)
{
// Hexadecimal text
string hexText = "bad85d9e7f5aff959b6b332b44af2cc554d8a6eb";
// Convert hexadecimal text to byte array
byte[] encryptedData = new byte[hexText.Length / 2];
for (int i = 0; i < encryptedData.Length; i++)
{
encryptedData[i] = Convert.ToByte(hexText.Substring(i * 2, 2), 16);
}
// IV length
int ivLength = 1;
// Key loop iterations
int keyIterations = 20;
// Encryption key
string encryptionKey = "MyKey";
// Convert encryption key to byte array
byte[] keyData = Encoding.UTF8.GetBytes(encryptionKey);
// Create an array to store the IV
byte[] ivData = new byte[ivLength];
// Copy the first `ivLength` bytes of the encrypted data to the IV array
Array.Copy(encryptedData, 0, ivData, 0, ivLength);
// Create an array to store the encrypted message
byte[] messageData = new byte[encryptedData.Length - ivLength];
// Copy the remaining bytes of the encrypted data to the message data array
Array.Copy(encryptedData, ivLength, messageData, 0, messageData.Length);
// Create an array to store the decrypted message
byte[] decryptedData = new byte[messageData.Length];
// Perform the decryption
for (int i = 0; i < messageData.Length; i++)
{
decryptedData[i] = (byte)(messageData[i] ^ keyData[i % keyData.Length]);
for (int j = 0; j < keyIterations; j++)
{
decryptedData[i] = (byte)(decryptedData[i] ^ ivData[j % ivData.Length]);
}
}
// Convert the decrypted data to a string and print it
string decryptedMessage = Encoding.UTF8.GetString(decryptedData);
Console.WriteLine("Decrypted message: " + decryptedMessage);
}
}
Now when I try it returns: �$�#���Jf=�I���
What mistake am I making in the code or am I implementing it wrong?
I tested the text with the following site to see if it was ok: https://ruletheweb.co.uk/cgi-bin/saber.cgi
CipherSaber uses as IV the first 10 bytes of the encrypted message. The rest is the actual ciphertext. The IV is appended to the key (giving the key setup input), which is used as input to the CipherSaber key setup, see CipherSaber, Technical description, 1st section.
In the posted code, an IV length of 1 is applied instead of 10, which incorrectly determines IV (and thus key setup input) and actual ciphertext. The correct determination of IV and actual ciphertext is:
private static (byte[], byte[]) SeparateIvCiphertext(byte[] ivCiphertext)
{
int ivLen = 10;
byte[] iv = new byte[ivLen];
Buffer.BlockCopy(ivCiphertext, 0, iv, 0, iv.Length);
byte[] ciphertext = new byte[ivCiphertext.Length - iv.Length];
Buffer.BlockCopy(ivCiphertext, iv.Length, ciphertext, 0, ciphertext.Length);
return (iv, ciphertext);
}
and of the key setup input:
private static byte[] GetKeySetupInput(byte[] key, byte[] iv)
{
byte[] keySetupInput = new byte[key.Length + iv.Length];
Buffer.BlockCopy(key, 0, keySetupInput, 0, key.Length);
Buffer.BlockCopy(iv, 0, keySetupInput, key.Length, iv.Length);
return keySetupInput;
}
Furthermore, the decryption itself seems to be implemented incorrectly or at least incompletely. CipherSaber uses RC4 as its encryption/decryption algorithm, which can be divided into a key setup and the actual encryption/decryption:
The referenced website performs decryption using CipherSaber-2. Compared to the original CipherSaber (referred to as CipherSaber-1), a modified key setup is used in which the CipherSaber-1/RC4 key setup is repeated multiple times, 20 times in the case of the posted data.
A description of the CipherSaber-1/RC4 key setup can be found here, Key-scheduling algorithm (KSA), a possible implementation for CipherSaber-2 is:
private static byte[] sBox = new byte[256];
private static void KeySetup(byte[] input, int iterations)
{
for (int i = 0; i < 256; i++)
{
sBox[i] = (byte)i;
}
int j = 0;
for (int cs2loop = 0; cs2loop < iterations; cs2loop++) // CipherSaber-2 modification
{
for (int i = 0; i < 256; i++)
{
j = (j + sBox[i] + input[i % input.Length]) % 256;
Swap(ref sBox[i], ref sBox[j]);
}
}
}
private static void Swap(ref byte val1, ref byte val2)
{
if (val1 == val2) return;
val1 = (byte)(val1 ^ val2);
val2 = (byte)(val2 ^ val1);
val1 = (byte)(val1 ^ val2);
}
The loop marked CipherSaber-2 modification in the code snippet is the modification compared to CipherSaber-1/RC4!
The actual encryption/decryption is described here, Pseudo-random generation algorithm (PRGA), a possible implememtation is:
private static byte[] Process(byte[] input)
{
int i = 0, j = 0;
byte[] result = new byte[input.Length];
for (int k = 0; k < input.Length; k++)
{
i = (i + 1) % 256;
j = (j + sBox[i]) % 256;
Swap(ref sBox[i], ref sBox[j]);
result[k] = (byte)(sBox[(sBox[i] + sBox[j]) % 256] ^ input[k]);
}
return result;
}
Note that this algorithm is used for both encryption and decryption.
With this, the posted encrypted message can be decrypted as follows:
using System;
using System.Text;
...
byte[] key = Encoding.UTF8.GetBytes("MyKey");
byte[] encryptedData = Convert.FromHexString("bad85d9e7f5aff959b6b332b44af2cc554d8a6eb");
(byte[] iv, byte[] ciphertext) = SeparateIvCiphertext(encryptedData);
byte[] keySetupInput = GetKeySetupInput(key, iv);
int iterations = 20;
KeySetup(keySetupInput, iterations);
byte[] plaintext = Process(ciphertext);
Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // Hola Mundo
which gives Hola Mundo as plaintext.
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;
}
I have the following code :
public static string Encrypt3Des(string cipherString)
{
string result = "";
byte[] keyArray;
byte[] ivArray;
byte[] toEncryptArray = Enc3DesPerChar(cipherString);
//string toEncryptString = ByteArrayToString(toEncryptArray);
// Get the key from config file
System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
string key = (string)settingsReader.GetValue("SecurityKey", typeof(String));
string iv = (string)settingsReader.GetValue("InitializationVector", typeof(String));
keyArray = StringToByteArray(key);
ivArray = StringToByteArray(iv);
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
//set the secret key for the tripleDES algorithm
tdes.Key = keyArray;
tdes.IV = ivArray;
//ChiperMode
tdes.Mode = CipherMode.CBC;
//PaddingMode(if any extra byte added)
tdes.Padding = PaddingMode.None;
ICryptoTransform cTransform = tdes.CreateEncryptor();
//transform the specified region of bytes array to resultArray
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
//Release resources held by TripleDes Encryptor
tdes.Clear();
result = ByteArrayToString(resultArray);
return result;
}
And this is my method :
protected static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
protected static byte[] StringToByteArray(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
protected static byte[] Enc3DesPerChar(String toEncrypt)
{
string toAsciiString = ByteArrayToString(Encoding.ASCII.GetBytes(toEncrypt));
string toRoll = toAsciiString;
int NumberChars = toRoll.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(toRoll.Substring(i, 2), 16);
}
return bytes;
}
Everything works fine with the above method until I found that the method cannot accept less than 8 character.
The block code that raise an error :
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
Error message :
Length of the data to encrypt is invalid.
Example input :
Encrypt3Des("14022000"); // return encrypt because 8 character or more
Encrypt3Des("1402200"); // return error because 7 character
Does anybody know why this is or how I can fix it? (I don't know if it comes from my encrypting method, but I know a web app which uses the exact same thing to encrypt strings and that one does work.)
EDIT :
The tool that I used for manual encrypt : 3des
The option must :
Text input type
Plaintext input text
3DES function
CBC mode
Fixed Key Hex
Fixed Init Vector
You are using padding as none. Set the padding mode to PKCS7.
Ok, I think just found the solution (my client told me how), I need to fill up the character with null before the loop. null can be converted to ascii with "00". so I decide to PadRight to the ascii result with '0' to 16 character, so one of my method become :
protected static byte[] Enc3DesPerChar(String toEncrypt)
{
string toAsciiString = ByteArrayToString(Encoding.ASCII.GetBytes(toEncrypt));
string toRoll = toAsciiString.PadRight(16,'0');
int NumberChars = toRoll.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(toRoll.Substring(i, 2), 16);
}
return bytes;
}
I got my encryption algorithm working fine and left it alone for a few months. Today I needed to use it again and somehow it broke. I have poked at it for a while and have been unable to spot what the problem is. There are no errors, it just returns junk data.
The Setup: A PHP script(that has worked in production for a long time) encrypts some string using:
function hexstr($hexstr)
{
// return pack('H*', $hexstr); also works but it's much harder to understand.
$return = '';
for ($i = 0; $i < strlen($hexstr); $i+=2) {
$return .= chr(hexdec($hexstr[$i] . $hexstr[$i+1]));
}
return $return;
}
function encrypt($str, $key)
{
$key = hexstr($key);
$size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($size, MCRYPT_RAND);
return $iv . mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_CBC,$iv);
}
If I decrypt this on the php side it works fine.
Then the string is base64 encoded:
$encoded = base64_encode($encrypted);
Finally the C# program gets a hold of it and does the following:
....
byte[] decoded = Convert.FromBase64String(myWebText);
Decrypt(decoded)
.....
public static byte[] Decrypt(byte[] p)
{
RijndaelManaged aes128 = new RijndaelManaged();
aes128.KeySize = 128;
//aes128.BlockSize =
aes128.Padding = PaddingMode.Zeros;
aes128.Mode = CipherMode.CBC;
aes128.Key = StringToByteArray("SOMEHEXSTRING");
//pull the iv off the front of the byte array
if (p.Length <= 16)
{
Utils.ReportError("byte array too short");
return null;
}
byte[] iv = new byte[16];
Array.Copy(p, 0, iv, 0, 16);
aes128.IV = iv;
ICryptoTransform transform = aes128.CreateDecryptor();
byte[] result = transform.TransformFinalBlock(p, 16, p.Length - 16);
Debug.Log("If this encrypted stuff was text it would be:"+System.Text.Encoding.UTF8.GetString(result));
return result;
}
public static byte[] StringToByteArray(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;
}
Does anyone know what this might not be working? It must be very close because I am sure it worked at one point.
Update: I did a binary compare of when i C# encode\decode and php encode decode. The php encoded Hello World is not the same as the c# Hello world when using the same IV. is this possible or does this indicate they are not using the same configuration somehow.
Turns out I had changed the key to have non upper case letters and my GetHexVal function shown there only took upper case letters... Ouch
IDEA: The decryption key has changed.
Very nasty when it happens.
Are you using a config file with decryption key?
If not What is the decryptionKey used ?
Has it changed?
If using localApp.config or ROOT.config and the key was changed, you can go hunting around all backups to get the key.
<machineKey decryptionKey="SomeHexValueHere" validationKey="SomeveryLongHexvalueHere" />
But if your PHP version is working, the key must be "known"
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));
}