加密解密概述及.NET中对加密解密的支持(二)

.NET中加密解密的支持

相信通过前面几页的叙述,大家已经明白了加密解密、数字签名的基本原理,下面我们看一下在.NET中是如何来支持加密解密的。正如上面我们所进行的分类,.NET中也提供了两组类用于加密解密,一组为对称加密,一组为非对称加密。这些类按照名称还可以分为两组,一组后缀为“CryptoServiceProvider”的,是对于底层Windows API的包装类,一组后缀为“Managed”,是在.NET中全新编写的类。

.NET对称加密解密支持

.NET Framework提供了一些常见的对称算法的实现,包括DES、RC2等。下面列出了与对称算法有关的类的结构:

System.Security.Cryptography.SymmetricAlgorithm

System.Security.Cryptography.Aes
System.Security.Cryptography.AesCryptoServiceProvider
System.Security.Cryptography.AesManaged
System.Security.Cryptography.DES

System.Security.Cryptography.DESCryptoServiceProvider
System.Security.Cryptography.RC2

System.Security.Cryptography.RC2CryptoServiceProvider
System.Security.Cryptography.Rijndael

System.Security.Cryptography.RijndaelManaged
System.Security.Cryptography.TripleDES

System.Security.Cryptography.TripleDESCryptoServiceProvider

这些类中黑体表示的类为实现算法的具体类,其它的都是抽象类;也可以根据需要实现自己的对称算法。

.NET非对称加密解密支持

RSA是当今最常用的非对称算法,其被应用于很多领域。RSA算法的理论依据来自于一个大素数所具有的特性:对于给定的两个大素数A与B,很容易计算出它们的乘积;但是,仅知道AB的成绩却很难计算原来的A与B各自的值。

.NET Frameork提供了两个类供我们使用RSA算法:用于加密数据的RSACryptoServiceProvider类以及用于对数据做数字签名的DSACryptoServiceProvider类(DSA: Digital Signature Algorithm,数字签名算法),其类的层次结构如下

System.Security.Cryptography.AsymmetricAlgorithm
System.Security.Cryptography.DSA

System.Security.Cryptography.DSACryptoServiceProvider
System.Security.Cryptography.RSA

System.Security.Cryptography.RSACryptoServiceProvider

System.Security.Cryptography.ECDiffieHellman(椭圆曲线Diffie-Hellman (ECDH) 算法实现)

System.Security.Cryptography.ECDiffieHellmanCng(椭圆曲线Diffie-Hellman (ECDH) 算法的下一代加密技术 (CNG) 实现)
System.Security.Cryptography.ECDsa

System.Security.Cryptography.ECDsaCng(椭圆曲线数字签名算法 (ECDSA) 的下一代加密技术 (CNG) 实现)

.NET散列算法支持

散列算法是把长长的一列数据变成较短的代码,这是最流行的64位散列密钥。两个最流行的散列算法是SHA(Secured Hash Algorithm)和MD5(MessageDigest version5)。这些散列密钥用于标记数字文档。

散列值是根据一个数据集合计算出来的数字。如果数据集合不同,那么计算出来的数字基本上也是不同的。.NET Framework在其System.Security.Cryptography命名空间下提供了一些主要的散列算法,包括:SHAx、RIPEMD160、与MD5。

System.Security.Cryptography.HashAlgorithm

System.Security.Cryptography.KeyedHashAlgorithm(键控哈希算法的实现基类)

System.Security.Cryptography.HMAC (基于哈希的消息验证代码 (HMAC)的抽象类)

System.Security.Cryptography.HMACMD5

System.Security.Cryptography.HMACRIPEMD160

System.Security.Cryptography.HMACSHA1

System.Security.Cryptography.HMACSHA256

System.Security.Cryptography.HMACSHA384

System.Security.Cryptography.HMACSHA512

System.Security.Cryptography.MACTripleDES
System.Security.Cryptography.MD5

