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.