0

I'm getting the infamous "Padding is invalid and cannot be removed." error. However, despite ensuring the padding mode is set as well as verifying the key is the same on both encrypt and decrypt. I can't get past the issue.

What am I doing wrong o' great sages of the internet?

//Targeting .Net 4.5.2

//Key is hard coded, ONLY while working through this error in a minimum repo.
byte[] testKey = Convert.FromBase64String("KN1df3fOkLmSPyOP4r+grlVFDC/JVlWuew1u/hDGvUU=");

//Called to Encrypt ("D:\\Assets\\", "Test.txt")
public void DoWork(string filePath, string fileName){
    CryptographyUtil crypto = new CryptographyUtil(testKey);

    crypto.EncryptFile(filePath, fileName));
}

//Called to Decrypt ("D:\\Assets\\", "Test.txt.dat")
public void UnDoWork(string filePath, string fileName){
    CryptographyUtil crypto = new CryptographyUtil(testKey);

    crypto.DecryptFile(filePath, fileName));
}

public class CryptographyUtil(){
    RijndaelManaged rjndl;
    RNGCryptoServiceProvider cRng;
        
    public CryptographyUtil(byte[] key, int keySize = 256, int blockSize = 256) {
        cRng = new RNGCryptoServiceProvider();

        rjndl = new RijndaelManaged();
        rjndl.Key = key;
        rjndl.KeySize = keySize;
        rjndl.BlockSize = blockSize;
        rjndl.Mode = CipherMode.CBC;
        rjndl.Padding = PaddingMode.PKCS7;
    }

    public void EncryptFile(string filePath, string fileName) {
        string inputFilePath = Path.Combine(filePath, fileName);
        if(!File.Exists(inputFilePath)) {
            throw new FileLoadException("Unable to locate or open file.", inputFilePath);
        }
        
        string outputDirectory = Path.Combine(filePath, "Encrypted");
        
        if(!Directory.Exists(outputDirectory)){
            Directory.CreateDirectory(outputDirectory);
        }
        
        string outputPath = Path.Combine(outputDirectory, fileName + ".dat");
        
        //Create a unique IV each time
        byte[] iv = new byte[rjndl.BlockSize / 8]
        cRng.GetBytes(iv);
        byte[] ivSize = BitConverter.GetBytes(iv.Length);

        ICryptoTransform encryptor = rjndl.CreateEncryptor(rjndl.Key, iv);
        
        using(FileStream readStream = File.OpenRead(inputFilePath)) {
            using(FileStream writeStream = new FileStream(outputPath, FileMode.Create)) {
                using(CryptoStream encryptStream = new CryptoStream(writeStream, encryptor, CryptoStreamMode.Write)) {
                    //Write the following to the file before the encrypted data:
                    // - length of the IV
                    // - the IV 
                    writeStream.Write(ivSize, 0, ivSize.Length);
                    writeStream.Write(iv, 0, iv.Length);

                    readStream.CopyTo(encryptStream);
                    readStream.Flush();

                    encryptStream.FlushFinalBlock();
                    encryptStream.Close();
                }
                writeStream.Close();
            }
            readStream.Close();
        }
    }

    public void DecryptFile(string filePath, string fileName) {
        string outputDirectory = Path.Combine(filePath, "Decrypted");
        
        if(!Directory.Exists(outputDirectory)){
            Directory.CreateDirectory(outputDirectory);
        }
        
        //Remove the ".dat" from the end of the file
        string outputFilePath = Path.Combine(outputDirectory, fileName.Substring(0, fileName.LastIndexOf('.')));
        
        if(File.Exists(outputFilePath)){
            File.Delete(outputFilePath);
        }

        using(FileStream readStream = File.OpenRead(Path.Combine(filePath, fileName))) {
            //Size buffer for IV Length (int = 4 bytes)
            byte[] buffer = new byte[4];

            readStream.Read(buffer, 0, buffer.Length);
            int ivLength = BitConverter.ToUInt16(buffer, 0);

            //Re-size buffer for IV
            buffer = new byte[ivLength];

            //Read IV to buffer and use to create decryptor
            readStream.Read(buffer, 0, ivLength);
        
            //Use IV in buffer to create decryptor
            ICryptoTransform decryptor = rjndl.CreateDecryptor(rjndl.Key, buffer);

            buffer = new byte[1024];

            using(FileStream writeStream = new FileStream(outputFilePath, FileMode.Create)) {
                using(CryptoStream decryptStream = new CryptoStream(readStream, decryptor, CryptoStreamMode.Read)) {

                    //FIXME: Padding Error
                    int readIdx = decryptStream.Read(buffer, 0, buffer.Length);
                    while(readIdx > 0) {
                        writeStream.Write(buffer, 0, readIdx);
                        readIdx = decryptStream.Read(buffer, 0, buffer.Length);
                    }

                    decryptStream.Flush();
                    decryptStream.Close();
                }
                writeStream.Close();
            }
            readStream.Close();
        }
    }       
}

Using RijndaelManaged as I have to create encrypted packages for legacy software that uses it to decrypt.

Reahreic
  • 596
  • 2
  • 7
  • 26
  • 3
    Remove the line `rjndl.KeySize = keySize`. This triggers key generation, overwriting the previously set key. Note that a block size of 32 bytes is not AES. – Topaco Feb 06 '23 at 22:10
  • Thank you, I figured it was something stupid I wasn't seeing, just didn't think it'd be as stupid as the key being nuked when setting the size. (Makes sense though as the array would be re-initialized). My desire is to migrate this app, and the app it's creating packages for to AES, just need the request to get to the top of the task list, and stay there for a little. – Reahreic Feb 07 '23 at 12:33

0 Answers0