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