System.Security.Cryptography.MD5Cng(128 位哈希算法的 CNG(下一代加密技术)实现

System.Security.Cryptography.MD5CryptoServiceProvider

System.Security.Cryptography.RIPEMD160

System.Security.Cryptography.RIPEMD160Managed

System.Security.Cryptography.SHA1

System.Security.Cryptography.SHA1CryptoServiceProvider
System.Security.Cryptography.SHA1Managed

System.Security.Cryptography.SHA256

System.Security.Cryptography.SHA256Managed

System.Security.Cryptography.SHA384

System.Security.Cryptography.SHA384Managed

System.Security.Cryptography.SHA512

System.Security.Cryptography.SHA512Managed

KeyedHash

键控哈希算法是依赖于密钥的单向哈希函数,用作消息验证代码。只有知道密钥的人才能验证哈希值。加密哈希算法提供没有机密的真实性。

SHA

SHA(安全散列算法)是一个块密码,在64位的数据块上执行,这个算法的改进版本采用了更大的密钥值,当然,密钥值越大所需的计算时间越长。而且,对于相对较小的文件,散列值越小就越安全,就是说,散列算法的块大小应小于或等于数据块本身的大小。 SHA1算法的散列范围是160位。

.NET Framework也提供了更大的密钥值算法,分别为SHA256、SHA384和SHA512。名称最后的数字表示其块大小。

ASP.NET安全性中的成员资格提供程序使用SHA1加密用户密码。

MD5

MD5表示MessageDigest版本5。它是一个加密的单向散列算法。MD5有抗伪造,计算成本低且执行简单等特点。现在MD5已成为散列算法的事实标准。

.NET Framework提供了MD5CryptoServiceProvider这个类实现MD5算法。该类与SHA1共享同一个基类,前面的示例也只需要做简单的改动即可成为使用MD5进行处理的例子。

RIPEMD-160

基于MD5的RIPEMD-160最早出现于欧洲,是一个使用160位的散列算法。.NET也引入了对这种算法的支持。

对称加密解密示例

现在假设我们以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.如果是加密,在provider上调用CreateEncryptor()方法,创建一个ICryptoTransform类型的加密器对象;如果是解密,在provider上调用CreateDecryptor()方法,同样是创建一个ICryptoTransform类型的解密器对象。ICryptoTransform定义了加密转换的运算,.NET将在底层调用这个接口。

4.因为流和byte[]是数据类型无关的一种数据结构,可以保存和传输任何形式的数据,区别只是byte[]是一个静态的概念而流是一个动态的概念。因此,.NET采用了流的方式进行加密和解密,我们可以想到有两个流,一个是明文流,含有加密前的数据;一个是密文流,含有加密后的数据。那么就必然有一个中介者,将明文流转换为密文流;或者将密文流转换为明文流。.NET中执行这个操作的中介者也是一个流类型,叫做CryptoStream。它的构造函数如下,共有三个参数:

publicCryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode)

5. 当加密时,stream为密文流(注意此时密文流还没有包含数据,仅仅是一个空流);ICryptoTransform是第3步创建的加密器,包含着加密的算法;CryptoStreamMode枚举为Write,意思是将流经CryptoStream的明文流写入到密文流中。最后,从密文流中获得加密后的数据。

6. 当解密时,stream为密文流(此时密文流含有数据);ICryptoTransform是第3步创建的解密器,包含着解密的算法;CryptoStreamMode枚举为Read,意思是将密文流中的数据读出到byte[]数组中,进而再由byte[]转换为明文流、明文字符串。

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

