C# AES and RSA File Encryption - How to use IV? - c#

I'm writing a program at the moment that works under the following scenario:
I've got some confidential log files that I need to backup to a server.
I have a program that generates these log files every day.
These log files would rarely if ever need to be opened.
I have only one RSA public/private key pair.
The program has only the RSA public key.
I generate a random AES key each time the program makes one of these confidential files.
The program uses this AES key to encrypt the log file.
I then use the RSA public key to encrypt the AES Key
I then backup both the AES encrypted file and RSA encrypted AES key to the server.
As far as I understand, that protocol is fitting for my use case.
The issue I'm having is coding it up in C#. I ran into needing an Initialization Vector(IV) for my AES encryption, I tried to encrypt this along with the AES key by using the public RSA key on both. But the 512(2 * 256) size is larger than RSA is happy to encrypt. So I figured out since I created the Initialization Vector randomly each time just like the AES Key, I can add the IV to the front of the AES ciphertext. However, I'm not sure where the code to do this would be inserted in my functions
Any help in the right direction to the "protocol" or other ways to write the IV to the ciphertext would be great. Thank you in advance.
static public Tuple<byte[], byte[]> EncryptAES(byte[] toEncryptAES, RSAParameters RSAPublicKey)
{
byte[] encryptedAES = null;
byte[] encryptedRSA = null;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Mode = CipherMode.CBC;
AES.GenerateIV();
AES.GenerateKey();
encryptedRSA = RSAEncrypt(AES.Key, RSAPublicKey);
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
ms.Write(AES.IV, 0, AES.KeySize); //DOESNT WORK HERE
//Can't use CS to write it to the stream else it will encrypt along with file
cs.Write(toEncryptAES, 0, toEncryptAES.Length);
cs.Close();
}
encryptedAES = ms.ToArray();
}
}
return new Tuple<byte[], byte[]>(encryptedAES, encryptedRSA);
}
static public byte[] DecryptAES(byte[] toDecryptAES, byte[] AESKeyAndIV, RSAParameters RSAPrivateKey)
{
byte[] AESKey = RSADecrypt(AESKeyAndIV, RSAPrivateKey);
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Key = AESKey;
ms.Read(AES.IV, 0, AES.KeySize); //Not sure if can read MS here
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
//Would I need to move 0 to 256?
cs.Write(toDecryptAES, 0, toDecryptAES.Length);
cs.Close();
}
return ms.ToArray();
}
}
}

You where quite close, write out the IV before you create the CryptoStream
static public Tuple<byte[], byte[]> EncryptAES(byte[] toEncryptAES, RSAParameters RSAPublicKey)
{
byte[] encryptedAES = null;
byte[] encryptedRSA = null;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Mode = CipherMode.CBC;
AES.GenerateIV();
AES.GenerateKey();
encryptedRSA = RSAEncrypt(AES.Key, RSAPublicKey);
ms.Write(AES.IV, 0, AES.KeySize); //Move the write here.
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(toEncryptAES, 0, toEncryptAES.Length);
cs.Close();
}
encryptedAES = ms.ToArray();
}
}
return new Tuple<byte[], byte[]>(encryptedAES, encryptedRSA);
}
For the decrypt, make sure you loop over the read till you have fully read the byte[] for the IV, Stream.Read is not guaranteed to read all the bytes you asked it to read. I usually make a static method ReadFully to ensure all bytes are read.
private static byte[] ReadFully(Stream stream, int length)
{
int offset = 0;
byte[] buffer = new byte[length];
while(offset < length)
{
offset += stream.Read(buffer, offset, length - offset);
}
return buffer;
}
Then just use that method to read in the IV. You also want to use cs.Read not cs.Write to read out the encrypted data and put the stream in to read mode, however it is easier to just use .CopyTo and copy the data to a new MemoryStream.
static public byte[] DecryptAES(byte[] toDecryptAES, byte[] AESKeyAndIV, RSAParameters RSAPrivateKey)
{
byte[] AESKey = RSADecrypt(AESKeyAndIV, RSAPrivateKey);
using (MemoryStream source = new MemoryStream(toDecryptAES))
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Key = AESKey;
var iv = ReadFully(source, AES.KeySize);
AES.IV = iv;
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(source, AES.CreateDecryptor(), CryptoStreamMode.Read))
{
using(var dest = new MemoryStream())
{
cs.CopyTo(dest);
return dest.ToArray();
}
}
}
}
}
For other readers, note that RSAEncrypt and RSADecrypt are wrappers for calls to the RSACryptoServiceProvider.

