I am quite new to AES Encryption and therefore have some question about IV and Key
The code I have written works, i can both encrypt and decrypt a String
Just for fun i tried to go on a Website https://www.devglan.com/online-tools/aes-encryption-decryption
And tried to see if the website can decrypt, if i give the same key and IV. but I ran into an error
The website said "Length of secret key should be 32 for 256 bits key size"
and "Length of initialization vector must be 16 with AES."
Is there anything I have done wrong?
static void Main(string[] args)
{
byte[] key = GenerateRandomByteArray(32);
Console.WriteLine($"Key: {Convert.ToBase64String(key)}");
byte[] encrypted = EncryptMessage("test message", key);
Console.WriteLine($"Encrypted : {Convert.ToBase64String(encrypted)}");
string decrypted = DecryptMessage(encrypted, key);
Console.WriteLine($"Decrypted: {decrypted}");
Console.ReadLine();
}
private static byte[] GenerateRandomByteArray(int size)
{
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
byte[] buffer = new byte[size];
rng.GetBytes(buffer);
return buffer;
}
}
public static byte[] EncryptMessage(string mess, byte[] secretkey)
{
byte[] encrypted;
using (Aes aes = Aes.Create())
{
aes.Key = secretkey;
//Generates a IV with 16 random bytes
aes.IV = GenerateRandomByteArray(16);
Console.WriteLine($"IV: {Convert.ToBase64String(aes.IV)}");
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Writes the IV, so when we decrypt we can get the first 16 bytes where IV i located
msEncrypt.Write(aes.IV, 0, aes.IV.Length);
//Write all data to the stream.
swEncrypt.Write(mess);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
public static string DecryptMessage(byte[] mess, byte[] key)
{
string plaintext = null;
// Create an Aes object
using (Aes aes = Aes.Create())
{
//Sets the AES key
aes.Key = key;
using (MemoryStream msDecrypt = new MemoryStream(mess))
{
//Reads the first 16 bytes from the message which is where the IV i stored
byte[] iv = new byte[aes.BlockSize / 8];
msDecrypt.Read(iv, 0, iv.Length);
//Sets the IV
aes.IV = iv;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
// Create the streams used for decryption.
//using (MemoryStream msDecrypt = new MemoryStream(mess))
//{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
Is i remember right GenerateRandomByteArray(32) is 256 bits for Key
And GenerateRandomByteArray(16) is 128 bits for IV
Output from Console:
Key: 3dmQoxqrAyoc5kMKQIQl8Hp4UVVCE8B64S19iNZOQRY=
IV: kO1nKUs08c/8mrlbHj3/xQ==
Encrypted : kO1nKUs08c/8mrlbHj3/xR43S5L3BJ5Ueg01xngw93I=
Decrypted: test message
Related
I was wondering why the first 16 bytes of all my strings being encrypted, then when being decrypted are missing and how to fix this if it is possible. I am encrypting like so in c#
public static string EncryptString(string b_key, string plainText)
{
byte[] iv = new byte[16];
byte[] array;
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(b_key);
aes.IV = iv;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamWriter = new StreamWriter((Stream)cryptoStream))
{
streamWriter.Write(plainText);
}
array = memoryStream.ToArray();
}
}
}
return Convert.ToBase64String(array);
}
and decrypting in python3 like so
enc = base64.b64decode(self.text)
iv = enc[:16]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
plain_text = cipher.decrypt(enc[16:])
plain_text = self.dePKCS7_padding(plain_text)
return plain_text
Is readding the first 16 bytes possible? or must be used for encryption. I also want it to crypto safe but the first 16 bytes are kind of important is this possible? anyway to get around this in either c# or python3?
Based on the discussion in comments and inputs from #MichaelFehr and #user9014097, I came up with the following code.
In this code the IV of AES will have random value created when AES.Create() is called. And the same will be used in the outcome of the encrypted value.
The decryptString method will capture the iv value from the incoming encrypted string and assign it to AES while decrypting the string.
public static string EncryptString(string b_key, string plainText)
{
byte[] array;
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(b_key);
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream())
{
// Adding aes.IV to the stream's start.
memoryStream.Write(aes.IV);
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
{
streamWriter.Write(plainText);
}
}
array = memoryStream.ToArray();
}
}
// The final encrypted outcome will be aes.IV+encryptedtext.
return Convert.ToBase64String(array);
}
public static string DecryptString(string key, string cipherText)
{
//input is iv+encrypted text, convert them to byte array.
byte[] buffer = Convert.FromBase64String(cipherText);
// byte array for iv
byte[] iv = new byte[16];
// byte array for rest of the cipher text.
byte[] cipherBuffer = new byte[buffer.Length - 16];
// copy first 16 bytes from the cipher text to iv.
Buffer.BlockCopy(buffer, 0, iv, 0, 16);
// copy rest of the cipher text to the cipher buffer to be decrypted.
Buffer.BlockCopy(buffer, 16, cipherBuffer, 0, buffer.Length - 16);
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(cipherBuffer))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader(cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
I have following assumption in writing above code.
Length of IV is 16.
Python code (shared above) does not need split the input text based on some specific character. It takes first 16 bytes as IV value and rest of the bytes as cipher text.
I was able to encrypt and decrypt values successfully in C# using above methods.
I was not able to decrypt the value in python code as I have little to no idea on how to work with python.
You can test the outcome of above encryption in python to decrypt it. Let me know if it doesn't work as expected.
I hope this will help you solve your issue.
I am trying to decrypt a string in C# using AES:
public static string AesDecrypt(byte[] cipherText, byte[] Key, byte[] IV)
{
string plaintext = null;
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Padding = PaddingMode.Zeros;
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decryptor to perform the stream transform
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption
using MemoryStream msDecrypt = new MemoryStream(cipherText);
using CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
using StreamReader srDecrypt = new StreamReader(csDecrypt);
// Read the decrypted bytes from the decrypting stream and place them in a string
plaintext = srDecrypt.ReadToEnd();
return plaintext;
}
The encoded data is JSON, but when I decrypt it, I get all the right data except that the closing } of the JSON content is missing.
I think that the AES itself is not my problem here. I have doubts in the
plaintext = srDecrypt.ReadToEnd();
since only the last character is missing.
I don't know if I am supposed to flush any of the streams explicitly, but in any case it's a very curious problem.
Here's the full code for the encryption:
public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Padding = PaddingMode.Zeros;
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption
using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using StreamWriter swEncrypt = new StreamWriter(csEncrypt);
// Write all data to the stream
swEncrypt.Write(plainText);
swEncrypt.Flush();
return Convert.ToBase64String(msEncrypt.ToArray());
}
And this is how I call the decryption method:
public static AuthenticationData ParseAuthenticationToken(string token)
{
byte[] tokenBytes = Convert.FromBase64String(token);
string json = AesEncryption.AesDecrypt(tokenBytes, aes.Key, aes.IV);
return JsonConvert.DeserializeObject<AuthenticationData>(json);
}
The problem is in your encryption code. Although you're calling seEncrypt.Flush(), you're not calling csEncrypt.FlushFinalBlock(). That automatically happens when the stream is disposed, but you're not doing that until after you've called msEncrypt.ToArray(). I would rewrite that code as:
MemoryStream msEncrypt = new MemoryStream();
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using StreamWriter swEncrypt = new StreamWriter(csEncrypt);
swEncrypt.Write(plainText);
// swEncrypt is disposed here, flushing it. Then csEncrypt is disposed,
// flushing the final block.
}
return msEncrypt.ToArray();
I am interested, why encryption/decryption only works with small, 0 bytes size on the disk files, but stop working with larger files, where I get errors The input data is not a complete block and Index was outside the bounds of the array.
I use ECDiffieHellmanCng for generating the same symmetric key on both sides.
Exchange of keys on encryption side:
using (ECDiffieHellmanCng sendingMode = new ECDiffieHellmanCng())
{
sendingMode.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
sendingMode.HashAlgorithm = CngAlgorithm.Sha256;
sendersPublicKey = sendingMode.PublicKey.ToByteArray();
CngKey secretKey = CngKey.Import(receiversPublicKey, CngKeyBlobFormat.EccPublicBlob);
sendersKey = sendingMode.DeriveKeyMaterial(CngKey.Import(receiversPublicKey, CngKeyBlobFormat.EccPublicBlob));
byte[] encryptedFile = null;
byte[] ivFile = null;
byte[] fileBytes = File.ReadAllBytes(fileToSendPath);
Encryption(sendersKey, fileBytes, out encryptedFile, out ivFile);
}
Exchange on receiving side:
using (ECDiffieHellmanCng receivingMode = new ECDiffieHellmanCng())
{
receivingMode.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
receivingMode.HashAlgorithm = CngAlgorithm.Sha256;
receiversPublicKey = receivingMode.PublicKey.ToByteArray();
CngKey secretKey = CngKey.Import(sendersPublicKey, CngKeyBlobFormat.EccPublicBlob);
receiversKey = receivingMode.DeriveKeyMaterial(CngKey.Import(sendersPublicKey, CngKeyBlobFormat.EccPublicBlob));
byte[] decryptedFile = new byte[50000000];
Decryption(encryptedFile, ivFile, out decryptedFile);
}
Encrypt/decrypt methods:
private void Encryption(byte[] key, byte[] unencryptedMessage,out byte[] encryptedMessage, out byte[] iv)
{
using (Aes aes = new AesCryptoServiceProvider())
{
aes.Key = key;
iv = aes.IV;
// 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 AesCryptoServiceProvider())
{
aes.Key = receiversKey;
aes.IV = iv;
// 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();
}
}
}
}
AES is a block cipher requiring input to be in block size multiples, 16-bytes for AES. The simple solution is to use PKCS#7 (née PKCS#5) padding option and the padding will be transparently added on encryption and removed on decryption.
when i use this code to encrypt and decrypt i got an error said
Padding is invalid and cannot be removed.
any idea
public static class Crypto
{
private static readonly byte[] initVectorBytes = Encoding.ASCII.GetBytes("tu89geji340t89u2");
// This constant is used to determine the keysize of the encryption algorithm.
private const int keysize = 256;
public static string Encrypt(string plainText, string passPhrase)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null))
{
byte[] keyBytes = password.GetBytes(keysize / 8);
using (RijndaelManaged symmetricKey = new RijndaelManaged())
{
symmetricKey.Mode = CipherMode.CBC;
using (ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
public static string Decrypt(string cipherText, string passPhrase)
{
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
using (PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null))
{
byte[] keyBytes = password.GetBytes(keysize / 8);
using (RijndaelManaged symmetricKey = new RijndaelManaged())
{
symmetricKey.Mode = CipherMode.CBC;
using (ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes))
{
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
}
}
I tried the following using your methods and it worked fine:
var passPhrase = "123456";
var e = Encrypt("testtesttesttest", passPhrase);
Console.WriteLine(e); // YEtSJshcn686ZO+JlEQ48ap/odhuvIGalbAT1XhinqQ=
var d = Decrypt(e, passPhrase);
Console.WriteLine(d); // testtesttesttest
This suggests that you're either passing a different passPhrase to Decrypt() to the one you passed to Encrypt(), or that you are somehow corrupting the ciphertext prior to decryption. (Are you perhaps calling Decrypt with the ciphertext and passphrase parameters reversed?)
It's also worth noting that essentially everything in the comments at the top of your code is wrong:
You're not passing any salt to PasswordDeriveBytes.
The size of the IV must be equal to the block size (16 bytes), it is unrelated to the key size used.
Passing a 16 character string through Encoding.ASCII.GetBytes() results in a 16 byte output, not 32 bytes. (This rather coincidentally means that your initVectorBytes is in fact the correct length for the IV).
Furthermore, PasswordDeriveBytes is deprecated and should not be used. You should be using Rfc2898DeriveBytes instead, and you should be using a proper salt value. The IV should also not be a static value, and definitely not one derived from an ASCII string!
I'm trying to read a file, encrypt it, and send it to a server over socket, where it is written. And then the other way around, read it on server, send it to client, decrypt it, and write it again.
My problem using C# Aes class is, that the input size doesn't equal the output size.
For example, when I read 4096 bytes from the file, the output size is 4112 bytes, 16 bytes more. OK, so 4112 bytes are sent and written on the server, but when I get the file again, I can only send a maximum of 4096 bytes over the socket, and then, of course, the decrypt function on client throws an exception, that the padding is invalid and cannot be removed. Sure I could try to read less bytes on the client, but that doesn't work as well.
I'm a very experienced C++ programmer, and I've done this with OpenSsl, and it worked like a charm. The input size has been always the output size, I don't know what is wrong with my functions in C#.
this is the sending part:
byte[] SendData = new byte[4096];
iBytesRead = FileRead.Read (SendData, 0, 4096);
SendData = aes.encrypt (Encoding.Default.GetString (SendData, 0, iBytesRead), iBytesRead);
String a = aes.decrypt (SendData); // no problems here because the size is correct
Socket.sendB (SendData, SendData.Length);
and the part of receiving from server:
byte[] WriteData = new byte[4096],
Temp;
if ((iBytesReceived = Socket.receiveB (ref WriteData)) == 0)
break;
if (Encoding.ASCII.GetString (WriteData, 0, iBytesReceived) == "end")
break;
for (uint i = 0; i < iBytesReceived; i++)
Temp[i] = WriteData[i];
byte[] a = Encoding.Default.GetBytes (aes.decrypt (Temp));
FileWrite.Write (a, 0, Temp.Length);
Aes functions:
public byte[] encrypt(String _InStr, int _InStrLength)
{
if (!bKeySet)
return ErrorReturn;
byte[] encrypted;
using (Aes aes = Aes.Create ())
{
aes.Key = Key;
aes.IV = IV;
//aes.Padding = PaddingMode.PKCS7;
//aes.BlockSize = 128;
//aes.KeySize = 128;
//aes.Mode = CipherMode.CFB;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
// Create the streams used for encryption.
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(_InStr);
}
}
ms.Close ();
encrypted = ms.ToArray ();
}
}
return encrypted;
}
public String decrypt(byte[] _InStr)
{
if (!bKeySet)
return "";
String plaintext;
using (Aes aes = Aes.Create ())
{
aes.Key = Key;
aes.IV = IV;
//aes.Padding = PaddingMode.PKCS7;
//aes.BlockSize = 128;
//aes.KeySize = 128;
//aes.Mode = CipherMode.CBC;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(_InStr))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd ();
}
}
}
}
return plaintext;
}
As it was said, if any padding is used, the output will be aligned to a block size. However .Net doesn't want to work with incomplete blocks when PaddingMode.None is used. You should pad data yourself before encryption(decryption) and remove added bytes after.
One of the way to do this is to wrap ICryptoTransform passed to a CryptoStream