前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >对称加密和解密

对称加密和解密

作者头像
小蜜蜂
发布2019-07-24 17:10:37
2K0
发布2019-07-24 17:10:37
举报
文章被收录于专栏:明丰随笔明丰随笔

.NET提供了一组类型来实现对称加密和解密。这些类型拥有共同的基类SymmetricAlgorithm,如图所示。

可以看到上面类型也分为后缀为"CryptoServiceProvider"和"Managed"两种,作用相同,区别是Managed后缀的类是由托管代码写的,CryptoServiceProvider后缀的类调用的是Windows Crypto API,相当于一个包装类。

现在假设选择TripleDES作为算法,加密的流程如下:

1. 先创建一个TripleDESCryptoServiceProvider的实例,比如provider。

2. 在provider上指定密钥IV,也就是它的Key属性IV属性

这里简单解释一下IV(Initialization vector,初始化向量),如果一个字符串(或者数据)在加密之前很多部分是重复的,比如ABCABCABC,那么加密之后尽管字符串是乱码,但相关部分也是重复的。为了解决这个问题,就引入了IV,在使用它以后,加密之后即使是重复的也被打乱了。

对于特定算法,密钥和IV的值可以随意指定,但长度是固定的,通常密钥为128位或196位IV为64位。密钥和IV都是byte[]类型,因此,如果使用Encoding类来将字符串转换为byte[],那么编码方式就很重要,因为UTF8是变长编码,所以对于中文和英文,需要特别注意byte[]的长度问题。

3.1 如果是加密,在provider上调用CreateEncryptor()方法,创建一个ICryptoTransform类型的加密器对象

3.2 如果是解密,在provider上调用CreateDecryptor()方法,同样创建一个ICryptoTransform类型的解密器对象

3.3 ICryptoTransform定义了加密转换的运算,.NET将在底层调用这个接口。

4.1 因为流和byte[]是数据类型无关的一种数据结构,可以保存和传输任何形式的数据,区别只是byte[]是一个静态的概念而流是一个动态的概念。

4.2 因此,.NET采用了流的方式进行加密解密,运算过程会涉及两个流,一个是明文流,含有加密前的数据;一个是密文流,含有加密后的数据。

4.3 那么就必然有一个中介者,将明文流转换为密文流;或者将密文流转换为明文流。.NET中执行这个操作的中介者也是一个流类型,叫做CryptoStream

4.4 CryptoStream的构造函数如下,共有三个参数:

代码语言:javascript
复制
public CryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode)

5. 当加密时,构造函数签名中的stream参数为密文流(注意此时密文流还没有包含数据,仅仅是一个空流);ICryptoTransform是步骤3.1创建的加密器,负责进行加密运算;CryptoStreamMode枚举为Write,意思是将流经CryptoStream的明文流写入到密文流中。

最后,从密文流中获得加密后的数据。

6. 当解密时,stream为密文流(此时密文流含有数据);ICryptoTransform是步骤3.2创建的解密器,负责进行解密运算;CryptoStreamMode枚举为Read,意思是将密文流中的数据读出到明文流,进而再转换为明文的、原本的格式。

可见,CryptoStream总是接受密文流,并且根据CryptoStreamMode枚举的值来决定是将明文流写入到密文流(加密),还是将密文流读入到明文流中(解密)。

下面是一个用于对称加密和解密的SymmetricCryptoHelper帮助类:

代码语言:javascript
复制
// 对称加密帮助类
public class SymmetricCryptoHelper
{
  private ICryptoTransform encryptor; // 加密器对象
  private ICryptoTransform decryptor; // 解密器对象
  private const int BufferSize = 1024;

