Click here to Skip to main content
15,885,309 members
Articles / Web Development / ASP.NET

Password Protected Stream Using Decorator Pattern

Rate me:
Please Sign up or sign in to vote.
3.81/5 (9 votes)
9 Jan 2012CPOL3 min read 28.5K   718   34  
Builds a password protected stream on top of System.IO.Stream
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace SecureApplication.Classes
{
    //extends the stream class
    public class PasswordProtectedStream : Stream
    {
        string password; //password for the stream
        Stream str; //the base stream
        bool eventRaised = false; //this is a kind of locking variable so that if read operations are called multiple times, the event (the description to be followed) doesn't get invoked multiple times

        public delegate void ReadCompleteHandler(object s, ReadCompleteEventArgs e); //delegate for event
        public event ReadCompleteHandler On_ReadComplete; // event that passes data and read status to the client application 

        public PasswordProtectedStream(Stream str, string password) //set the base parameters
            : base()
        {
            this.str = str;
            this.password = password;
        }

        #region "Overridden Methods of Stream"
        public override void Write(byte[] buffer, int offset, int count) //override the write method
        {
            byte[] data = new byte[count];

            for (int i = offset, j = 0; j < count; i++, j++)
                data[j] = buffer[i]; //construct the actual data buffer

            DataEnvelop env = new DataEnvelop(data, password); //create an instance of our own custom serialized class (to be followed later)

            BinaryFormatter f = new BinaryFormatter();
            f.Serialize(str, env); //serialize the object
        }

        public override int Read(byte[] buffer, int offset, int count) //override the read method
        {
            int r = str.Read(buffer, offset, count); //read all bytes from base stream

            byte[] newData = new byte[str.Length]; //construct buffer to hold actual data and not the default buffer otherwise the object won't be de-serialized properly due to padded empty bytes

            for (int i = 0; i < str.Length; i++) //in respect to the actual length of the base stream
                newData[i] = buffer[i]; //copy all non-empty bytes

            MemoryStream mstr = new MemoryStream(newData); //consruct memory stream for de-serialization

            BinaryFormatter f = new BinaryFormatter();
            DataEnvelop env = (DataEnvelop)f.Deserialize(mstr);

            if (env.password == password) //if password is matched
            {
                if (On_ReadComplete != null && !eventRaised) //if event is not empty and its not been invoked earlier
                {
                    On_ReadComplete(this, new ReadCompleteEventArgs(ReadCompleteEventArgs.Status.SUCCESS, env.data)); //bind successful read event
                    eventRaised = true; //mark it so that the event is not invoked again on multiple reads
                }
            }
            else //if wrong password
            {
                if (On_ReadComplete != null && !eventRaised) //if event is not empty and its not been invoked earlier
                {
                    On_ReadComplete(this, new ReadCompleteEventArgs(ReadCompleteEventArgs.Status.FAILURE, null)); //bind un-successful read event
                    eventRaised = true; //mark it so that the event is not invoked again on multiple reads
                }
            }
            return r; //return actual number of bytes read and not the bytes of the actual data otherwise it will loop. This is the only reason why I had to pass the data to the event and couldn't directly process here. If anyone can suggest a way, I would be grateful.
        }

        public override void Close()
        {
            str.Close();
            str.Dispose();
        }
        #endregion

        #region "Overridden Properties of Stream"
        public override void SetLength(long value)
        {
            str.SetLength(value);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return str.Seek(offset, origin);
        }

        

        public override long Position
        {
            get
            {
                return str.Position;
            }
            set
            {
                str.Position = value;
            }
        }
        public override long Length
        {
            get { return str.Length; }
        }
        public override bool CanWrite
        {
            get { return str.CanWrite; }
        }
        public override void Flush()
        {
            str.Flush();
        }
        public override bool CanSeek
        {
            get { return str.CanSeek; }
        }
        public override bool CanRead
        {
            get { return str.CanRead; }
        }
        #endregion
        
        
        [Serializable]
        class DataEnvelop //private DataEnvelop Class that is serialized to the base stream with the password and the data bytes, it can be enhanced to encrypt the password
        {

            public byte[] data { get; set; }
            public string password { get; set; }

            public DataEnvelop(byte[] data, string password)
            {

                this.data = data;
                this.password = password;
            }
        }

        public class ReadCompleteEventArgs : EventArgs //arguments passed to the event that passes data to the client
        {

            byte[] data; //actually data bytes
            Status status; //status of the current output, whether password is correct or not

            public enum Status { SUCCESS, FAILURE } //if password is correct, set as CORRECT else FAILURE

            public ReadCompleteEventArgs(Status status, byte[] data)
            {

                this.data = data;
                this.status = status;
            }

            public byte[] Data { get { return data; } }
            public Status ReadStatus { get { return status; } }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
India India
Hi Friends,

Myself, Sumit Mehta, an IT consultant and a software engineer in India.

My Skillset is based on the 7+ years of working experience in different heterogeneous technologies.

I have successfully designed, developed, and supported live web applications in .NET.

Besides this, I have also managed and administered Plesk control panel, having almost 100 domains and 50+ clients.

I have developed projects for a wide scale and range, i.e. from small scale clients to banking applications. Currently I work in .NET 4.0 and Visual Studio 2010. I also have hands on experience in PHP, Java and VB6. I am also heading towards iPhone development now.

You can get in touch with me at http://www.sumitmehta.net

Comments and Discussions