-1

I try write and read back string from file. Use this code for the write:

...
Aes aes = Aes.Create();
                aes.KeySize = 256;
                aes.BlockSize = 128;
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;
                Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(passwordBytes, salt, 185000, HashAlgorithmName.SHA512);
                aes.Key = key.GetBytes(aes.KeySize / 8);
                aes.IV = key.GetBytes(aes.BlockSize / 8);
                createstream.Write(salt, 0, salt.Length);

                using (DeflateStream compresstream = new DeflateStream(createstream, CompressionLevel.Optimal))
                {
                    using (CryptoStream cryptostream = new CryptoStream(compresstream, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        byte[] str = Encoding.UTF8.GetBytes("TestString")!;
                        byte[] strl = BitConverter.GetBytes(str.Length);
                        cryptostream.Write(strl, 0, 4);
                        cryptostream.Write(str, 0, str.Length);

                        byte[] str1 = Encoding.UTF8.GetBytes("TestString2")!;
                        byte[] strl1 = BitConverter.GetBytes(str1.Length);
                        cryptostream.Write(strl1, 0, 4);
                        cryptostream.Write(str1, 0, str1.Length);
                    }
                }

And this for the read back:

...
readstream.Read(salt, 0, salt.Length);
...
 Aes aes = Aes.Create();
                aes.KeySize = 256;
                aes.BlockSize = 128;
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;
                Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(passwordBytes, salt, 185000, HashAlgorithmName.SHA512);
                aes.Key = key.GetBytes(aes.KeySize / 8);
                aes.IV = key.GetBytes(aes.BlockSize / 8);

                using (DeflateStream decompresstream = new DeflateStream(readstream, CompressionMode.Decompress))
                {
                    using (CryptoStream decryptostream = new CryptoStream(decompresstream, aes.CreateDecryptor(), CryptoStreamMode.Read))
                    {
                        byte[] str = new byte[4];
                        decryptostream.Read(str, 0, str.Length);
                        int size = BitConverter.ToInt32(str, 0);
                        byte[] strl = new byte[size];
                        decryptostream.Read(strl, 0, size);
                        string result = Encoding.UTF8.GetString(strl);

                        byte[] str1 = new byte[4];
                        decryptostream.Read(str1, 0, str1.Length);
                        int size1 = BitConverter.ToInt32(str1, 0);
                        byte[] strl1 = new byte[size1];
                        decryptostream.Read(strl1, 0, size1);
                        string result1 = Encoding.UTF8.GetString(strl1);

                        Console.WriteLine($"String: {result} {result1}");

                        Console.ReadLine();
                    }
                }

My problem is...after reading back, i get this result: String: TestString TestStrin. Why cryptostream is cut my string?

If i write the digits, the CryptoStream is read correctly for all. How to right write or read string correctly use CryptoStream?

Malcolm
  • 33
  • 1
  • 7
  • Start with some basic debugging: put breakpoints near the writes and read and make sure the integer length values written and read match exactly. I also wonder how you are observing this result. For example, the VS debugger view can truncate strings by default. – Joel Coehoorn Jul 17 '23 at 16:49
  • 1
    Probably caused by [this breaking change](https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/partial-byte-reads-in-streams), s. [this post](https://stackoverflow.com/a/69911546/9014097) for more details. – Topaco Jul 17 '23 at 17:43
  • Bcuz after write is **str1** have is **[84, 101, 115, 116, 83, 116, 114, 105, 110, 103, 50]** bytes, but after reading **strl1** is have **[0, 0, 84, 101, 115, 116, 83, 116, 114, 105, 110]** and i don't know why like this, bcuz first string write and read correctly... but, second and others...is not. – Malcolm Jul 17 '23 at 17:45
  • Side note: compressing an encrypted stream is almost completely pointless. The encryption renders it basically incompressible, so you won't save much space. – Charlieface Jul 17 '23 at 20:26

1 Answers1

1

Try switching to ReadExactly (available since ) from Read:

using (DeflateStream decompresstream = new DeflateStream(readstream, CompressionMode.Decompress))
using (CryptoStream decryptostream =
       new CryptoStream(decompresstream, aes.CreateDecryptor(), CryptoStreamMode.Read))
{
    byte[] str = new byte[4];
    decryptostream.ReadExactly(str, 0, str.Length);
    int size = BitConverter.ToInt32(str, 0);
    byte[] strl = new byte[size];
    decryptostream.ReadExactly(strl, 0, strl.Length);
    string result = Encoding.UTF8.GetString(strl);

    byte[] str1 = new byte[4];
    decryptostream.ReadExactly(str1, 0, str1.Length);
    int size1 = BitConverter.ToInt32(str1, 0);
    byte[] strl1 = new byte[size1];
    decryptostream.ReadExactly(strl1, 0, strl1.Length);
        
    string result1 = Encoding.UTF8.GetString(strl1);

    Console.WriteLine($"String: {result} {result1}");
}

If you are running earlier version then you will need to implement it yourself for example via while cycle. Something along these lines:

public static class StreamExts
{
    public static void ReadExactlyOrTillEnd(this Stream stream, byte[] buffer)
    {
        if (buffer.Length == 0)
        {
            return;
        }

        var read = -1;
        var total = 0;
        while (total < buffer.Length && read != 0)
        {
            read = stream.Read(buffer, total, buffer.Length - total);
            total += read;
        }
    }
}

Read more:

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • Yeah, sir! That;s work greates with ReadExactly! But on earlier version can u add example for read this in a while loop? I'll make u answer is right. I try do something loke this... `byte[] str1 = new byte[4]; decryptostream.Read(str1, 0, str1.Length); int size1 = BitConverter.ToInt32(str1, 0); byte[] strl1 = new byte[size1]; string result1 = string.Empty; while (decryptostream.Read(strl1, 0, strl1.Length) >0) { result1 = Encoding.UTF8.GetString(strl1); } ` – Malcolm Jul 18 '23 at 02:26
  • But this code return me this: **String: TestString g2TestStrin** -where im doing wrong? – Malcolm Jul 18 '23 at 02:29
  • @Malcolm you need to advance the offset passed to the `Read`. Also in general you need to use the `while` for every read. Try the extension method above. – Guru Stron Jul 18 '23 at 06:38
  • Ty, but one problem - how to usage this extension method?; i try lake this: `ReadExactlyOrTillEnd(decryptostream, strl1);` but, how to extract string from this method? Can u show me how to usage it? – Malcolm Jul 18 '23 at 07:24
  • @Malcolm the same way you did before, this should just fill the buffer. – Guru Stron Jul 18 '23 at 07:25
  • Ok, i got this: `byte[] str1 = new byte[4]; ReadExactlyOrTillEnd(decryptostream, str1); int size1 = BitConverter.ToInt32(str1, 0); byte[] strl1 = new byte[size1]; ReadExactlyOrTillEnd(decryptostream, strl1); string result1 = Encoding.UTF8.GetString(strl1);` – Malcolm Jul 18 '23 at 07:33
  • But i get exception: **'Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.** in there: - **read = stream.Read(buffer, total, buffer.Length);** – Malcolm Jul 18 '23 at 07:34
  • @Malcolm yep, I forgot `-total` see the update. – Guru Stron Jul 18 '23 at 07:36
  • You're a PERFECT! More mega thanks for ya! – Malcolm Jul 18 '23 at 07:41
  • @Malcolm if I was perfect I would not forgot the `-total` at least =))) Was glad to help! – Guru Stron Jul 19 '23 at 00:47
  • It's just the human factor,don't worry about this, but im very happy invite people like you, answer questions. – Malcolm Jul 19 '23 at 15:42