I'm working on an implementation of AES-256-CBC (shown below), this will be used in a client-to-database setting. The key is stored locally and the intention of this scheme is to make certain values unreadable if the database contents are leaked.
Since the IV is a fixed length, I planned to have the ciphertext be the encrypted message with the initialization vector prefixed (IV+C), and the decryption procedure take the ciphertext, store the IV and C seperately, and work out C with the stored initialization vector.
After several failed attempts at validating this method, I commented out the encryption part to see what happens. It turns out that either the IV or the ciphertext has the wrong length even though this length is fixed. At first I assumed Rijndael initialization vector with 16 bytes was going to help, as such I converted everything to UTF8, yet it still doesn't work as intended. Worse than that: the ciphertext misses the first character at random (which is a bigger problem to me than a consistent error).
I'm out of ideas, what am I missing here? (note: the messagesboxes are for debug purposes)
public string Encrypt(string input)
{
string cipher = null;
RijndaelManaged Crypto = new RijndaelManaged();
Random r = new Random();
Crypto.KeySize = 256;
Crypto.BlockSize = 128;
Crypto.Mode = CipherMode.CBC;
Crypto.Padding = PaddingMode.PKCS7;
byte[] message = Encoding.UTF8.GetBytes(@input);
byte[] pubkey = Encoding.UTF8.GetBytes(@public_key.PadRight(16, '#').Substring(0, 16));
byte[] iv = new byte[16];
r.NextBytes(iv);
Crypto.Key = pubkey;
Crypto.IV = iv;
try
{
ICryptoTransform Encrypt = Crypto.CreateEncryptor();
cipher = @Encoding.UTF8.GetString(iv) + @Encoding.UTF8.GetString(message);//@Encoding.UTF8.GetString(iv) + @Encoding.UTF8.GetString(Encrypt.TransformFinalBlock(message, 0, message.Length));
if (debug)
MessageBox.Show("ENC\r\ninput (" + input.Length + "): " + @input + "\r\nmessage (" + message.Length + "): " + @System.Text.Encoding.UTF8.GetString(message) + "\r\npubkey (" + pubkey.Length + "):" + @System.Text.Encoding.UTF8.GetString(pubkey) + "\r\ninitialization vector (" + iv.Length + "): " + @System.Text.Encoding.UTF8.GetString(iv) + "\r\ncipher (" + cipher.Length + "): " + @cipher);
}
catch (CryptographicException e)
{
MessageBox.Show("ENC\r\ninput (" + input.Length + "): " + @input + "\r\nmessage (" + message.Length + "): " + @System.Text.Encoding.UTF8.GetString(message) + "\r\npubkey (" + pubkey.Length + "):" + @System.Text.Encoding.UTF8.GetString(pubkey) + "\r\ninitialization vector (" + iv.Length + "): " + @System.Text.Encoding.UTF8.GetString(iv) + "\r\ncipher (" + cipher.Length + "): " + @cipher + "\r\n\r\nException:\r\n" + e.ToString());
cipher = "ENCRYPTION ERROR";
}
return cipher;
}
public string Decrypt(string input)
{
string message = "";
byte[] iv = Encoding.UTF8.GetBytes(@input.Substring(0, 15)); // ლ(ಠ益ಠლ)
byte[] cipherdata = Encoding.UTF8.GetBytes(@input.Substring(16, input.Length - 16)); // ಥ‸ಥ
byte[] pubkey = Encoding.UTF8.GetBytes(public_key.PadRight(16, '#').Substring(0, 16));
RijndaelManaged Crypto = new RijndaelManaged();
Crypto.Key = pubkey;
Crypto.Mode = CipherMode.CBC;
Crypto.Padding = PaddingMode.PKCS7;
Crypto.KeySize = 256;
Crypto.BlockSize = 128;
try
{
ICryptoTransform Decrypt = Crypto.CreateDecryptor();
Decrypt.TransformFinalBlock(cipherdata, 0, cipherdata.Length);
message = System.Text.Encoding.UTF8.GetString(cipherdata);
if (debug)
MessageBox.Show("DEC\r\ninput (" + input.Length + "): " + @input + "\r\ncipherdata (" + cipherdata.Length + "): " + @Encoding.UTF8.GetString(cipherdata) + "\r\ninitialization vector (" + iv.Length + "): " + @Encoding.UTF8.GetString(iv) + "\r\npubkey (" + pubkey.Length + "): " + @Encoding.UTF8.GetString(pubkey) + "\r\nMessage (" + message.Length + "): " + message);
}
catch (CryptographicException e)
{
MessageBox.Show("DEC\r\ninput (" + input.Length + "): " + @input + "\r\ncipherdata (" + cipherdata.Length + "): " + @Encoding.UTF8.GetString(cipherdata) + "\r\ninitialization vector (" + iv.Length + "): " + @Encoding.UTF8.GetString(iv) + "\r\npubkey (" + pubkey.Length + "): " + @Encoding.UTF8.GetString(pubkey) + "\r\nMessage (" + message.Length + "): " + message + "\r\n\r\nException:\r\n" + e.ToString());
message = "DECRYPTION ERROR";
}
return message;
}
Concatenating random byte sequences as though they were strings when using a variable length encoding like UTF8 is a bad idea.
You should concatenate the IV byte array and the ciphertext output buffer to produce a byte array, then Convert.ToBase64String
to produce a portable string representation thereof.