Search code examples
c#streamdeflatestreamcryptostream

Unable to resolve compression, encryption, decryption, decompression stream implementation


I've been fighting with chained using statements, and am unable to resolve the latest in a long line of implementation issues. I need to compress, then encrypt and append the generated IV to the selected file. This all appears to work correctly, however i'm unable to unwind the process. After looking at several similar stack postings and articles i'm still unable to get it to work and am now after more direct assistance.

The latest thrown error is System.IO.InvalidDataException: 'Found invalid data while decoding.' It appears that the decryption stream isn't functioning as intended and that's throwing the decompression stream out of wack.

byte[] key;
byte[] salt;
const int keySize = 256;
const int blockSize = keySize;
byte[] iv = new byte[blockSize / 8];//size to bits
RijndaelManaged rjndl;
RNGCryptoServiceProvider cRng;

void InitializeCryptor() {
    //Temporarily define the salt & key
    salt = Encoding.UTF8.GetBytes("SaltShouldBeAtLeast8Bytes");
    key = new Rfc2898DeriveBytes("MyL0ngPa$$phra$e", salt, 4).GetBytes(keySize / 8);

    //Initialize the crypto RNG generator
    cRng = new RNGCryptoServiceProvider();

    // Create instance of Rijndael (AES) for symetric encryption of the data.
    rjndl = new RijndaelManaged();
    rjndl.KeySize = keySize;
    rjndl.BlockSize = blockSize;
    rjndl.Mode = CipherMode.CBC;
}

void CompressAndEncryptFile(string relativeFilePath, string fileName) {
    //Create a unique IV each time
    cRng.GetBytes(iv);

    //Create encryptor
    rjndl.Key = key;
    rjndl.IV = iv;
    ICryptoTransform encryptor = rjndl.CreateEncryptor(rjndl.Key, rjndl.IV);

    //Create file specific output sub-directory
    Directory.CreateDirectory(Path.Combine(outputPath, relativeFilePath));

    //Read and compress file into memory stream
    using (FileStream readStream = File.OpenRead(Path.Combine(initialpath, relativeFilePath, fileName)))
            using (FileStream writeStream = new FileStream(Path.Combine(outputPath, relativeFilePath, fileName + ".dat"), FileMode.Create))
    using (CryptoStream encryptStream = new CryptoStream(writeStream, encryptor, CryptoStreamMode.Write))
    using (DeflateStream compStream = new DeflateStream(encryptStream, CompressionLevel.Optimal)) {
        //Write the following to the FileStream for the encrypted file:
        // - length of the IV
        // - the IV
        byte[] ivSize = BitConverter.GetBytes(rjndl.IV.Length);
        writeStream.Write(ivSize, 0, 4);
        writeStream.Write(rjndl.IV, 0, rjndl.BlockSize / 8);

        readStream.CopyTo(compStream);
    }
}

void DecryptAndDecompressFile(string relativeFilePath) {
    string outputPath = Path.Combine(initialpath, "Unpack");
    Directory.CreateDirectory(outputPath);

    using (FileStream readStream = new FileStream(Path.Combine(initialpath, manifestData.version, relativeFilePath + ".dat"), FileMode.Open)) {
        byte[] tmpLength = new byte[4];

        //Read length of IV
        readStream.Seek(0, SeekOrigin.Begin);
        readStream.Read(tmpLength, 0, 3);

        int ivLength = BitConverter.ToInt32(tmpLength, 0);

        byte[] readIv = new byte[ivLength];

        //Read IV
        readStream.Seek(4, SeekOrigin.Begin);
        readStream.Read(readIv, 0, ivLength);
        rjndl.IV = readIv;

        //Start at beginning of encrypted data
        readStream.Seek(4 + ivLength, SeekOrigin.Begin);

        //Create decryptor
        ICryptoTransform decryptor = rjndl.CreateEncryptor(key, readIv);
        using (CryptoStream decryptStream = new CryptoStream(readStream, decryptor, CryptoStreamMode.Read))
        using (DeflateStream decompStream = new DeflateStream(decryptStream, CompressionMode.Decompress))
        using (FileStream writeStream = new FileStream(Path.Combine(outputPath, relativeFilePath), FileMode.Create)) {
            decompStream.CopyTo(writeStream);
        }
    }
}

For those who like to point to other similar stack questions and vote to close/duplicate without offering support, the following are the threads, and posts I've worked through first, each without success.


Solution

  • After ~2 days of investigating, i located my error.

    I was calling rjndl.CreateEncryptor instead of rjndl.CreateDecryptor during the decryption portion... (Please tell me this type of $#!t happens to others too)

    Once i finish testing i'll update my question code to serve as a nice example for anyone who lands here via google in the future.