Related

C# to node crypto hashing - md5 and sha256

Here is the C# code I'm trying to port into Node crypto, but since I don't know c# it's proving a little tricky!
public static string EncryptStringToBytes_Aes(string username, string password)
{
string encrypted = string.Empty;
byte[] clearBytes = Encoding.UTF8.GetBytes(password);
Console.WriteLine("1." + clearBytes);
using (Aes aesAlg = Aes.Create())
{
byte[] k; byte[] iv;
byte[] bytes = Encoding.UTF8.GetBytes(username);
k = SHA256.Create().ComputeHash(bytes);
iv = MD5.Create().ComputeHash(bytes);
aesAlg.Key = k;
aesAlg.IV = iv;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
csEncrypt.Write(clearBytes, 0, clearBytes.Length); }
encrypted = Convert.ToBase64String(msEncrypt.ToArray());
}
}
return encrypted;
}
C# repl:
https://repl.it/#HarryLincoln/NegligiblePoisedHexagon
Node workings:
crypto.createCipheriv() definitely looks like the way to go, but the I don't believe the c# methods (SHA256.Create() & MD5.Create()) care for the length of the key and iv - but crypto.createCipheriv() does.
The c# uses a CryptoStream: So I think some kind of Buffer is in order looking at some similar C# -> Node crypto stuff
Would really appreciate some help!
.Net Framework - AES encryption uses a 256 bit key and CBC mode and PKCS7 padding by default.
The code to port is very simple to read, it just does this:
return
BASE64 (
AES_ENCRYPT (
password,
Key: SHA256(username),
IV: MD5(username)
)
)
The same can easily be achieved on Node.
const crypto = require('crypto');
const key = crypto.createHash('sha256').update('username', 'utf8').digest();
const iv = crypto.createHash('md5').update('username', 'utf8').digest();
const encryptor = crypto.createCipheriv("aes-256-cbc", key, iv);
var crypted = Buffer.concat([encryptor.update('password', 'utf8'), encryptor.final()]);
let base64data = crypted.toString('base64');
console.log(base64data);

Padding-related CryptographicException in previously working code

