65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.47/5 (6投票s)

2004年7月1日

3分钟阅读

viewsIcon

56407

downloadIcon

781

有很多关于 .NET 加密的文章,但使用它的示例应用程序却不多。本文(第一部分)介绍了一个可以通过电子邮件发送安全消息的应用程序。

Sample image

引言

你是否曾想过你的电子邮件是否安全保密?你的网络管理员是否能够阅读你所有的通信内容?你是否重视你的隐私?

如果任何一个问题的答案是“是”,那么这篇文章可以帮助你保护你的私人电子邮件不被泄露。

示例加密消息看起来是这样的

-== 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 编辑窗口时才能进行格式设置。“重置”按钮会删除格式。
  • 与电子邮件应用程序集成,以便在发送电子邮件时自动进行消息编码/解码。
© . All rights reserved.