Tyler Tyler - 8 days ago 5
Vb.net Question

Can't read an XML in Stream

I have a little problem with an XML file and a

Stream
. I create and save an XML file in a
Stream
, encrypt the
Stream
and then save it to a normal file.

I want to read this XML, however I can only do this if I use a
FileStream
and write a decrypted file to disk.

Is there a way to decrypt and keep this file in memory?

This is my code:

XMLDecriptato = New MemoryStream()
Using stream_readerX As New StreamReader(XMLDecriptato, Text.Encoding.UTF8)

XMLDecriptato.SetLength(0)
Dim FStreamCrypted As FileStream = File.Open(varTitolo, FileMode.Open, FileAccess.Read)
FStreamCrypted.Seek(0, SeekOrigin.Begin)
CryptStream(Pass, FStreamCrypted, XMLDecriptato, Type.Decrypt)

'try to read the stream
Dim xDocumentX As New XmlDocument()
xDocumentX.Load(stream_readerX) 'here is the error
End Using


It keeps saying that the
Stream
is closed. I have tried also another way. The only one that works is to write the stream to the hard disk with a
FileStream
.

And that is the Encrypt/Decrypt Sub:

Public Sub CryptStream(ByVal Psw As String, ByVal IN_Stream As Stream, ByVal OUT_Stream As Stream, ByVal CrtType As CryptType)
Dim AES_Provider As New AesCryptoServiceProvider()

Dim Key_Size_Bits As Integer = 0
For i As Integer = 1024 To 1 Step -1
If (aes_provider.ValidKeySize(i)) Then
Key_Size_Bits = i
Exit For
End If
Next i

Dim Block_Size_Bits As Integer = AES_Provider.BlockSize
Dim Key() As Byte = Nothing
Dim IV() As Byte = Nothing
Dim Salt() As Byte = "//my salt//"
MakeKeyAndIV(Psw, Salt, Key_Size_Bits, Block_Size_Bits, Key, IV)

Dim Crypto_Transform As ICryptoTransform = Nothing

Select Case CrtType
Case CryptType.Encrypt
Crypto_Transform = AES_Provider.CreateEncryptor(key, iv)
Case CryptType.Decrypt
Crypto_Transform = AES_Provider.CreateDecryptor(key, iv)
End Select
If Crypto_Transform Is Nothing Then Exit Sub

Try
Using Crypto_Stream As New CryptoStream(OUT_Stream, Crypto_Transform, CryptoStreamMode.Write)
Const Block_Size As Integer = 1024
Dim Buffer(Block_Size) As Byte
Dim Bytes_Read As Integer
Do
Bytes_Read = IN_Stream.Read(Buffer, 0, Block_Size)
If (Bytes_Read = 0) Then Exit Do

Crypto_Stream.Write(Buffer, 0, Bytes_Read)
Loop
End Using
Catch ex As Exception
End Try

Crypto_Transform.Dispose()
End Sub

Answer

It turns out that when CryptoStream.Dispose() is called by the Using/End Using block, the CryptoStream also disposes the underlying stream (in this case your MemoryStream).
This behaviour can be confirmed by checking Microsoft's Reference Source.

Since the CryptoStream doesn't have a LeaveOpen flag like the StreamReader does since .NET 4.5 and up, I removed the Using block and wrote the necessary calls on my own for your method.

The changes:

Public Sub CryptStream(ByVal Psw As String, ByVal IN_Stream As Stream, ByVal OUT_Stream As Stream, ByVal CrtType As CryptType, Optional ByVal LeaveOpen As Boolean = False)
    ...code...

    Try
        Dim Crypto_Stream As New CryptoStream(OUT_Stream, Crypto_Transform, CryptoStreamMode.Write)

        Const Block_Size As Integer = 1024
        Dim Buffer(Block_Size) As Byte
        Dim Bytes_Read As Integer
        Do
            Bytes_Read = IN_Stream.Read(Buffer, 0, Block_Size)
            If (Bytes_Read = 0) Then Exit Do

            Crypto_Stream.Write(Buffer, 0, Bytes_Read)
        Loop

        If Crypto_Stream.HasFlushedFinalBlock = False Then Crypto_Stream.FlushFinalBlock()

        If LeaveOpen = False Then
            Crypto_Stream.Dispose()
        End If
    Catch ex As Exception
    End Try

    ...code...
End Sub

And since data will be fed into the MemoryStream its position will have changed, so you have to reset that too before loading the XML document:

XMLDecriptato = New MemoryStream()
Using stream_readerX As New StreamReader(XMLDecriptato, System.Text.Encoding.UTF8)

    Dim FStreamCrypted As FileStream = File.Open(OpenFileDialog1.FileName, FileMode.Open, FileAccess.Read)
    CryptStream(Pass, FStreamCrypted, XMLDecriptato, CryptType.Decrypt, True) 'True = Leave the underlying stream open.

    XMLDecriptato.Seek(0, SeekOrigin.Begin) 'Reset the MemoryStream's position.

    Dim xDocumentX As New XmlDocument()
    xDocumentX.Load(stream_readerX)
End Using

As you might have noticed I removed the FStreamCrypted.Seek(0, SeekOrigin.Begin) line. This was because you've just opened the stream and done nothing with it, so the position will already be at 0.

Hope this helps!

Comments