I have a program which stores encrypted text data. Whenever new data is added, the encrypted data is loaded from file, decrypted, the new string data is appended, and finally the data is re-encrypted and saved to disk. This used to work until recently, now however I get a CryptographicException: Padding is invalid and cannot be removed when decrypting the loaded data. This is the code I use to encrypt/decrypt the data:
public static byte[] Encrypt(string text, byte[] key) {
byte[] encrypted;
byte[] IV;
using (var aes = Aes.Create()) {
aes.Key = key;
aes.GenerateIV();
IV = aes.IV;
aes.Mode = CipherMode.CBC;
var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (var msEncrypt = new MemoryStream())
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
using (var swEncrypt = new StreamWriter(csEncrypt)) {
swEncrypt.Write(text);
}
encrypted = msEncrypt.ToArray();
}
}
// return plaintext IV and encrypted text payload concatenated
return IV.Concat(encrypted).ToArray();
} // Encrypt()
public static string Decrypt(byte[] bytes, byte[] key) {
string plaintext = null;
using (var aes = Aes.Create()) {
aes.Key = key;
// split iv and encryped cyphertext
byte[] IV = new byte[aes.BlockSize / 8];
byte[] cipherText = new byte[bytes.Length - IV.Length];
Array.Copy(bytes, IV, IV.Length);
Array.Copy(bytes, IV.Length, cipherText, 0, cipherText.Length);
aes.IV = IV;
aes.Mode = CipherMode.CBC;
var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (var msDecrypt = new MemoryStream(cipherText))
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
using (var srDecrypt = new StreamReader(csDecrypt)) {
plaintext = srDecrypt.ReadToEnd();
// !!! EXCEPTION HAPPENS HERE !!!
}
}
return plaintext;
}
The encrypted byte[] array is read/written using plain File.ReadAllBytes / File.WriteAllBytes.
I tried to change the the code to read byte by byte until an exception occurs, which gave me the decrypted text minus 16 bytes. However, only the first third contained valid text, the rest was a few garbled bytes, followed by a (equally garbled) 16 byte sequence, repeated over and over. This makes me suspect that some kind of corruption occurred during a previous save operation.
Any help would be greatly appreciated, regarding to recovering the data (if possible) and pointing out problems/bugs in the encryption/decryption code (if any).

Trying to decrypt a string using AES Managed

I am trying to write code to decrypt a string.
I was given an equivalent in python and I am trying to create the same in . NET
Python:
//Initialization vector is just a string of 16 null bytes
iv = '\x00' * 16
//Create new AES object using the key and init vector
aes = AES.new(key, AES.MODE_CBC, iv)
//Decrypt password and remove padding
result = aes.decrypt(myString).rstrip('\x0b\x08\x07')
return result
Here is my attempt:
byte[] iv = new byte[16];
byte[] rawPlaintext = Convert.FromBase64String("MyBase64String");
byte[] key = // Read from common source
using (Aes aes = new AesManaged())
{
aes.Padding = PaddingMode.None;
aes.KeySize = 128; // in bits
aes.Key = new byte[128 / 8]; // 16 bytes for 128 bit encryption
aes.IV = new byte[128 / 8]; // AES needs a 16-byte IV
// Should set Key and IV here. Good approach: derive them from
// a password via Cryptography.Rfc2898DeriveBytes
byte[] cipherText = key;
byte[] plainText = iv;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(rawPlaintext, 0, rawPlaintext.Length);
}
cipherText = ms.ToArray();
}
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherText, 0, cipherText.Length);
}
plainText = ms.ToArray();
}
string s = System.Text.Encoding.Unicode.GetString(plainText);
Console.WriteLine(s);
}
It doesn't appear to be working for the result is a string of symbols.
Possible issues:
- I see a mode of CBC getting set. I'm not sure where that equivalent setting would be. I've tried to play with the PaddingMode.
- Could my iv byte[] be causing the issue? Is the default null or 0?
EDIT:
- From what I am reading AesManaged uses AES in CBC mode so that should be a non-issue.
Try replacing this:
string s = System.Text.Encoding.Unicode.GetString(plainText);
to:
string s = System.Text.Encoding.UTF8.GetString(plainText);

Why do these two decrypt/encrypt functions work only sometimes?

