0

At this point I'm quite confused about the implementation of an encryption/decryption of game data.

Write function

private void WriteEncryptedData<T>(T Data, string path)
{
    byte[] array;

    using (Aes aes = Aes.Create())  
    {  
        aes.Key = Convert.FromBase64String(KEY);
        aes.IV = Convert.FromBase64String(IV);
        aes.Padding = PaddingMode.PKCS7;

        ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

        using MemoryStream memoryStream = new();
        using CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write);
        cryptoStream.Write(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(Data)));
        cryptoStream.FlushFinalBlock();
        array = memoryStream.ToArray();
    }
    
    File.WriteAllText(path, Convert.ToBase64String(array));
}

And read function

private T ReadEncryptedData<T>(string path)
{
    var data = File.ReadAllText(path);
    byte[] array = Convert.FromBase64String(data);

    string result;

    using (Aes aes = Aes.Create())
    {
        aes.Key = Convert.FromBase64String(KEY);
        aes.IV = Convert.FromBase64String(IV);
        aes.Padding = PaddingMode.PKCS7;

        using ICryptoTransform cryptoTransform = aes.CreateDecryptor(aes.Key, aes.IV);
        using MemoryStream decryptionStream = new(array);
        using CryptoStream cryptoStream = new(
            decryptionStream,
            cryptoTransform,
            CryptoStreamMode.Read
        );
        
        cryptoStream.Flush();
        result = Encoding.UTF8.GetString(decryptionStream.ToArray());
    }

    Debug.Log($"Decrypted result (if the following is not legible, probably wrong key or iv): {result}");
    return JsonConvert.DeserializeObject<T>(result);
}

For some reason I get the following error and at this point I'm worried that I'm missing something important in the implementation. I'm using hard-coded KEY and IV. I plan to generate the IV dynamically.

Failed to load data due to: Bad PKCS7 padding. Invalid length 0. at Mono.Security.Cryptography.SymmetricTransform.ThrowBadPaddingException (System.Security.Cryptography.PaddingMode padding, System.Int32 length, System.Int32 position) [0x00056] in <381999f96ea944d79e461a81dcbea654>:0 at Mono.Security.Cryptography.SymmetricTransform.FinalDecrypt (System.Byte[] inputBuffer, System.Int32 inputOffset, System.Int32 inputCount) [0x00146] in <381999f96ea944d79e461a81dcbea654>:0 at Mono.Security.Cryptography.SymmetricTransform.TransformFinalBlock (System.Byte[] inputBuffer, System.Int32 inputOffset, System.Int32 inputCount) [0x0002e] in <381999f96ea944d79e461a81dcbea654>:0 at System.Security.Cryptography.CryptoStream.FlushFinalBlock () [0x00013] in <381999f96ea944d79e461a81dcbea654>:0 at System.Security.Cryptography.CryptoStream.Dispose (System.Boolean disposing) [0x0000b] in <381999f96ea944d79e461a81dcbea654>:0 at System.IO.Stream.Close () [0x00000] in <381999f96ea944d79e461a81dcbea654>:0 at System.IO.Stream.Dispose () [0x00000] in <381999f96ea944d79e461a81dcbea654>:0

Ando
  • 1,802
  • 4
  • 25
  • 47
  • check my (not only) answer here https://stackoverflow.com/questions/76284645/cryptojs-aes-decrypt-in-c-sharp/76284994#76284994 – dododo May 19 '23 at 22:33
  • 1
    @dododo Code only answers are not answers, and just replacing one code with another is not OK either. – Maarten Bodewes May 20 '23 at 00:01
  • Have you tried removing the call to `flush`, as that's the only part that doesn't make sense to me at first glance. **Edit** Oh, sheesh, your `decryptionStream` doesn't decrypt, it is the `MemoryStream` instance. Read from `cryptoStream` instead. Maybe rename that to `ciphertextStream` and `decryptionStream` respectively. And still remove the `flush`! – Maarten Bodewes May 20 '23 at 00:04
  • @MaartenBodewes can't really thank you enough for your comment! I spent so much time to figure this out but couldn't wrap my head around all the crypto methods and lingo. I used a StreamReader `using StreamReader reader = new(cryptoStream); result = reader.ReadToEnd();` It would be great if you can wrap your comment with a bit of description into an answer so I can accept it. Thanks again! – Ando May 20 '23 at 05:31
  • 1
    It is less error-prone if you follow the [MS example](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aes?view=net-7.0) (not least because here the necessary flush statements are executed implicitly). Also be aware of this [breaking change](https://stackoverflow.com/a/69911546/9014097) starting with .NET 6 (though effectively prevented now by your `ReadToEnd()`). – Topaco May 20 '23 at 06:10
  • @Ando Sorry, I've seen this error before, and I currently don't have a C# dev environment setup. Need to get to that (switched to Windows due to instability, finding out that it was my BIOS - oh well). – Maarten Bodewes May 20 '23 at 10:55
  • @Ando, I didn't say use my solution, I said: see, there is a solution that use the same approach as in your case, but doesn't fail, so you can analyze the difference to find solution for your case – dododo May 20 '23 at 11:15
  • cc @Maarten Bodewes – dododo May 20 '23 at 11:18
  • Yeah, well, that doesn't use the streaming API, as it probably should, it doesn't contain good key handling and it incorrectly treats a password as a key. There, reviewed it, it's shit. – Maarten Bodewes May 20 '23 at 11:30
  • @Topaco are you saying there is some issue with the flush statements? I'm not sure if I'm using them in the wrong way. In the MS link you shared, there doesn't seem to be any flush statements. I thought they are necessary to ensure the correct byte size... – Ando May 20 '23 at 12:09
  • 1
    @Ando - I don't know your final implementation, but as it seems to work, there are probably no issues with the flush calls. You don't find any explicit calls in the MS example, since they are all made implicitly and in the correct order (which is just the point, since you don't have to worry about it). – Topaco May 20 '23 at 14:37

0 Answers0