加密解密概述及.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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值