Tyler
Tyler

Reputation: 426

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

Upvotes: 0

Views: 297

Answers (1)

Visual Vincent
Visual Vincent

Reputation: 18310

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!

Upvotes: 1

Related Questions