  public SymmetricCryptoHelper(string algorithmName, byte[] key)
  {
    //1和2 创建对称加密算法提供器provider,并指定密钥和IV
    using (SymmetricAlgorithm provider = SymmetricAlgorithm.Create(algorithmName))
    {
      provider.Key = key;
      provider.IV = new byte[] { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
      //创建加密器对象
      encryptor = provider.CreateEncryptor();
      //创建解密器对象
      decryptor = provider.CreateDecryptor();
    }
  }
  /// <summary>
  /// 默认使用TripleDES对称算法
  /// </summary>
  /// <param name="key"></param>
  public SymmetricCryptoHelper(byte[] key) : this(SymmetricCryptoTypes.TripleDES, key)
  {
  }
  // 加密算法
  public string Encrypt(string clearText)
  {
    // 根据明文文本创建明文流
    byte[] clearBuffer = Encoding.UTF8.GetBytes(clearText);
    using (MemoryStream clearStream = new MemoryStream(clearBuffer))
    {
      // 创建空的密文流
      using (MemoryStream encryptedStream = new MemoryStream())
      {
        // 创建控制加密的流对象cryptoStream
        using (CryptoStream cryptoStream = new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write))
        {
          // 将明文流写入到buffer中
          // 将buffer中的数据写入到cryptoStream中
          int bytesRead = 0;
          byte[] buffer = new byte[BufferSize];
          do
          {
            //使用buffer字节数组,批量从明文数据流中获取数据,然后写入cryptoStream,并进行了加密
            bytesRead = clearStream.Read(buffer, 0, BufferSize);
            cryptoStream.Write(buffer, 0, bytesRead);
          }
          while (bytesRead > 0);
          //从缓冲区写入encryptedStream对象中
          cryptoStream.FlushFinalBlock();
          // 获取加密后的文本
          buffer = encryptedStream.ToArray();
          // 对加密后的字节进行base64编码处理
          string encryptedText = Convert.ToBase64String(buffer);
          return encryptedText;
        }
      }
    }
  }
  // 解密算法
  public string Decrypt(string encryptedText)
  {
    // 对加密的数据进行base64解码
    byte[] encryptedBuffer = Convert.FromBase64String(encryptedText);
    // 得到加密的数据流
    using (Stream encryptedStream = new MemoryStream(encryptedBuffer))
    {
      // 创建空的明文流
      using (MemoryStream clearStream = new MemoryStream())
      {
        // 创建控制解密的流对象cryptoStream
        using (CryptoStream cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
        {
          int bytesRead = 0;
          byte[] buffer = new byte[BufferSize];
          do
          {
            //通过buffer字节数组,批量从密文数据流中读取明文流数据
            bytesRead = cryptoStream.Read(buffer, 0, BufferSize);
            //得到解密后的文明流然后写入clearStream
            clearStream.Write(buffer, 0, bytesRead);
          }
          while (bytesRead > 0);
          //获取明文流中的字节数据
          buffer = clearStream.GetBuffer();
          //通过UTF8编码获得文本字符串
          string clearText = Encoding.UTF8.GetString(buffer, 0, (int)clearStream.Length);
          return clearText;
        }
      }
    }
  }

  public static string Encrypt(string clearText, string key)
  {
    byte[] keyData = new byte[16];
    byte[] sourceData = Encoding.UTF8.GetBytes(key);
    //密钥长度固定为16字节,那么当传入的字符串key转换成数组后太短了的时候,
    //需要将keyData补齐为16位,数组后面空余的位数补0;
    //当传入的字符串key转换成数组后太长了的时候,则只取前面的16位。
    //正确的密钥长度可以通过在算法对象上调用LegalKeySizes获得(对本例来说,例如provider.LegalKeySizes)。
    int copyBytes = 16;
    if (sourceData.Length < 16)
    {
      copyBytes = sourceData.Length;
    }
    Array.Copy(sourceData, keyData, copyBytes);
    //准备好了定长的keyData,然后初始化对称加密算法提供器
    SymmetricCryptoHelper helper = new SymmetricCryptoHelper(keyData);
    return helper.Encrypt(clearText);
  }

  public static string Decrypt(string encryptedText, string key)
  {
    byte[] keyData = new byte[16];
    byte[] sourceData = Encoding.UTF8.GetBytes(key);
    //密钥长度固定为16字节,那么当传入的字符串key转换成数组后太短了的时候,
    //需要将keyData补齐为16位,数组后面空余的位数补0;
    //当传入的字符串key转换成数组后太长了的时候,则只取前面的16位。
    //正确的密钥长度可以通过在算法对象上调用LegalKeySizes获得(对本例来说,例如provider.LegalKeySizes)。
    int copyBytes = 16;
    if (sourceData.Length < 16)
    {
      copyBytes = sourceData.Length;
    }

    Array.Copy(sourceData, keyData, copyBytes);
    //准备好了定长的keyData,然后初始化对称加密算法提供器
    SymmetricCryptoHelper helper = new SymmetricCryptoHelper(keyData);
    return helper.Decrypt(encryptedText);
  }
}

public class SymmetricCryptoTypes
{
  public const string DES = "DES";
  public const string TripleDES = "TripleDES";
  public const string Rijndael = "Rijndael";
  public const string RC2 = "RC2";
}

对于这个帮助类,我添加了详细的注释,请结合代码进行阅读。

可以对上面的CryptoHelper类进行以下测试:

代码语言:javascript
复制
string key = "secret key";
string plainText = "Hello, world!";
string encryptedText = SymmetricCryptoHelper.Encrypt(plainText, key);
Console.WriteLine(encryptedText);
string clearText = SymmetricCryptoHelper.Decrypt(encryptedText, key);
Console.WriteLine(clearText);

可以看到下面的输出结果:

+l2YvR8Sm9l54pG3atJYxw==

Hello, world!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 明丰随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档