none
F# & crypto-stream RRS feed

  • Question

  • Hi!

    I was using System.Security.Cryptography and find an interesting thing which feels like a bug maybe in CLR. Here is F# code that you can run in the F# interactive to test the issue. The question is: why is first input cutted and second is full? Flushing streams didn't change anything. There is no exceptions. What happens?

    open System.IO
    open System.Security.Cryptography
    open System.Text
    open System.Diagnostics.Contracts
    
    let DeCryptString1 = 
        let secret = "bTbJw50wt2OuJFRZsU+rTC15iyNuF8G0Ky+lvOYisBM="
        let key = "12345678"
        let iv = "abcdefghijklmnop"
        
        let enc = new ASCIIEncoding()
        let algo = Rijndael.Create()
        use decrypted = new MemoryStream()
        use decode = new FromBase64Transform()
        try
            use decryptor = algo.CreateDecryptor(enc.GetBytes(key), enc.GetBytes(iv))
            use tmpcrypt = new CryptoStream(decrypted, decryptor, CryptoStreamMode.Write)
            use decodestream = new CryptoStream(tmpcrypt, decode, CryptoStreamMode.Write)
            let salatutbytet = enc.GetBytes(secret);
            decodestream.Write(salatutbytet, 0, salatutbytet.Length);
            enc.GetString(decrypted.ToArray()) |> System.Console.WriteLine
            //Output: this-is-the-secr
        with
            | :? CryptographicException as ex -> failwith("Decrypt error..." + ex.ToString())
            | :? System.FormatException as ex -> failwith("Decrypt error..." + ex.ToString())
        
        enc.GetString(decrypted.ToArray()) |> System.Console.WriteLine
        //Output: this-is-the-secret-text
    
    
    Friday, January 22, 2010 11:59 AM

Answers

  • You need to close the stream, before you get the full decoded text. Just add this line:
        decodestream.Close();
    before you call decrypted.TyArray().
    The try-with-finally block will close the stream on your behalf (via use keyword and IDisposable interface). Check out the IL to see the IDisposable::Dispose call on 'decodestream' in the finally block.

    Here is how it would like in C# (it behaves the same way if you comment out the decodeBase64Stream.Close call):

    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    
    class HelloWorld
    {
        static void Main()
        {
            ASCIIEncoding enc = new ASCIIEncoding();
            byte[] encodedText = enc.GetBytes("bTbJw50wt2OuJFRZsU+rTC15iyNuF8G0Ky+lvOYisBM=");
            byte[] key = enc.GetBytes("12345678");
            byte[] iv = enc.GetBytes("abcdefghijklmnop");
    
            Rijndael alg = Rijndael.Create();
            MemoryStream output = new MemoryStream();
    
            CryptoStream decryptStream = new CryptoStream(
                output,
                alg.CreateDecryptor(key, iv),
                CryptoStreamMode.Write);
            CryptoStream decodeBase64Stream = new CryptoStream(
                decryptStream,
                new FromBase64Transform(),
                CryptoStreamMode.Write);
            
            decodeBase64Stream.Write(encodedText, 0, encodedText.Length);
            decodeBase64Stream.Close();
            Console.WriteLine(enc.GetString(output.ToArray()));
        }
    }


    These facts are kind of hinted in MSDN docs for CryptoStream (Remarks section about calling Close) and CryptoStream.Flush (state (partial characters) is partial message block in this case), however it is not called out very specifically and it is confusing. I'll see what I can do about that.

    -Karel

    Saturday, January 23, 2010 12:46 AM
    Moderator

All replies

  • You need to close the stream, before you get the full decoded text. Just add this line:
        decodestream.Close();
    before you call decrypted.TyArray().
    The try-with-finally block will close the stream on your behalf (via use keyword and IDisposable interface). Check out the IL to see the IDisposable::Dispose call on 'decodestream' in the finally block.

    Here is how it would like in C# (it behaves the same way if you comment out the decodeBase64Stream.Close call):

    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    
    class HelloWorld
    {
        static void Main()
        {
            ASCIIEncoding enc = new ASCIIEncoding();
            byte[] encodedText = enc.GetBytes("bTbJw50wt2OuJFRZsU+rTC15iyNuF8G0Ky+lvOYisBM=");
            byte[] key = enc.GetBytes("12345678");
            byte[] iv = enc.GetBytes("abcdefghijklmnop");
    
            Rijndael alg = Rijndael.Create();
            MemoryStream output = new MemoryStream();
    
            CryptoStream decryptStream = new CryptoStream(
                output,
                alg.CreateDecryptor(key, iv),
                CryptoStreamMode.Write);
            CryptoStream decodeBase64Stream = new CryptoStream(
                decryptStream,
                new FromBase64Transform(),
                CryptoStreamMode.Write);
            
            decodeBase64Stream.Write(encodedText, 0, encodedText.Length);
            decodeBase64Stream.Close();
            Console.WriteLine(enc.GetString(output.ToArray()));
        }
    }


    These facts are kind of hinted in MSDN docs for CryptoStream (Remarks section about calling Close) and CryptoStream.Flush (state (partial characters) is partial message block in this case), however it is not called out very specifically and it is confusing. I'll see what I can do about that.

    -Karel

    Saturday, January 23, 2010 12:46 AM
    Moderator
  • Hi Tuomas,
    Is Karel's suggestion helpful? please feel free to let us know if you have any other concerns.
    Sincerely,
    Eric

    Please remember to mark helpful replies as answers.
    Tuesday, January 26, 2010 1:58 AM