应用加密技术第一部分:用于加密/解密电子邮件的简单工具






3.47/5 (6投票s)
2004年7月1日
3分钟阅读

56407

781
有很多关于 .NET 加密的文章,但使用它的示例应用程序却不多。本文(第一部分)介绍了一个可以通过电子邮件发送安全消息的应用程序。
引言
你是否曾想过你的电子邮件是否安全保密?你的网络管理员是否能够阅读你所有的通信内容?你是否重视你的隐私?
如果任何一个问题的答案是“是”,那么这篇文章可以帮助你保护你的私人电子邮件不被泄露。
示例加密消息看起来是这样的
-== 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 ==-
应用程序
MailCryptNet 是一个允许你加密你想通过电子邮件发送的任何消息的工具。加密使用 Rijndael 算法和 `System.Security.Cryptography` 命名空间中的 `RijndaelManaged` 类。你只需要做的就是给你的朋友一份 MailCryptNet 的副本,并为他们准备密码。
你可能会问:“我为什么要费心准备密码?直接把工具给我的朋友不就够了吗?”这可以防止其他人(包括你讨厌的管理员)下载工具并解密你的消息。为你的朋友创建秘密密码可以确保没有人能阅读你的消息。
创建密码和密钥
密码创建有点复杂,因为 Rijndael 是一种对称算法,它使用相同的密钥来加密和解密数据。在这种情况下,你所有的朋友(包括你自己)都必须使用相同的密码。但没有人喜欢分享密码(因为它不再是秘密)。
但是,有一个方法可以使用对称算法和不同的密码。诀窍在于有 **两个级别的密码**。第一级是所有受信任的客户端都通用的,第二级是用户特定的,用于加密第一级密码。
例如,所有受信任的客户端(你和你的朋友)都使用第一级密码,例如:“哦,鱼儿鱼儿鱼儿鱼!”(或任何其他奇怪而扭曲的文本),然后每个用户创建自己的私有密码来加密第一级密码。这就是第二级密码。
算法 - RijndaelManaged
为什么是 RijndaelManaged?因为它强大、快速且是托管的,确保它可以在安装了 .NET Framework 的任何机器上运行。
Base64 编码
电子邮件消息以纯文本格式发送。加密数据是二进制格式。因此,需要在这些格式之间进行一些转换。Base64 是一种广为人知且广泛使用的方法,用于将二进制数据作为纯文本发送。为此,Microsoft 实现了一些非常有用的方法,称为 `ToBase64String` 和 `FromBase64String`,它们位于 `Convert` 命名空间中。
Application
MailCryptNet 的功能
- 启动时,它会读取 `.config` 文件以查找加密的密钥。
- 如果存储的密钥为空,它会允许创建新的加密密钥。
- 当 `.config` 包含加密密钥时,它会使用你的私有(第二级)密码进行解密。
- 解密后的密钥由 Rijndael 算法使用。
- 在 `RichEdit` 中输入的任何消息(包括 RTF 格式)都会被加密并放入剪贴板。
- 加密的消息可以从剪贴板恢复,解密后会放入 `RichEdit` 中。
准备密码
默认密码是“secret”。你可以通过复制文章开头的加密消息并按下“解密”按钮来测试它。要更改密码,请执行以下操作:
- 从 `.config` 文件中删除 `Key` 和 `IV` 值。
- 运行 MailCryptNet。
- 输入第一级密码(所有朋友通用)。
- 输入第二级密码 - 你的私有密码。
- 将生成的密钥复制到 `.config` 文件中。
- 重新启动应用程序。
用法
正常使用通常涉及以下步骤:
- 输入消息。
- 点击“加密到剪贴板”按钮。
- 创建新电子邮件(在你的电子邮件应用程序中)。
- 从剪贴板粘贴文本。
- 发送电子邮件。
收到加密电子邮件时,执行以下操作:
- 将电子邮件文本复制到剪贴板。
- 在 MailCryptNet 中,点击“从剪贴板解密”按钮。
- 阅读消息。
代码
使用 `PasswordDeriveBytes` 函数
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);
//generate key and iv
rijnKey = pb.GetBytes(32);
rijnIV = pb.GetBytes(16);
}
转换为 Base64 块
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++;
//add "new line" when block width met
if (lineLength == blockLength)
{
lineLength = 0;
sb.Append("\r\n");
}
}
//if the last line shorter than block width - add new line
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 加密
`EncryptData` 方法用于对消息进行编码以使其保密。
private byte[] EncryptData(byte[] dataToEncrypt,
byte[] rijnKey, byte[] rijnIV)
{
RijndaelManaged rijn = new RijndaelManaged();
//Get an encryptor.
ICryptoTransform encryptor = rijn.CreateEncryptor(rijnKey, rijnIV);
//Encrypt the data.
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt,
encryptor, CryptoStreamMode.Write);
//Write all data to the crypto stream and flush it.
csEncrypt.Write(dataToEncrypt, 0, dataToEncrypt.Length);
csEncrypt.FlushFinalBlock();
//Get encrypted array of bytes.
return msEncrypt.ToArray();
}
Rijndael 解密
`DecryptData` 方法用于对保密消息进行解码。
private byte[] DecryptData(byte[] dataToDecrypt,
byte[] rijnKey, byte[] rijnIV)
{
RijndaelManaged rijn = new RijndaelManaged();
//Get a decryptor.
ICryptoTransform decryptor =
rijn.CreateDecryptor(rijnKey, rijnIV);
//Now decrypt the message
MemoryStream msDecrypt = new MemoryStream(dataToDecrypt);
CryptoStream csDecrypt = new CryptoStream(msDecrypt,
decryptor, CryptoStreamMode.Read);
byte[] fromEncrypt = new byte[dataToDecrypt.Length];
//Read the data out of the crypto stream.
int len = csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
byte[] decrypted = new byte[len];
Array.Copy( fromEncrypt,decrypted,len);
return decrypted;
}
按钮和剪贴板处理程序
private void btnEncrypt_Click(object sender, System.EventArgs e)
{
string toEncrypt = richTextBox1.Rtf;
byte[] toEncryptBuf = System.Text.Encoding.ASCII.GetBytes(toEncrypt);
//Rijndael encryption
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))
{
//Rijndael decryption
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;
}
}
}
未来增强功能
此软件可以添加一些功能:
- 隔离存储支持 - 允许多个用户使用单独的密码使用该应用程序。代替 `.config` 文件,用户密码可以存储在隔离存储中。
- 输入消息时的一些文本格式设置 - 目前,只有在将格式化的文本粘贴到 MailCryptNet 编辑窗口时才能进行格式设置。“重置”按钮会删除格式。
- 与电子邮件应用程序集成,以便在发送电子邮件时自动进行消息编码/解码。