Articles
Encrypting with the Compact Framework
on a PocketPC using a "BlowFish" Stream
Implementation |
Limits |
Some useful links... |
Revision history... |
While developing applications for PocketPC we were faced with the need to encrypt the data to be persisted onto the disk. In fact, because of its "mobile" nature, the PocketPC might be easily stolen.
We searched the web and found that Markus Hahn ported in c# the Blowfish algorithm, from Bruce Schneier. In order to use it easily in our applications, we implemented auround it a Stream derived class.
Implementation
Because we do not know what kind of stream we will require, our Blowfish stream will simply "adapt" a stream object parameter.
namespace FlowGroup.Crypto
{
...
public class BlowfishStream : Stream
{
...
private System.IO.Stream mStream;
/// <summary>
/// Construct a BlowfishStream that adapt the
/// "stream" parameter in order to encrypt/decrypt
/// the data
/// </summary>
/// <param name="stream">inner stream</param>
/// <param name="key">key for the encryption</param>
/// <param name="mode">Read or Write</param>
public BlowfishStream(Stream stream,
byte[] key,
BlowfishStreamMode mode)
{
...
mStream = stream;
...
}
...
}
}
The same stream object might be used for streams that are encypted or not, we decided to sign the stream. If the stream is not encrypted, all the calls are delegated to the parameter stream object.
public class BlowfishStream : Stream
{
static private readonly byte[] signature =
{ 5, 11, 14, 22, 6, 17, 15, 192};
private System.IO.Stream mStream;
...
private bool mIsEncrypted = false;
private byte[] mBuffer;
private int mCount = 0;
private byte mOffset = 0;
...
public BlowfishStream(Stream stream,
byte[] key,
BlowfishStreamMode mode)
{
...
mStream = stream;
mBuffer = new byte[256];
mMode = mode;
switch(mode)
{
case BlowfishStreamMode.Read:
mCount = (byte)mStream.Read(mBuffer, 0, 8);
mOffset = 0;
if(mCount == 8)
{
mIsEncrypted = true;
while(mCount > 0)
{
mCount--;
if(mBuffer[mCount] != signature[mCount])
{
mCount = 8;
mIsEncrypted = false;
break;
} // if
}
}
break;
case BlowfishStreamMode.Write:
mIsEncrypted = true;
mStream.Write(signature, 0, 8);
break;
}
}
...
public override long Seek(long offset, System.IO.SeekOrigin origin)
{
if(mIsEncrypted)
throw new System.NotSupportedException();
return mStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
if(mIsEncrypted)
throw new NotSupportedException();
mStream.SetLength(value);
}
public override bool CanRead
{
get
{
return mIsEncrypted ? (mMode == BlowfishStreamMode.Read)
: mStream.CanRead;
}
}
public override bool CanSeek
{
get
{
return mIsEncrypted ? false : mStream.CanSeek;
}
}
public override bool CanWrite
{
get
{
return mIsEncrypted ? (mMode == BlowfishStreamMode.Write)
: mStream.CanWrite;
}
}
public override long Length
{
get
{
if(mIsEncrypted)
throw new System.NotSupportedException();
return mStream.Length;
}
}
public override long Position
{
get
{
if(mIsEncrypted)
throw new System.NotSupportedException();
return mStream.Position;
}
set
{
if(mIsEncrypted)
throw new System.NotSupportedException();
mStream.Position = value;
}
}
The algorithm used 8 bytes blocks, so we have to use a buffer to read/write to and from the stream. So let's implement the read, write and flush functions...
public override void Flush()
{
if(!mIsEncrypted)
{
}
else if(mMode == BlowfishStreamMode.Write)
{
if(mCount > 0)
{
int bound = ((mCount % 8) == 0) ? mCount
: (((mCount / 8) + 1) * 8);
mBlowfish.Encrypt(mBuffer, mBuffer, 0, 0, bound);
mStream.WriteByte((byte)(mCount - 1));
mStream.Write(mBuffer, 0, bound);
mCount = 0;
mOffset = 0;
}
}
mStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
if(mIsEncrypted)
{
int totalRead = 0;
int read = 0;
int bound;
// while data are required, fill the internal buffer
while(count > 0)
{
if(mCount == 0)
{
int nRead = 0;
read = mStream.ReadByte();
if(read == -1)
break; // the stream is empty
mCount = read + 1;
mOffset = 0;
bound = ((mCount % 8) == 0) ? mCount
: (((mCount / 8) + 1) * 8);
read = mStream.Read(mBuffer, mOffset, bound);
nRead += read;
if(bound != read)
{
int nMaxCount = 256;
while(nRead < bound)
{
read = mStream.Read(mBuffer,
mOffset+nRead,
bound-nRead);
nRead += read;
nMaxCount--;
if(nMaxCount <= 0)
throw new System.IO.IOException();
}
}
mBlowfish.Decrypt(mBuffer,
mBuffer,
mOffset,
mOffset,
bound);
}
buffer[offset++] = mBuffer[mOffset++];
count--;
mCount--;
totalRead++;
}
return totalRead;
}
else
{
int cbRead = 0;
if(mCount > 0)
{
while((mOffset < 8) && (count > 0))
{
buffer[offset++] = mBuffer[mOffset++];
count--;
cbRead++;
}
if(mOffset == 8)
{ // the "wrong" signature has been transfered back
// to the reader, so empty the buffer
mCount = 0;
mOffset = 0;
}
}
cbRead += mStream.Read(buffer, offset, count);
return cbRead;
}
}
public override void Write(byte[] buffer, int offset, int count)
{
if(mIsEncrypted)
{
string str =
System.Text.ASCIIEncoding.ASCII.GetString(buffer,
0,
count);
// while data are required, fill the internal buffer
while(count > 0)
{
if(mCount == 256)
{
mStream.WriteByte(255);
mBlowfish.Encrypt(mBuffer, mBuffer, 0, 0, 256);
mStream.Write(mBuffer, 0, 256);
mOffset = 0;
mCount = 0;
}
mBuffer[mOffset++] = buffer[offset++];
count--;
mCount++;
}
}
else
mStream.Write(buffer, offset, count);
}
Limits
In order to simplify the code, the stream can be opened for reading or writing, but not both, and cannot be seeked.
Some useful links...
The Blowfish Encryption Algorithm... Where to find information on the Blowfish algorithm | |
Where to download the c# implementation of the Blowfish Homepage of Markus Hahn, who implented the Blowfish algorithm in c#. | |
Download the source code size 11 Kb, last modification date 06/08/2013 | |
Contacts us Any comments, questions? Just contact us. |
Revision history...
date | révision |
---|---|
19/12/2005 00:00 | The link to Markus Hahn website has been updated. |
26/10/2004 00:00 | The link to Markus Hahn website has been updated. |