// 对称加密帮助类 public class CryptoHelper { private SymmetricAlgorithm provider; private ICryptoTransform encryptor; private ICryptoTransform decryptor; private const int BufferSize = 1024; public CryptoHelper(string algorithmName) { provider = SymmetricAlgorithm.Create(algorithmName); string strKey = ConfigurationManager.AppSettings["CryptoKey"]; string strIV = ConfigurationManager.AppSettings["CryptoIV"]; if (string.IsNullOrEmpty(strKey) || string.IsNullOrEmpty(strIV)) throw new ArgumentNullException("CryptoKey"); //Key byte[] bsKey = Encoding.UTF8.GetBytes(strKey); int keySize = provider.KeySize/8; if (bsKey.Length != keySize) { byte[] key = new byte[keySize]; if (bsKey.Length > keySize) Array.Copy(bsKey, key, keySize); else Array.Copy(bsKey, key, bsKey.Length); provider.Key = key; } else { provider.Key = bsKey; } //IV byte[] bsIV = Encoding.UTF8.GetBytes(strIV); int ivSize = provider.BlockSize / 8; if (bsIV.Length != ivSize) { byte[] iv = new byte[ivSize]; if(bsIV.Length > ivSize) Array.Copy(bsIV, iv, ivSize); else Array.Copy(bsIV, iv, bsIV.Length); provider.IV = iv; } else { provider.IV = bsIV; } encryptor = provider.CreateEncryptor(); decryptor = provider.CreateDecryptor(); } public CryptoHelper() : this("TripleDES") { } // Encrypt public string Encrypt(string clearText) { byte[] clearBuffer = Encoding.UTF8.GetBytes(clearText); MemoryStream clearStream = null; MemoryStream encryptedStream = null; CryptoStream cryptoStream = null; try { clearStream = new MemoryStream(clearBuffer); encryptedStream = new MemoryStream(); cryptoStream = new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write); byte[] buffer = new byte[BufferSize]; int bytesRead = 0; while((bytesRead = clearStream.Read(buffer, 0, BufferSize)) > 0) { cryptoStream.Write(buffer, 0, bytesRead); } cryptoStream.FlushFinalBlock(); buffer = encryptedStream.ToArray(); string encryptedText = Convert.ToBase64String(buffer); return encryptedText; } finally { if (cryptoStream != null) { cryptoStream.Close(); cryptoStream = null; } if (encryptedStream != null) { encryptedStream.Close(); encryptedStream = null; } if (clearStream != null) { clearStream.Close(); clearStream = null; } if (provider != null) { provider.Clear(); } } } // Decrypt public string Decrypt(string encryptedText) { byte[] encryptedBuffer = Convert.FromBase64String(encryptedText); byte[] buffer = new byte[BufferSize]; Stream encryptedStream = null; MemoryStream clearStream = null; CryptoStream cryptoStream = null; try { encryptedStream = new MemoryStream(encryptedBuffer); clearStream = new MemoryStream(); cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read); int bytesRead = 0; while ((bytesRead = cryptoStream.Read(buffer, 0, BufferSize)) > 0) { clearStream.Write(buffer, 0, bytesRead); } buffer = clearStream.GetBuffer(); string clearText = Encoding.UTF8.GetString(buffer, 0, (int)clearStream.Length); return clearText; } finally { if (encryptedStream != null) { encryptedStream.Close(); encryptedStream = null; } if (clearStream != null) { clearStream.Close(); clearStream = null; } if (cryptoStream != null) { cryptoStream.Close(); cryptoStream = null; } if (provider != null) { provider.Clear(); } } } }


非对称加密解密示例

如果对文件的安全性要求不是很严格,只是控制文件在传输中的完整性,可以用散列算法算出一个文件的散列,然后用非对称算法加密并把加密后的信息附加于文件中,接收方使用收到的文件中的散列验证文件的完整性,同时也能确认文件的发送者,即实现了简单的文件签名的功能。

下面示例演示了如何使用RSACryptoServiceProvider类加密一个字符串。其中,ExpertParameter(bool)方法根据参数的真假允许获得公钥/私钥对(true)或只有公钥(false)。

using System; using System.Text; using System.Security.Cryptography; class Program { static void Main() { string sMsg = "The message to encrypt!"; string sEnc, sDec; Encoding utf = new UTF8Encoding(); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); RSAParameters publicKey = rsa.ExportParameters(false); RSAParameters publicAndPrivateKey = rsa.ExportParameters(true); { RSACryptoServiceProvider rsaEncryptor = new RSACryptoServiceProvider(); rsaEncryptor.ImportParameters(publicKey); byte[] bMsg = utf.GetBytes(sMsg); byte[] bEnc = rsaEncryptor.Encrypt(bMsg, false); sEnc = Convert.ToBase64String(bEnc); } { RSACryptoServiceProvider rsaDecryptor = new RSACryptoServiceProvider(); rsaDecryptor.ImportParameters(publicAndPrivateKey); byte[] bEnc = Convert.FromBase64String(sEnc); byte[] bDec = rsaDecryptor.Decrypt(bEnc, false); sDec = utf.GetString(bDec); } Console.WriteLine("Message : " + sMsg); Console.WriteLine("Encrypted: " + sEnc); Console.WriteLine("Decrypted: " + sDec); } }


哈希计算示例

以下以MD5算法计算哈希值为例,给出简单示例。

public string MD5Crypto(string input) { MD5 md5Hasher = null; try { // 创建? MD5CryptoServiceProvider 对象U的I实例a md5Hasher = MD5.Create(); //计算Z哈u希o代a码 byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input)); StringBuilder sBuilder = new StringBuilder(); for (int i = 0; i < data.Length; i++) { sBuilder.Append(data[i].ToString("X2")); } return sBuilder.ToString(); } finally { if (md5Hasher != null) { md5Hasher.Clear(); md5Hasher.Dispose(); md5Hasher = null; } } }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值