There seems to be a convention in which an IV can be stored in plaintext in the beginning of an AES encrypted file.
I can encrypt and decrypt a file successfully when providing both the key and the IV during both encryption and decryption. This uses the code below, with slight modification. If that code would help, please LMK and I'll add it.
However, if I try to write the IV to the beginning of the file in plain text, I cannot decrypt the content (not sure if it's really being encrypted right either).
Can someone point out what's wrong below? Not sure why the .NET framework doesn't have this a a built in option.
If anyone can point out ways in which I'm not following the aforementioned convention, please do point them out!
using System;
using System.Security.Cryptography;
public class AESBase : IDisposable
{
protected AesManaged AES;
protected ICryptoTransform CryptoTransform;
public AESBase(byte[] key, byte[] iv = null)
{
AES = new AesManaged
{
BlockSize = 128,
KeySize = 256,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
Key = key
};
if (iv != null) { AES.IV = iv; }
}
}
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
internal class AESFiles : AESBase, IFileCrytpo
{
internal AESFiles(byte[] key, byte[] iv) : base(key, iv) { }
#region internal methods
public void Encrypt(string inputFileName, string outputFileName, bool overwriteFile)
{
CryptoTransform = AES.CreateEncryptor(AES.Key, AES.IV);
if (overwriteFile)
{
DeleteFile(outputFileName);
}
Transform(inputFileName, outputFileName, true);
}
public void Decrypt(string inputFileName, string outputFileName, bool overwriteFile)
{
CryptoTransform = AES.CreateDecryptor(AES.Key, AES.IV);
if (overwriteFile)
{
DeleteFile(outputFileName);
}
Transform(inputFileName, outputFileName, false);
}
#endregion public methods
#region private methods
private void Transform(string inputFileName, string outputFileName, bool encrypt)
{
var destination = new FileStream(outputFileName, FileMode.CreateNew, FileAccess.Write, FileShare.None);
if (encrypt)
{
//put the IV unencrypted in the front of the string
destination.Write(AES.IV, 0, AES.BlockSize / 8);
}
var source = new FileStream(inputFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
if (!encrypt)
{
source.Read(AES.IV, 0, AES.BlockSize / 8);
//var temp = Encoding.UTF8.GetString(AES.IV);
}
Transform(source, destination, CryptoTransform);
}
private static void Transform(Stream inputStream, Stream outputStream, ICryptoTransform transform)
{
using (var cryptoStream = new CryptoStream(outputStream, transform, CryptoStreamMode.Write))
{
//inputStream.Position = AES.BlockSize/8 + 1; CryptographicException : Length of the data to decrypt is invalid.
//inputStream.Position = AES.BlockSize/8; CryptographicException : Padding is invalid and cannot be removed.
inputStream.CopyTo(cryptoStream);
cryptoStream.FlushFinalBlock();
}
}
private static void DeleteFile(string fileName)
{
if (File.Exists(fileName))
{
File.Delete(fileName);
}
}
#endregion private methods
}
[TestFixture]
class AESFilesTest
{
private const string Path = #"C:\Users\Joe\Desktop\";
private const string FileInput = "Input.csv";
private const string FileEncrypted = "Encrypted.csv";
private const string FileDecrypted = "Decrypted.csv";
private readonly string _fileContents = String.Format("Test3,Test4" + Environment.NewLine, "Test5,Test6");
private readonly byte[] _key;
private readonly byte[] _iv;
private readonly Engine _engine;
public AESFilesTest()
{
_engine = new Engine();
_key = Encoding.UTF8.GetBytes("CEC520FA51EA0A47E87295FA32442605"); //test key
_iv = Encoding.UTF8.GetBytes("FB423A0BCB2AF4A4"); //test iv
File.WriteAllText(Path + FileInput, _fileContents);
}
[Test]
public void decrypted_text_matches_original()
{
const string inputFileWithPath = Path + FileInput;
_engine.Encrypt(_key, _iv, inputFileWithPath, Path + FileEncrypted, true);
_engine.Decrypt(_key, Path + FileEncrypted, Path + FileDecrypted, true);
var decrypted = File.ReadAllText(Path + FileDecrypted);
Console.WriteLine(decrypted);
Assert.AreEqual(_fileContents, decrypted);
}
There were many errors... The correct code:
private void Transform(string inputFileName, string outputFileName, bool encrypt)
{
using (var source = new FileStream(inputFileName, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var destination = new FileStream(outputFileName, FileMode.CreateNew, FileAccess.Write, FileShare.None))
{
ICryptoTransform cryptoTransform;
if (encrypt)
{
//put the IV unencrypted in the front of the string
destination.Write(AES.IV, 0, AES.BlockSize / 8);
cryptoTransform = AES.CreateEncryptor(AES.Key, AES.IV);
}
else
{
byte[] bytes = new byte[AES.BlockSize / 8];
source.Read(bytes, 0, bytes.Length);
AES.IV = bytes;
cryptoTransform = AES.CreateDecryptor(AES.Key, AES.IV);
}
Transform(source, destination, cryptoTransform, encrypt);
}
}
private static void Transform(Stream inputStream, Stream outputStream, ICryptoTransform transform, bool encrypt)
{
using (var cryptoStream = new CryptoStream(encrypt ? outputStream : inputStream, transform, encrypt ? CryptoStreamMode.Write : CryptoStreamMode.Read))
{
//inputStream.Position = AES.BlockSize/8 + 1; CryptographicException : Length of the data to decrypt is invalid.
//inputStream.Position = AES.BlockSize/8; CryptographicException : Padding is invalid and cannot be removed.
if (encrypt)
{
inputStream.CopyTo(cryptoStream);
// Not needed. Done by the Dispose()
//cryptoStream.FlushFinalBlock();
}
else
{
cryptoStream.CopyTo(outputStream);
}
}
}
then remove the now useless
protected ICryptoTransform CryptoTransform;
In general, the handling of the encrypt and of the decrypt operation are totally different... In many places the parameters change.
Another problem was in the reading of the IV: you can't read directly into the IV property of AesManaged, you have to read to a temporary buffer (bytes) and then assign the buffer to IV.
Third problem: you have to create the CryptoTransform when you have the Key and the IV and you know if you want to encrypt or decrypt.
Ah... and note that you aren't writing the plain text IV, because the IV isn't text, it is binary. You are writing the IV in binary format (or in its native format... or whatever you want to call it). Unless with plaintext you meant the non-encrypted IV. Then yes, you are writing the plaintext version of it. But still you can't encode it with Encoding.UTF8, because it isn't a "text"... It is binary data.
Related
I have the following class with service for AES encryption and decryption. Everything works fine until .Net 5. After migrating to .Net 6, the decryption method returns incomplete text.
Cryptography class:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace framework.mstiCSharp.Services
{
public class CriptografiaService
{
#region Settings
private static int _iterations = 2;
private static int _keySize = 256;
private static string _hash = "SHA1";
private static string _salt = "aselrias38490a32"; // Random
private static string _vector = "8947az34awl34kjq"; // Random
#endregion
public static string Encrypt(string value, string AesKey)
{
return Encrypt<AesManaged>(value, AesKey);
}
private static string Encrypt<RijndaelManaged>(string value, string password)
where RijndaelManaged : SymmetricAlgorithm, new()
{
byte[] vectorBytes = Encoding.UTF8.GetBytes(_vector);
byte[] saltBytes = Encoding.UTF8.GetBytes(_salt);
byte[] valueBytes = Encoding.UTF8.GetBytes(value);
byte[] encrypted;
using (RijndaelManaged cipher = new RijndaelManaged())
{
PasswordDeriveBytes _passwordBytes =
new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
byte[] keyBytes = _passwordBytes.GetBytes(_keySize / 8);
cipher.Mode = CipherMode.CBC;
using (ICryptoTransform encryptor = cipher.CreateEncryptor(keyBytes, vectorBytes))
{
using (MemoryStream to = new MemoryStream())
{
using (CryptoStream writer = new CryptoStream(to, encryptor, CryptoStreamMode.Write))
{
writer.Write(valueBytes, 0, valueBytes.Length);
writer.FlushFinalBlock();
encrypted = to.ToArray();
}
}
}
cipher.Clear();
}
return Convert.ToBase64String(encrypted);
}
public static string Decrypt(string value, string AesKey)
{
return Decrypt<AesManaged>(value, AesKey);
}
private static string Decrypt<T>(string value, string password) where T : SymmetricAlgorithm, new()
{
byte[] vectorBytes = Encoding.UTF8.GetBytes(_vector);
byte[] saltBytes = Encoding.UTF8.GetBytes(_salt);
byte[] valueBytes = Convert.FromBase64String(value);
byte[] decrypted;
int decryptedByteCount = 0;
using (T cipher = new T())
{
PasswordDeriveBytes _passwordBytes = new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
byte[] keyBytes = _passwordBytes.GetBytes(_keySize / 8);
cipher.Mode = CipherMode.CBC;
try
{
using (ICryptoTransform decryptor = cipher.CreateDecryptor(keyBytes, vectorBytes))
{
using (MemoryStream from = new MemoryStream(valueBytes))
{
using (CryptoStream reader = new CryptoStream(from, decryptor, CryptoStreamMode.Read))
{
decrypted = new byte[valueBytes.Length];
decryptedByteCount = reader.Read(decrypted, 0, decrypted.Length);
}
}
}
}
catch (Exception)
{
return String.Empty;
}
cipher.Clear();
}
return Encoding.UTF8.GetString(decrypted, 0, decryptedByteCount);
}
}
}
Test class:
using framework.mstiCSharp.Services;
using Xunit;
namespace framework.mstiCSharpTest
{
public class CriptografiaServiceTest
{
[Theory]
[InlineData("abc123")]
[InlineData("Silvair Leite Soares")]
[InlineData("abcdefghij 1234567890")]
public void encriptTest(string originalText)
{
string key = "1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm";
string encriptText = CriptografiaService.Encrypt(originalText, key);
string decriptText = CriptografiaService.Decrypt(encriptText, key);
Assert.Equal(originalText, decriptText);
}
}
}
I found this other question Problem Updating to .Net 6 - Encrypting String, and this issue AES Encryption broken on Dot Net 6, on this same subject. But I couldn't adapt the answers and suggestions to my situation.
Test cases:
Test
1
Original text
Silvair Leite Soares
After encryption
u2LcEdwpyTT4j+YJSYenJzFz0o+t0027DbvOn/i8bjU=
After decrypting
Silvair Leite So
Test
2
Original text
abc123
After encryption
k3tyVen0ulvrsLJ/MuVevA==
After decrypting
abc123
Test
3
Original text
abcdefghij 1234567890
After encryption
BUCUPbc1PhLOBvV9R2XP2NE7bWYQi5O4BjciiZd70pI=
After decrypting
abcdefghij 12345
As can be seen, in some situations, with smaller text, it still works. But in larger texts, it fails every time.
Any help will be most welcome.
encryption in this way works fine but when i try to use Decrypt it gives me padding Error i don't know why ?? Here is main Method and used Key & IV Value.
& BaseEncryptor Class Where i Use Encryption & Decryption Method
I tried uesing every type in padding but it also doesn't work.I tried to change block size and key size it also doesn't work.
static void Main(string[] args)
{
string key = "ldm_encrypt_code1234567891234567";
string IV = "9876897123651785";
string encstr = BaseEncryptor.EncryptUsingAes(Encoding.UTF8.GetBytes(#"Hello Code"), Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(IV)); //Encryption
string xc=BaseEncryptor.Decrypt(Encoding.UTF8.GetBytes(encstr), Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(IV)); //Decryption way
}
public class BaseEncryptor
{
public static string EncryptUsingAes(byte[] inputTextByte, byte[] privateKeyByte, byte[] sharedIVKeyByte)
{
using (var aesEncryption = AesEncryptionType(privateKeyByte, sharedIVKeyByte))
{
// Convert string to byte array
byte[] dest = new byte[inputTextByte.Length];
// encryption
using (ICryptoTransform encrypt = aesEncryption.CreateEncryptor(aesEncryption.Key, aesEncryption.IV))
{
dest = encrypt.TransformFinalBlock(inputTextByte, 0, inputTextByte.Length);
encrypt.Dispose();
}
return BitConverter.ToString(dest).Replace("-", "");
}
}
public static AesCryptoServiceProvider AesEncryptionType(byte[] privateKeyByte, byte[] sharedIVKeyByte)
{
//used in both encryption & Decryption
var aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.IV = sharedIVKeyByte;
aes.Key = privateKeyByte;
aes.Mode = CipherMode.CBC; //Mode
aes.Padding = PaddingMode.PKCS7; //Padding not working in case of none or Zeros
return aes;
}
public static string DecryptUsingAes(byte[] inputTextByte, byte[] privateKeyByte, byte[] sharedIVKeyByte)
{
using (var aesDecryption = AesEncryptionType(privateKeyByte, sharedIVKeyByte))
{
// Convert string to byte array
byte[] dest = new byte[inputTextByte.Length];
// encryption
using (ICryptoTransform decrypt = aesDecryption.CreateDecryptor(aesDecryption.Key, aesDecryption.IV))
{
dest = decrypt.TransformFinalBlock(inputTextByte, 0, inputTextByte.Length);
decrypt.Dispose();
}
// Convert byte array to UTF8 string
return Encoding.UTF8.GetString(dest); ;
}
}
public static string Decrypt(byte[] cipherText, byte[] Key, byte[] IV)
{
string plaintext = null;
// Create AesManaged
using (var aesDecryption = AesEncryptionType(Key, IV))
{
// Create a decryptor
// Create the streams used for decryption.
using (ICryptoTransform decryptor = aesDecryption.CreateDecryptor(Key, IV))
{
string result;
using (var msDecrypt = new MemoryStream(cipherText))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
result = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}
}
I try to build simple AES encryption helper to encrypt/decrypt some strings
Fist, I have an issue with Padding mode wherein decryption it only accepts if Zero otherwise an error about padding occurs!
The second issue is when I try to encrypt simple string "Hello World," it got encrypted, and I have the base64 string, but when trying to decrypt, there's no error, but a weird unknown character is shown! like 㡲啁䎰廾ử靱㡲啁䎰廾ử靱
My code:
private static int keySizes = 256;
private static int blockSize = 128;
private static PaddingMode pMode = PaddingMode.Zeros;
private static CipherMode cMode = CipherMode.ECB;
private static byte[] key = GenEncryptionKey();
private const string passphrase = #"StartYourMorningWithASmile";
private static byte[] GenEncryptionKey()
{
HashAlgorithm hash = MD5.Create();
return hash.ComputeHash(Encoding.Unicode.GetBytes(passphrase));
}
private static AesManaged CreateCryptor()
{
AesManaged cryptor = new AesManaged();
cryptor.KeySize = keySizes;
cryptor.BlockSize = blockSize;
cryptor.Padding = pMode;
cryptor.Key = key;
cryptor.Mode = cMode;
cryptor.GenerateIV();
return cryptor;
}
public static string EncryptParams(string reqVal)
{
string cipherText = "";
if (string.IsNullOrEmpty(reqVal) || reqVal.Length < 1)
throw new ArgumentNullException();
byte[] plainBytes = Encoding.Unicode.GetBytes(reqVal);
using (var cryptor = CreateCryptor())
{
ICryptoTransform encryptor = cryptor.CreateEncryptor();
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(plainBytes, 0, plainBytes.Length);
}
byte[] cipherBytes = ms.ToArray();
cipherText = Convert.ToBase64String(cipherBytes);
}
cryptor.Clear();
}
return cipherText;
}
public static string DecryptParams(string resVal)
{
var data = Convert.FromBase64String(resVal);
byte[] cipherBytes = new byte[data.Length];
string plainText = "";
using (var crypto = CreateCryptor())
{
ICryptoTransform Dec = crypto.CreateDecryptor();
using (MemoryStream ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, Dec, CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
plainText = Encoding.Unicode.GetString(ms.ToArray());
}
}
crypto.Clear();
}
return plainText;
}
UPDATE 1:
Please set also the IV yourself to achieve successful decryption, as #maarten-bodewes pointed out. I missed that part and the decryption somehow worked (?) with your existing code, but you always should provide the same key and IV to a symmetric encryption algorithm to have it work both ways.
ORIGINAL ANSWER:
Your decryption fails (produces incorrect results) because you implemented the decryption part incorrectly (by using CryptoStreamMode.Write instead of CryptoStreamMode.Read) and besides feeding the decryption stream all zero bytes
At the point of execution of
cs.Write(cipherBytes, 0, cipherBytes.Length);
the variable cipherBytes is all zero. The real encrypted buffer is in the data variable which you only use to set the length of cipherBytes
So change your decryption method to this.
BONUS: After correcting the decryption part, you can specify the padding as you wish! I tested with PKCS7 and it is ok.
public static string DecryptParams(string resVal)
{
var cipherBytes = Convert.FromBase64String(resVal);
string plainText = "";
using (var crypto = CreateCryptor())
{
ICryptoTransform Dec = crypto.CreateDecryptor();
using (MemoryStream ms = new MemoryStream(cipherBytes))
{
using (var cs = new CryptoStream(ms, Dec, CryptoStreamMode.Read))
{
byte[] decryptBlock = new byte[4096];
MemoryStream decryptStream = new MemoryStream();
int readBytes;
while ((readBytes = cs.Read(decryptBlock, 0, 4096)) > 0)
{
decryptStream.Write(decryptBlock, 0, readBytes);
}
plainText = Encoding.Unicode.GetString(decryptStream.ToArray());
}
}
crypto.Clear();
}
return plainText;
}
Hope this helps.
Thanks to Oguz
Below is my description method after edit
public static string DecryptParams(string resVal)
{
var data = Convert.FromBase64String(resVal);
byte[] cipherBytes = new byte[data.Length];
string plainText = "";
using (var crypto = CreateCryptor())
{
ICryptoTransform Dec = crypto.CreateDecryptor();
using (MemoryStream ms = new MemoryStream(data))
{
using (var cs = new CryptoStream(ms, Dec, CryptoStreamMode.Read))
{
cs.Read(cipherBytes, 0, cipherBytes.Length);
plainText = Encoding.Unicode.GetString(cipherBytes.ToArray());
}
}
crypto.Clear();
}
return plainText;
}
one more thing about the return result after the decryption I got the original string plus \0\0\0\0 so I use myString.TrimEnd('\0') to solve that.
I'm building a game which needs encrypted saves etc. So I'm making a few functions to help me encrypt them. However, the only function I currently have uses FileStreams for input and output, but I would like to use strings instead. The functions work fine on Files, but I'm having trouble swapping from FileStreams to MemoryStreams to strings.
NOTE: I HAVE REMOVED IRRELEVANT CODE. OurCodeWorld.GenerateRandomSalt() 100% works, tested with FileStream encryption
FULL CODE:
Program.cs:
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
namespace Encryption_test
{
class Program
{
static public string encryptedExtension = ".aes";
static public string decryptedExtension = ".decrypted";
static void Main(string[] args)
{
string path = Environment.CurrentDirectory;
Log($"Current path: {path}");
string filePath = $"{path}/unencrypted.txt";
string message =
#"Hello world!
This is my message. 1234";
RunStackOverFlowString();
//Sleep forever...zzz
Thread.Sleep(10000);
Console.WriteLine();
float TicksToMs(long ticks)
{
return ticks / (float)Stopwatch.Frequency * 1000F;
}
void RunStackOverFlowString()
{
byte[] salt = OurCodeWorld.GenerateRandomSalt();
int iterations = 1024;
string password = "";
string enc = StackOverflow.EncryptString(message, password, salt, iterations);
Log($"Enc: {enc}");
string dec = StackOverflow.DecryptString(enc, password, salt, iterations);
Log($"Dec: {dec}");
}
private static void WriteFile(string path, string value)
{
FileStream _file = File.OpenWrite(path);
byte[] info = new UTF8Encoding(true).GetBytes(value);
_file.Write(info, 0, info.Length);
_file.Close();
}
private static string ReadFile(string filePath, long length = long.MaxValue)
{
FileStream _file = File.OpenRead(filePath);
if (length == long.MaxValue)
length = _file.Length;
byte[] b = new byte[length];
UTF8Encoding temp = new UTF8Encoding(true);
_file.Read(b, 0, b.Length);
_file.Close();
return temp.GetString(b);
}
private static void DeleteFile(string path)
{
File.Delete(path);
}
private static void CreateFile(string path)
{
if (File.Exists(path))
DeleteFile(path);
FileStream a = File.Open(path, FileMode.Create);
a.Close();
}
static void Log(string message)
{
Console.WriteLine(message);
}
}
}
StackOverFlow.cs:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
class StackOverflow
{
// Rfc2898DeriveBytes constants:
/// <summary>Decrypt a file.</summary>
/// <remarks>NB: "Padding is invalid and cannot be removed." is the Universal CryptoServices error. Make sure the password, salt and iterations are correct before getting nervous.</remarks>
/// <param name="sourceFilename">The full path and name of the file to be decrypted.</param>
/// <param name="destinationFilename">The full path and name of the file to be output.</param>
/// <param name="password">The password for the decryption.</param>
/// <param name="salt">The salt to be applied to the password.</param>
/// <param name="iterations">The number of iterations Rfc2898DeriveBytes should use before generating the key and initialization vector for the decryption.</param>
public static void DecryptFile(string sourceFilename, string destinationFilename, string password, byte[] salt, int iterations)
{
AesManaged aes = new AesManaged();
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
// NB: Rfc2898DeriveBytes initialization and subsequent calls to GetBytes must be eactly the same, including order, on both the encryption and decryption sides.
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations);
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = key.GetBytes(aes.BlockSize / 8);
aes.Mode = CipherMode.CBC;
ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);
using (FileStream destination = new FileStream(destinationFilename, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
{
try
{
using (FileStream source = new FileStream(sourceFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
{
source.CopyTo(cryptoStream);
}
}
catch (CryptographicException exception)
{
if (exception.Message == "Padding is invalid and cannot be removed.")
throw new ApplicationException("Universal Microsoft Cryptographic Exception (Not to be believed!)", exception);
else
throw;
}
}
}
}
/// <summary>Encrypt a file.</summary>
/// <param name="sourceFilename">The full path and name of the file to be encrypted.</param>
/// <param name="destinationFilename">The full path and name of the file to be output.</param>
/// <param name="password">The password for the encryption.</param>
/// <param name="salt">The salt to be applied to the password.</param>
/// <param name="iterations">The number of iterations Rfc2898DeriveBytes should use before generating the key and initialization vector for the decryption.</param>
public static void EncryptFile(string sourceFilename, string destinationFilename, string password, byte[] salt, int iterations)
{
AesManaged aes = new AesManaged();
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
// NB: Rfc2898DeriveBytes initialization and subsequent calls to GetBytes must be eactly the same, including order, on both the encryption and decryption sides.
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations);
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = key.GetBytes(aes.BlockSize / 8);
aes.Mode = CipherMode.CBC;
ICryptoTransform transform = aes.CreateEncryptor(aes.Key, aes.IV);
using (FileStream destination = new FileStream(destinationFilename, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
{
using (FileStream source = new FileStream(sourceFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
{
source.CopyTo(cryptoStream);
}
}
}
}
//THIS IS MY CODE
public static string EncryptString(string inputString, string password, byte[] salt, int iterations)
{
AesManaged aes = new AesManaged();
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
// NB: Rfc2898DeriveBytes initialization and subsequent calls to GetBytes must be eactly the same, including order, on both the encryption and decryption sides.
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations);
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = key.GetBytes(aes.BlockSize / 8);
aes.Mode = CipherMode.CBC;
ICryptoTransform transform = aes.CreateEncryptor(aes.Key, aes.IV);
var source = StringToStream(inputString);
var output = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(output, transform, CryptoStreamMode.Write);
source.CopyTo(cryptoStream);
return StreamToString(output);
}
public static string DecryptString(string inputString, string password, byte[] salt, int iterations)
{
AesManaged aes = new AesManaged();
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
// NB: Rfc2898DeriveBytes initialization and subsequent calls to GetBytes must be eactly the same, including order, on both the encryption and decryption sides.
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations);
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = key.GetBytes(aes.BlockSize / 8);
aes.Mode = CipherMode.CBC;
ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);
var source = StringToStream(inputString);
var output = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(output, transform, CryptoStreamMode.Write);
source.CopyTo(cryptoStream);
return StreamToString(output);
}
public static Stream StringToStream(string s)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
public static string StreamToString(Stream s)
{
s.Position = 0;
byte[] buffer = new byte[s.Length];
s.Read(buffer, 0, (int)s.Length);
return Encoding.Default.GetString(buffer);
}
}
I have already tested the StreamToString and StringToStream methods, and they work fine. When I run DecryptString, there is no output, and the function returns garbled strings, often looking similar to this Dec: ?K???
?#?????n?l?r????T?
I tested the code. There are two issues:
You take encrypted bytes and convert them to a string using an encoding. This process will fail in general because you can't take arbitrary bytes and convert them to a string. The mapping between bytes and strings is not 1:1. Likely, you should not work with strings at all here. Rather, keep the data in byte format and write those bytes to a file. The method signature should be like byte[] Encrypt(byte[] input, ...). The encryption code has no business converting from and to strings.
EncryptString must flush the crypto stream using FlushFinalBlock. If this is not done data at the end will be missing.
Conversion between strings and binary data is done with and Encoding. The encoding determines how the characters will be converted into binary data. Common encosings are Unicode, UTF8 or ASCII. If you want predicatble results you need to use a specific encoding for both conversions.
In your StringToStream you do not specify your encoding at all, while you use Encoding.Default in StreamToString. Encoding.Default does not provide a predictable result, because it uses the current code page (if you are running on a Windows system). So you can not be sure that you are using the same encoding in both methods.
The solution is to provide the same encoding on both ends:
public static Stream StringToStream(string s)
{
byte[] buffer = Encoding.UTF8.GetBytes(s);
return new MemoryStream(buffer);
}
public static string StreamToString(Stream s)
{
s.Position = 0;
byte[] buffer = new byte[s.Length];
s.Read(buffer, 0, (int)s.Length);
return Encoding.UTF8.GetString(buffer);
}
I am attempting to test a simple class to encrypt and decrypt data in C#.
`
{ [TestFixture]
public class CryptTest
{
[Test]
public void TestMethod()
{
String text = "Hello World!";
String crypt = EncryptionService.Encrypt(text, Config.KEY_STRING);
Console.WriteLine(crypt);
String clear = EncryptionService.Decrypt(crypt, Config.KEY_STRING);
Console.WriteLine(clear);
Assert.That(clear, Is.EqualTo(text));
}
`However, I am receiving the following exception:
Message: System.Security.Cryptography.CryptographicException : Padding is invalid and cannot be removed.
with the stack:
StackTrace " at System.Security.Cryptography.CapiSymmetricAlgorithm.DepadBlock(Byte[] block, Int32 offset, Int32 count)\r\n at System.Security.Cryptography.CapiSymmetricAlgorithm.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)\r\n at System.Security.Cryptography.CryptoStream.Read(Byte[] buffer, Int32 offset, Int32 count)\r\n at System.IO.StreamReader.ReadBuffer()\r\n at System.IO.StreamReader.ReadToEnd()\r\n at InsuranceMidAm.Services.EncryptionService.Decrypt(String cipher, String key) in C:\\Visual Studio Projects\\InsuranceMidAm\\InsuranceMidAm\\Services\\EncryptionService.cs:line 72\r\n at InsuranceMidAm.Tests.CryptTest.TestMethod() in C:\\Visual Studio Projects\\InsuranceMidAm\\InsuranceMidAm.Tests\\CryptTest.cs:line 20" string
This is the class under test:
namespace InsuranceMidAm.Services
{
public class EncryptionService
{
// Reference: https://stackoverflow.com/questions/273452/using-aes-encryption-in-c-sharp
public static String Encrypt(String text, String key)
{
byte[] value = UTF8Encoding.UTF8.GetBytes(text);
byte[] crypt;
byte[] iv;
using (Aes myAes = Aes.Create())
{
myAes.KeySize = 256;
myAes.Mode = CipherMode.CBC;
myAes.Key = HexToBin(key);
myAes.GenerateIV();
myAes.Padding = PaddingMode.PKCS7;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, myAes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(value, 0, value.Length);
cs.FlushFinalBlock();
crypt = ms.ToArray();
}
}
iv = myAes.IV;
myAes.Clear();
}
return ByteArrayToString(crypt) + ":" + ByteArrayToString(iv);
}
public static string Decrypt(String cipher, String key)
{
String outputString = "";
byte[] ivBytes = HexToBin(getIV(cipher));
byte[] valBytes = HexToBin(getSSN(cipher));
using (Aes myAes = Aes.Create())
{
int size = valBytes.Count();
myAes.KeySize = 256;
myAes.Mode = CipherMode.CBC;
myAes.Key = HexToBin(key);
myAes.IV = ivBytes;
myAes.Padding = PaddingMode.PKCS7;
char[] output = new char[256];
ICryptoTransform myDecrypter = myAes.CreateDecryptor(myAes.Key, myAes.IV);
using (MemoryStream memory = new MemoryStream(ivBytes))
{
using (CryptoStream cryptStream = new CryptoStream(memory, myDecrypter, CryptoStreamMode.Read))
{
using (StreamReader reader = new StreamReader(cryptStream))
{
outputString = reader.ReadToEnd();
}
return outputString;
}
}
}
}
private static byte[] HexToBin(String hexString)
{
int charCount = hexString.Length;
byte[] output = new byte[charCount / 2];
for (int i = 0; i < charCount; i += 2)
{
output[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
}
return output;
}
private static String getSSN(String cipher)
{
int delimiterIndex = cipher.IndexOf(":");
String SSN = cipher.Substring(0, delimiterIndex);
return SSN;
}
private static String getIV(String cipher)
{
int delimiterIndex = cipher.IndexOf(":");
String IV = cipher.Substring(delimiterIndex + 1);
return IV;
}
// Reference: https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa
private static string ByteArrayToString(byte[] ba)
{
string hex = BitConverter.ToString(ba);
return hex.Replace("-", "");
}
}
}
Line 73 (where the exception is encountered) is the end of the using block for the StreamReader in the decrypt method:
using (StreamReader reader = new StreamReader(cryptStream))
{
outputString = reader.ReadToEnd();
}
I referenced the following question, but could not resolve my issue.
Originally, the data was encrypted in a PHP application, and decrypted using a C# application (using nearly exactly the same decrypt method above). Now, I am wanting to both encrypt and decrypt the data using C#; however, I must still be able to properly decrypt the existing data (that was encrypted using PHP), so I would rather not modify the decrypt method too much.
Any advice would be appreciated.
You have minor mistake here:
ICryptoTransform myDecrypter = myAes.CreateDecryptor(myAes.Key, myAes.IV);
using (MemoryStream memory = new MemoryStream(ivBytes))
You pass your IV value to decrypt, instead of actually encrypted bytes. Fix:
ICryptoTransform myDecrypter = myAes.CreateDecryptor(myAes.Key, myAes.IV);
using (MemoryStream memory = new MemoryStream(valBytes))