I use ECDiffieHellmanCng for exchange of public keys and then AES for encrypting/decrypting.
Sometimes the decryption works, other times I get the following error in decryption method: Padding is invalid and cannot be removed.
Where is the cause of this?
Here is the code:
private void Encryption(byte[] key, byte[] unencryptedMessage,out byte[] encryptedMessage, out byte[] iv) // encryption funkcija
{
using (Aes aes = new AesManaged())
{
aes.Key = key;
//aes.GenerateIV();
iv = aes.IV;
aes.Padding = PaddingMode.PKCS7;
// Encrypt the message
using (MemoryStream ciphertext = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ciphertext, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(unencryptedMessage, 0, unencryptedMessage.Length);
cs.Close();
}
encryptedMessage = ciphertext.ToArray();
}
}
}
private void Decryption(byte[] encryptedMessage, byte[] iv, out byte[] decryptedMessage)
{
using (Aes aes = new AesManaged())
{
aes.Key = receiversKey;
aes.IV = iv;
aes.Padding = PaddingMode.PKCS7;
// Decrypt the message
using (MemoryStream decryptedBytes = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(decryptedBytes, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(encryptedMessage, 0, encryptedMessage.Length);
cs.Close();
}
decryptedMessage = decryptedBytes.ToArray();
}
}
}
Since you're already using the same padding mode for both encryption and decryption, the most likely causes of invalid padding error are:
Different keys that are used for encryption and decryption.
Invalid encrypted message passed to Decryption() method. You could mistakenly pass an empty string or non-encrypted data.
If this does not help, please provide the code that calls Encryption() and Decryption() methods and handles the key used in both cases. It's required because those methods itself looks ok, the most likely problem is in passed arguments.

My AES Intitialization Vectors seem to do nothing with AESCryptoServiceProvider in C#

I have been doing some research into using AESCryptoServiceProvider in C#. So far, I have an implementation which seems to be working. However, my understanding of initialization vectors is that it should protect my cipher text from being the same when the payload and key are the same, but it appears I am doing something wrong. Changing my initialization vector doesn't seem to affect the results of these functions at all.
Here are my functions:
public string EncryptString(string toEncrypt, byte[] encryptionKey, byte[] iv)
{
var toEncryptBytes = Encoding.Default.GetBytes(toEncrypt);
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = encryptionKey;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
//aes.GenerateIV();
aes.IV = iv;
using (var encryptor = aes.CreateEncryptor(encryptionKey, aes.IV))
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
using (var bWriter = new BinaryWriter(cs))
{
bWriter.Write(aes.IV, 0, aes.IV.Length);
bWriter.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return Convert.ToBase64String(ms.ToArray());
}
}
}
public string DecryptString(string toDecrypt, byte[] encryptionKey, byte[] iv)
{
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = encryptionKey;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
var toDecryptBytes = Convert.FromBase64String(toDecrypt);
Array.Copy(toDecryptBytes, 0, iv, 0, iv.Length);
aes.IV = iv;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, iv), CryptoStreamMode.Write))
using (var binWriter = new BinaryWriter(cs))
{
binWriter.Write(toDecryptBytes, iv.Length, toDecryptBytes.Length - iv.Length);
}
return Encoding.Default.GetString(ms.ToArray());
}
}
}
New version of my encrypt function I have now following the advice from #owlstead, changing back CBC mode. This does appear to be working correctly now using CBC.
public string EncryptString(string toEncrypt, byte[] encryptionKey)
{
var toEncryptBytes = Encoding.Default.GetBytes(toEncrypt);
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = encryptionKey;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.GenerateIV();
using (var encryptor = aes.CreateEncryptor(encryptionKey, aes.IV))
using (var ms = new MemoryStream())
{
ms.Write(aes.IV, 0, aes.IV.Length);
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
using (var bWriter = new BinaryWriter(cs))
{
bWriter.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return Convert.ToBase64String(ms.ToArray());
}
}
}
You are putting the CryptoStream and the BinaryWriter in the wrong order for at least the encrypt function. You are currently encrypting your IV, while the IV should be put in front of the ciphertext, in plain.
The IV is XORed with the plain text before encryption. Now, this means that you zero out the plain text: you are encrypting a zero valued block.
CipherMode.ECB mode seems not picking up the IV argument(which means no matter what IV you passed in, the result will be the same),this is possible because ECB doesn't need a concept as Initial Vector,for there's no subsequent vectors either. If you want to mask your ECB block, XOR your block mannually before encryption is OK.
While you changed to CipherMode.CBC, the IV does matters,for subsequent blocks will be XORed with previous encrypted block ,as forms a chain.

Categories