Malcolm
Malcolm

Reputation: 35

Read string from CryptoStream correctly?

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?

Upvotes: 0

Views: 369

Answers (1)

Guru Stron
Guru Stron

Reputation: 143098

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:

Upvotes: 2

Related Questions