|
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.