实现加密和解密的密钥
要使用对称算法,必须提供要使用的密钥。每个 CryptoSymmetricAlgorithm 实现都提供一种 GenerateKey 方法。它们实际上使用的是公共语言运行时 (CLR) 类中内置的随机数生成器类。我们来看一下 Gen Key(生成密钥)按钮的 Click 事件处理程序,看它如何生成要使用的随机密钥值。
获取服务提供程序的特定实现后,只需调用 GenerateKey 方法来创建一个用于加密的新的随机密钥。密钥的大小取决于用来加密的特定提供程序。例如,DES 密钥的大小为 64 位,而 TripleDES 密钥的大小为 192 位。每个 SymmetricAlgorithm 类上都有一个 KeySize 属性,它将返回用于生成密钥的密钥大小。
我们还需要生成初始化向量 (IV)。IV 将帮助算法生成最终加密字符串的数据块。IV 用于开始第一个块的加密。假如不提供 IV,那么只要密钥相同,在字符串之间传递的通用数据将保持同一种模式。因此,需要使用 IV 作为加密数据的“随机”组件。通过这种方式,只要使用的 IV 不同,即使密钥相同,同一个数据也会被加密成完全不同的值。下面是生成新的 IV 的 Gen IV(生成 IV)按钮的源代码。
此代码看起来与生成密钥的代码非常相似。每个加密服务提供程序类上都有一个 GenerateIV() 方法。假如未提供 IV,该方法将生成一个 IV。
加密数据
获得密钥和初始化向量后,现在可以使用 Key、IV 和 Original String 值来创建原始字符串值的加密版本。单击 Encrypt(加密)按钮将运行以下代码。
Click 事件过程将调用名为 EncryptString() 的方法,从 Original String(原始字符串)文本框中接受值并对其进行加密。然后返回该值并将其放到 Encrypted String(加密字符串)文本框中。下面是 EncryptString() 方法的代码。
现在我们分开看一下各行代码并了解这些代码的作用。首先是加密进程的几个变量。
ICryptoTransform 是一个接口。需要此接口才能在任何服务提供程序上调用 CreateEncryptor 方法,服务提供程序将返回定义该接口的实际 encryptor 对象。
然后需要将原始字符串转换成字节数组。大多数 .NET 加密算法处理的是字节数组而不是字符串。
byt = Encoding.UTF8.GetBytes(Value)
现在可以执行实际的加密了。此进程需要创建一个数据流,用于将加密的字节写入到其中。要使用名为 ms 的 MemoryStream 对象、ICryptoTransform 对象(提供给 CryptoStream 类的构造函数)以及说明您希望在何种模式(读、写等)下创建该类的枚举常数。创建 CryptoStream 对象 cs 后,现在使用 CryptoStream 对象的 Write 方法将数据写入到内存数据流。这就是进行实际加密的方法,加密每个数据块时,数据将被写入 MemoryStream 对象。
创建 MemoryStream 后,该代码将在 CryptoStream 对象上执行 FlushFinalBlock 方法,以确保所有数据均被写入 MemoryStream 对象。该过程将关闭 CryptoStream 对象。
最后,该过程将内存数据流从字节数组转换回字符串,这样才可以在窗体上的文本框内显示该字符串。可以使用 MemoryStream ToArray() 方法从数据流中获取字节数组,然后调用 Convert.ToBase64String() 方法,该方法接受字节数组输入并使用 Base64 编码方法将该字符串编码为可读内容。
解密数据
加密数据后,有时还需要解密数据。解密数据的过程非常简单,与加密过程相似。您需要提供加密过程中使用的密钥和初始化向量。SymmetricAlgorithm 类的 Key 和 IV 属性被定义为字节数组。因此,设置这些属性之前需要提供您创建的字符串并将其转换成字节数组。下面我们看一下窗体内用于解密字符串的 DecryptString 方法。该方法是从窗体上 Decrypt(解密)按钮的 Click 事件处理程序中调用的。
Encrypt 函数和 Decrypt 函数只有三个不同之处
1、需要使用 CryptoServicePRovider 类的 CreateDecryptor 方法来创建相应的 ICtryptoTransform 对象。
2、需要将 Base64 编码字符串转换成字节数组。需要使用 Convert.FromBase64String 方法来实现此转换。
3、通过对原始字节数组进行转换,将字节数组转换成相应的内存数据流。需要将内存数据流从字节数组转换回可以在窗体上再次显示的普通字符串。需要使用 Encoding.UTF8.GetString() 方法来实现此转换。
注重:Encoding.UTF8 类来自于 System.Text 命名空间。
是否可以使其更简单?!
尽管到目前为止显示的代码并不难,但有很多不同的类和接口您可能还不习惯于使用。此外,还要记住很多代码。下面我们学习如何将 Cryptography 类包装成易于使用的类。
名为 PDSACryptography 的程序集中有两个类,分别为 PDSAHash 和 PDSAEncryption。这两个类用于封装创建散列字符串或加密字符串的实际机制。此外,它们还答应您使用枚举常数来决定要使用哪种散列或加密算法。不必记住每个不同的加密服务提供程序的所有不同的名称,即可获得不错的 Intellisense? 提供程序列表。
使用 PDSAHash 包装散列
PDSAHash 类包含属性 HashType、HashObject、OriginalString、HashString、SaltValue、UseSalt 和 SaltLength。与该类相关的方法包括 SetEncryptor、CreateSalt、Reset 和 CreateHash。该类中创建了一个称为 PDSAHashType 的枚举,您可以从中选择要使用的相应散列类。该类的作用是将上文所示的代码简化为以下代码。
我宁愿键入以上代码也不愿意记住上文显示的所有代码。如您所见,这段代码相当简单,与上文所述的代码完全相同,只是被包装到一个易于使用的接口中。您可以从本文包含的示例中找到完整的类。下面列举了可以使用该类创建的不同散列算法。
其中的每个算法都为最终的散列提供了一个不同的安全级别。.NET 中完整的散列类列表如下所示:
●md5CryptoServiceProvider
●SHA1CryptoServiceProvider
●SHA256Managed
●SHA384Managed
●SHA512Managed
有关这些不同的散列类型的具体信息,请参阅 Visual Studio .NET 联机文档。
使用 PDSAEncryption 包装加密
就像 PDSAHash 类可以包装 .NET Framework 中的所有散列功能一样,PDSAEncryption 类可用于包装 .NET Framework 中的各种对称算法类。PDSAEncryption 类包括以下枚举类型,答应您创建各种加密/解密对象。
同样,每个服务提供程序都为加密字符串提供了不同的安全级别。这在 Visual Studio .NET 联机文档中都有具体的介绍,这里不再赘述。
该类包含属性 EncryptionType、OriginalString、EncryptedString、Key、KeyString、IV、IVString 和 CryptoProvider。其中的大多数属性都是不言自明的,但 Key 和 IV 属于字节数组,而 KeyString 和 IVString 是这些字节数组的字符串表示。
该类还包含一些方法。例如 Encrypt 和 Decrypt。还有 GenerateKey 和 GenerateIV 方法,假如没有现成的密钥和 IV,可以使用这两个方法创建一个密钥和 IV。另外还有 SetEncryptor 方法,它用于创建将在各种方法中使用的新的 CryptoProvider 对象。
该类的作用是使加密和解密字符串更轻易实现。例如,下面的代码片断显示了使用该类加密字符串是多么轻易。
小结
使用 Microsoft .NET Framework 中的类可以很轻易地在计算机中保存机密信息。您会发现 Cryptography 命名空间中的多个类都可以很好完成这一任务。为这些类创建包装可以帮助您大大减少需要编写的代码量。强烈建议您按照本文所述,创建类似的包装。
作者简介
Paul Sheriff 是 PDSA, Inc. 的总裁,该公司提供有关 .NET 的咨询、产品和服务,包括 SDLC 文档和体系结构框架 (www.pdsa.com)。Paul 是 Microsoft 在南加利福尼亚的区域负责人(英文)。他编写的 .NET 方面的书籍包括《asp.net Developer's Jumpstart》(Addison-Wesley) 以及在 PDSA Web 站点(英文)上列出的一些电子图书。