I'm trying to create a class to encrypt and decrypt large numbers of files with AES in C# .NET. However, during decryption I have an exception which is always thrown.
Encryptor class :
public class Encryptor
{
private const int KeySize = 256;
private const int BlockSize = 128;
private const CipherMode Cipher = CipherMode.CBC;
private const PaddingMode Padding = PaddingMode.PKCS7;
private byte[]? key;
private static byte[] ProtectData(byte[] data)
{
return ProtectedData.Protect(data, null, DataProtectionScope.CurrentUser);
}
private static byte[] UnprotectData(byte[] protectedData)
{
return ProtectedData.Unprotect(protectedData, null, DataProtectionScope.CurrentUser);
}
public void SetEncryptionKey(string key)
{
if (string.IsNullOrEmpty(key)) throw new ArgumentException("Key cannot be null or empty");
this.key = ProtectData(Encoding.UTF8.GetBytes(key));
}
public void SetEncryptionKey(byte[] key)
{
if (key == null || key.Length == 0) throw new ArgumentException("Key cannot be null or empty");
this.key = ProtectData(key);
}
private void ValidateInputs(string inputFilepath, string? outputFilepath = null)
{
if (key is null) throw new ArgumentNullException("The key has not been defined");
if (!File.Exists(inputFilepath)) throw new FileNotFoundException("Input file doesn't exist");
if (outputFilepath is not null && File.Exists(outputFilepath)) throw new ArgumentException("Output file already exists");
}
public void DecryptFile(string inputFilepath, string outputFilepath)
{
ValidateInputs(inputFilepath, outputFilepath);
using (Aes aes = Aes.Create())
{
aes.Key = UnprotectData(key);
aes.KeySize = KeySize;
aes.BlockSize = BlockSize;
aes.Mode = Cipher;
aes.Padding = Padding;
using (FileStream inputFileStream = new FileStream(inputFilepath, FileMode.Open, FileAccess.Read, FileShare.None))
{
byte[] buffer = new byte[BlockSize/8];
inputFileStream.Read(buffer, 0, buffer.Length);
inputFileStream.Seek(BlockSize / 8, SeekOrigin.Begin);
aes.IV = buffer;
using (FileStream outputFileStream = new FileStream(outputFilepath, FileMode.Create, FileAccess.Write, FileShare.None))
using (CryptoStream cryptoStream = new CryptoStream(outputFileStream, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
inputFileStream.CopyTo(cryptoStream);
}
}
}
}
public void EncryptFile(string inputFilepath, string outputFilepath)
{
ValidateInputs(inputFilepath, outputFilepath);
using (Aes aes = Aes.Create())
{
aes.Key = UnprotectData(key);
aes.KeySize = KeySize;
aes.BlockSize = BlockSize;
aes.Mode = Cipher;
aes.Padding = Padding;
aes.GenerateIV();
using (FileStream inputFileStream = new FileStream(inputFilepath, FileMode.Open, FileAccess.Read, FileShare.None))
using (FileStream outputFileStream = new FileStream(outputFilepath, FileMode.Create, FileAccess.Write, FileShare.None))
using (CryptoStream cryptoStream = new CryptoStream(outputFileStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
outputFileStream.Write(aes.IV,0, aes.IV.Length);
inputFileStream.CopyTo(cryptoStream);
}
}
}
}
Main class :
public static Main(string[] args)
{
const ssl_nopass = "...";
const input = "...";
const output_encrypted = "...";
const output_decrypted = "..."
Encryptor encryptor = new Encryptor();
encryptor.SetEncryptionKey(File.ReadAllBytes(ssl_nopass));
encryptor.EncryptFile(input, output_encrypted + "ssl_nopass");
encryptor.DecryptFile(output_encrypted + "ssl_nopass", output_decrypted + "ssl_nopass.txt");
}
Exception :
System.Security.Cryptography.CryptographicException
HResult=0x80131501
Message=Padding is invalid and cannot be removed.
Source=System.Security.Cryptography
Procedure call tree :
at System.Security.Cryptography.SymmetricPadding.GetPaddingLength(ReadOnlySpan`1 block, PaddingMode paddingMode, Int32 blockSize)
at System.Security.Cryptography.UniversalCryptoDecryptor.UncheckedTransformFinalBlock(ReadOnlySpan`1 inputBuffer, Span`1 outputBuffer)
at System.Security.Cryptography.UniversalCryptoDecryptor.UncheckedTransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
at System.Security.Cryptography.UniversalCryptoTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
at System.Security.Cryptography.CryptoStream.<FlushFinalBlockAsync>d__30.MoveNext()
at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at Encryption.Encryptor.DecryptFile(String inputFilepath, String outputFilepath) dans C:\...\Encryptor.cs :ligne 108
at Program.<Main>$(String[] args) dans C:\...\Program.cs :ligne 21
How the key was created :
openssl rand -out key.bin 32
With debugging mode I have already verified that:
- the byte key used during encryption and decryption is same;
- the byte IV used during encryption and decryption is same.
Thank you for your help.
While decrypting, what you need to do is read data from the encrypted file through
CryptoStream.