Introduction
Have you ever wondered if your e-mails are secure and secret? Does your network administrator have possibility to read all your correspondence? Do you value your privacy?
If any of the answers is "yes", this article can help you keep your private emails private.
Sample encrypted message looks like this:
-== Begin encryption ==-
9wZO1XjCVP5EwbuOCKP397LW77ar+pwJRhAX+7cRL2LVhstfN4qS4aPAJqeh
CM6rtubeEOj+W04vYymeqA/i/8UOMF/ajiLrH+HpqkfdqumcN2oWpN4PI+50
8K0kAnJqxsOnu7rcjg877yX9XECSlsaKPLZb7RoER+koJ9CRJhQhVusu53+b
rKmUksEBAcKeA4qP0AEsA2ii4LB0JsE/1FXhez8VFSX74Ij36C0/fC7aJgc7
FDNQKRxuE551Z6ERfRrFF/OyxCZxEJe/lsyibf2xMpiCrtCj6h7+d+h8njkc
+CdsB94qAzf9WHoNjQNDuhmVygWiw+M4RUE/L9c4EOJUUD8T52XohF+Y1HzF
Bm0=
-== End encryption ==-
The Application
MailCryptNet is a tool that enables to encrypt any message you want to send via email. Encryption uses Rijndael algorithm and RijndaelManaged
class from System.Security.Cryptography
namespace. Only thing you have to do is to give your friends a copy of MailCryptNet and prepare passwords for them.
You may ask "why should I bother preparing any passwords? Isn't it enough to give the tool to my friends?". It prevents the case when other people (including your hated admin) downloads the tool and decrypts your messages. Creating secret passwords for your friends ensures you that no one else can read your message.
Creating passwords and keys
Password creation is a bit complicated because Rijndael is a symmetric algorithm which uses the same key to encrypt and decrypt data. In such cases, all your friends (including you) would have to use the same password. But nobody likes to share his password (as it is no longer secret).
But there is a solution to use symmetric algorithm and different passwords. The trick is that there are two levels of passwords. First level is common to all trusted clients, and second level is user-specific and is used to encrypt the first-level one.
For example, all trusted clients (you and your friends) use first level password like that: "oh fishy fishy fishy fish!" (or any other weird and twisted text), then every user creates his own private password to encrypt the first-level one. This is a second-level password.
The algorithm - RijndaelManaged
Why RijndaelManaged? Because it is strong, fast and managed that ensures to run on every machine with .NET framework installed.
Base64 encoding
E-mail messages are sent with pure text format. Encrypted data is a binary format. Therefore, it is necessary to do some conversion between those formats. Base64 is a well known and broadly used method to send binary data as pure text. To do it, Microsoft implemented very useful methods called ToBase64String
and FromBase64String
located in Convert
namespace.
Application
What does MailCryptNet do:
- on startup, it reads .config file in order to find encrypted keys.
- if stored keys are empty, it enables to create new encryption keys.
- when .config contains encrypted key, it is decrypted using your private (second level) password.
- decrypted keys are used by Rijndael algorithm.
- any message typed in
RichEdit
is encrypted (including RTF formatting) and placed on Clipboard.
- encrypted message can be restored from Clipboard and after decrypting is placed in
RichEdit
.
Preparing passwords
Default password is "secret". You can test it by copying encrypted message from the beginning of the article and pressing "decrypt" button. To change the password, do the following:
- remove
Key
and IV
values from .config file.
- run MailCryptNet.
- enter first-level password (common for all friends).
- enter second-level password - your private password.
- copy generated keys into .config file.
- restart application.
Usage
Normal usage usually involves following steps:
- type the message.
- click the "Encrypt to Clipboard" button.
- create new email (in your email application).
- paste text from Clipboard.
- send email.
When receiving encrypted email, do the following:
- copy email text to Clipboard.
- in MailCryptNet, click "Decrypt from Clipboard" button.
- read a message.
The code
Using PasswordDeriveBytes
function:
void getKeysFromPassword(string pass,
out byte[] rijnKey, out byte[] rijnIV)
{
byte[] salt =
System.Text.Encoding.ASCII.GetBytes("some text to give it a bit salt");
PasswordDeriveBytes pb = new PasswordDeriveBytes(pass,salt);
rijnKey = pb.GetBytes(32);
rijnIV = pb.GetBytes(16);
}
Converting to Base64 blocks:
string makeBlocksFromString(string s)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("-== Begin encryption ==-\r\n");
int lineLength = 0;
for (int i=0;i<s.Length;i++)
{
char z = s[i];
sb.Append(z);
lineLength++;
if (lineLength == blockLength)
{
lineLength = 0;
sb.Append("\r\n");
}
}
if (lineLength > 0)
sb.Append("\r\n");
sb.Append("-== End encryption ==-\r\n");
return sb.ToString();
}
string makeStringFromBlocks(string s)
{
char[]seps = new char[] {'\r','\n'};
string[] tab = s.Split(seps);
System.Text.StringBuilder sb = new System.Text.StringBuilder();
bool wasBegin = false;
for (int i=0;i<tab.Length;i++)
{
if (tab[i] == "-== Begin encryption ==-")
{
wasBegin = true;
continue;
}
if (wasBegin)
{
if (tab[i] == "-== End encryption ==-")
{
return sb.ToString();
}
else
sb.Append(tab[i]);
}
}
throw new
CryptographicException("missing begin block - end block definitions");
}
Rijndael encryption:
EncryptData
method is used to encode message to be secret.
private byte[] EncryptData(byte[] dataToEncrypt,
byte[] rijnKey, byte[] rijnIV)
{
RijndaelManaged rijn = new RijndaelManaged();
ICryptoTransform encryptor = rijn.CreateEncryptor(rijnKey, rijnIV);
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt,
encryptor, CryptoStreamMode.Write);
csEncrypt.Write(dataToEncrypt, 0, dataToEncrypt.Length);
csEncrypt.FlushFinalBlock();
return msEncrypt.ToArray();
}
Rijndael decryption:
DecryptData
method is used to decode secret message.
private byte[] DecryptData(byte[] dataToDecrypt,
byte[] rijnKey, byte[] rijnIV)
{
RijndaelManaged rijn = new RijndaelManaged();
ICryptoTransform decryptor =
rijn.CreateDecryptor(rijnKey, rijnIV);
MemoryStream msDecrypt = new MemoryStream(dataToDecrypt);
CryptoStream csDecrypt = new CryptoStream(msDecrypt,
decryptor, CryptoStreamMode.Read);
byte[] fromEncrypt = new byte[dataToDecrypt.Length];
int len = csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
byte[] decrypted = new byte[len];
Array.Copy( fromEncrypt,decrypted,len);
return decrypted;
}
Button and Clipboard handlers:
private void btnEncrypt_Click(object sender, System.EventArgs e)
{
string toEncrypt = richTextBox1.Rtf;
byte[] toEncryptBuf = System.Text.Encoding.ASCII.GetBytes(toEncrypt);
try
{
byte[] encryptedBuf =
EncryptData(toEncryptBuf,cryptoKey,cryptoIV);
string encryptedString =
makeBlocksFromString(Convert.ToBase64String(encryptedBuf));
Clipboard.SetDataObject(encryptedString,true);
}
catch (FormatException ex)
{
MessageBox.Show(ex.Message);
return;
}
catch (CryptographicException ex)
{
MessageBox.Show(ex.Message);
return;
}
}
private void btnDecrypt_Click(object sender, System.EventArgs e)
{
IDataObject iData = Clipboard.GetDataObject();
if (iData.GetDataPresent(DataFormats.Text))
{
try
{
string encryptedString =
makeStringFromBlocks(iData.GetData(DataFormats.Text).ToString());
byte[] encryptedBuf = Convert.FromBase64String(encryptedString);
byte[] decryptedBuf = DecryptData(encryptedBuf,
cryptoKey,cryptoIV);
string plainText =
System.Text.Encoding.ASCII.GetString(decryptedBuf);
richTextBox1.Rtf = plainText;
}
catch (FormatException ex)
{
MessageBox.Show(ex.Message);
return;
}
catch (CryptographicException ex)
{
MessageBox.Show(ex.Message);
return;
}
}
}
Future enhancements
There are some features that could be added to this software:
- Isolated Storage support - for multiple users to use the application with separate passwords. Instead of .config file, user passwords can be stored in Isolated Storage.
- Some text formatting while typing message - at the moment, formatting is possible only when you paste formatted text into MailCryptNet edit window. "Reset" button removes the formatting.
- Some integration with email applications for automatic message encoding/decoding while sending emails.