.Net中的加密解密

  • 2019 年 10 月 3 日
  • 筆記

在一些比较重要的应用场景中,通过网络传递数据需要进行加密以保证安全。

说到加密,可能大家最熟悉的就是MD5了

MD5  不可逆加密:原文–加密–密文,密文无法解密出原文

MD5实际上只是一种散列运算,或者可以称为单向的加密,即是说无法根据密文(加密后的数据),推导出明文(原数据)

MD5(单向散列算法)的全称是Message-Digest Algorithm 5(信息摘要算法),经MD2MD3MD4发展而来。MD5算法的使用不需要支付任何版权费用。

 

MD5功能:
    输入任意长度的信息,经过处理,输出为128位的信息(数字指纹);
    不同的输入得到的不同的结果(唯一性);
    根据128位的输出结果不可能反推出输入的信息(不可逆);

MD5属不属于加密算法:
    认为不属于的人是因为他们觉得不能从密文(散列值)反过来得到原文,即没有解密算法,所以这部分人认为MD5只能属于算法,不能称为加密算法;
    认为属于的人是因为他们觉得经过MD5处理后看不到原文,即已经将原文加密,所以认为MD5属于加密算法;

 

MD5用途:
    1、防止被篡改:
    1)比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果ab一样就代表中途未被篡改。2)比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。3SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5.

    2、防止直接看到明文:
    现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码(其实这样是不安全的,后面我会提到)。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。)

    3、防止抵赖(数字签名):
    这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的数字签名

 

MD5安全性:
    普遍认为MD5是很安全,因为暴力破解的时间是一般人无法接受的。实际上如果把用户的密码MD5处理后再存储到数据库,其实是很不安全的。因为用户的密码是比较短的,而且很多用户的密码都使用生日,手机号码,身份证号码,电话号码等等。或者使用常用的一些吉利的数字,或者某个英文单词。如果我把常用的密码先MD5处理,把数据存储起来,然后再跟你的MD5结果匹配,这时我就有可能得到明文。

这里推荐采用MD5加盐的方法,应对简单密码破解。在原始密码加上指定字符串或MD5再MD5或MD5加盐再MD5。。。

 

 1     /// <summary>   2     /// 不可逆加密   3     /// 1 防止被篡改   4     /// 2 防止明文存储   5     /// 3 防止抵赖,数字签名   6     /// </summary>   7     public class MD5Encrypt   8     {   9         #region MD5  10         /// <summary>  11         /// MD5加密,和动网上的16/32位MD5加密结果相同,  12         /// 使用的UTF8编码  13         /// </summary>  14         /// <param name="source">待加密字串</param>  15         /// <param name="length">16或32值之一,其它则采用.net默认MD5加密算法</param>  16         /// <returns>加密后的字串</returns>  17         public static string Encrypt(string source, int length = 32)//默认参数  18         {  19             if (string.IsNullOrEmpty(source)) return string.Empty;  20             HashAlgorithm provider = CryptoConfig.CreateFromName("MD5") as HashAlgorithm;  21             byte[] bytes = Encoding.UTF8.GetBytes(source);//这里需要区别编码的  22             byte[] hashValue = provider.ComputeHash(bytes);  23             StringBuilder sb = new StringBuilder();  24             switch (length)  25             {  26                 case 16://16位密文是32位密文的9到24位字符  27                     for (int i = 4; i < 12; i++)  28                     {  29                         sb.Append(hashValue[i].ToString("x2"));  30                     }  31                     break;  32                 case 32:  33                     for (int i = 0; i < 16; i++)  34                     {  35                         sb.Append(hashValue[i].ToString("x2"));  36                     }  37                     break;  38                 default:  39                     for (int i = 0; i < hashValue.Length; i++)  40                     {  41                         sb.Append(hashValue[i].ToString("x2"));  42                     }  43                     break;  44             }  45             return sb.ToString();  46         }  47         #endregion MD5  48  49         #region MD5摘要  50         /// <summary>  51         /// 获取文件的MD5摘要  52         /// </summary>  53         /// <param name="fileName"></param>  54         /// <returns></returns>  55         public static string AbstractFile(string fileName)  56         {  57             using (FileStream file = new FileStream(fileName, FileMode.Open))  58             {  59                 return AbstractFile(file);  60             }  61         }  62         /// <summary>  63         /// 根据stream获取文件摘要  64         /// </summary>  65         /// <param name="stream"></param>  66         /// <returns></returns>  67         public static string AbstractFile(Stream stream)  68         {  69             MD5 md5 = new MD5CryptoServiceProvider();  70             byte[] retVal = md5.ComputeHash(stream);  71  72             StringBuilder sb = new StringBuilder();  73             for (int i = 0; i < retVal.Length; i++)  74             {  75                 sb.Append(retVal[i].ToString("x2"));  76             }  77             return sb.ToString();  78         }  79         #endregion  80     }

View Code

 

MD5特性:

 

 

1 相同原文加密的结果是一样的
2 不同长度的内容加密后加过都是32位
3 原文差别很小,结果差别很大
4 不管文件多大,都能产生32位长度摘要
5 文件内容有一点改动,结果变化非常大
6 文件内容不变,名字边了,结果是不变

 

消息在接收方和发送方进行安全传递,一般要满足下面三个要点:

1、消息的发送方能够确定消息只有预期的接收方可以解密(不保证第三方无法获得,但保证第三方无法解密)。

2、消息的接收方可以确定消息是由谁发送的(消息的接收方可以确定消息的发送方)。

3、消息的接收方可以确定消息在途中没有被篡改过(必须确认消息的完整性)。

 

对称加密

对称可逆加密:加密后能解密回原文,加密key和解密key是一个

 

加密算法都是公开的,密钥是保密的, 即使拿到密文 你是推算不了密钥 也推算不了原文
加密解密的速度快,问题是密钥的安全

DES算法

美国国家标准局1973年开始研究除国防部外的其它部门的计算机系统的数据加密标准,1977年1月,美国政府颁布:采纳IBM公司设计的方案作为非机密数据的正式数据加密标准(DES?Data Encryption Standard)

加密算法要达到的目的主要为以下四点:

☆提供高质量的数据保护,防止数据未经授权的泄露和未被察觉的修改;

☆具有相当高的复杂性,使得破译的开销超过可能获得的利益,同时又要便于理解和掌握;

☆DES密码体制的安全性应该不依赖于算法的保密,其安全性仅以加密密钥的保密为基础;

☆实现经济,运行有效,并且适用于多种完全不同的应用

目前在国内,随着三金工程尤其是金卡工程的启动,DES算法在POS、ATM、磁卡及智能卡(IC卡)、加油站、高速公路收费站等领域被广泛应用,以此来实现关键数据的保密,如信用卡持卡人的PIN的加密传输,IC卡与POS间的双向认证、金融交易数据包的MAC校验等,均用到DES算法

DES算法的入口参数有三个:Key、Data、Mode。其中Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密。 
DES算法是这样工作的:如Mode为加密,则用Key 去把数据Data进行加密, 生成Data的密码形式(64位)作为DES的输出结果;如Mode为解密,则用Key去把密码形式的数据Data解密,还原为Data的明码形式(64位)作为DES的输出结果

 1     /// <summary>   2     /// DES AES Blowfish   3     ///  对称加密算法的优点是速度快,   4     ///  缺点是密钥管理不方便,要求共享密钥。   5     /// 可逆对称加密  密钥长度8   6     /// </summary>   7     public class DesEncrypt   8     {   9         private static byte[] _rgbKey = ASCIIEncoding.ASCII.GetBytes(Constant.DesKey.Substring(0, 8));  10         private static byte[] _rgbIV = ASCIIEncoding.ASCII.GetBytes(Constant.DesKey.Insert(0, "w").Substring(0, 8));  11  12         /// <summary>  13         /// DES 加密  14         /// </summary>  15         /// <param name="text">需要加密的值</param>  16         /// <returns>加密后的结果</returns>  17         public static string Encrypt(string text)  18         {  19             DESCryptoServiceProvider dsp = new DESCryptoServiceProvider();  20             using (MemoryStream memStream = new MemoryStream())  21             {  22                 CryptoStream crypStream = new CryptoStream(memStream, dsp.CreateEncryptor(_rgbKey, _rgbIV), CryptoStreamMode.Write);  23                 StreamWriter sWriter = new StreamWriter(crypStream);  24                 sWriter.Write(text);  25                 sWriter.Flush();  26                 crypStream.FlushFinalBlock();  27                 memStream.Flush();  28                 return Convert.ToBase64String(memStream.GetBuffer(), 0, (int)memStream.Length);  29             }  30         }  31  32         /// <summary>  33         /// DES解密  34         /// </summary>  35         /// <param name="encryptText"></param>  36         /// <returns>解密后的结果</returns>  37         public static string Decrypt(string encryptText)  38         {  39             DESCryptoServiceProvider dsp = new DESCryptoServiceProvider();  40             byte[] buffer = Convert.FromBase64String(encryptText);  41  42             using (MemoryStream memStream = new MemoryStream())  43             {  44                 CryptoStream crypStream = new CryptoStream(memStream, dsp.CreateDecryptor(_rgbKey, _rgbIV), CryptoStreamMode.Write);  45                 crypStream.Write(buffer, 0, buffer.Length);  46                 crypStream.FlushFinalBlock();  47                 return ASCIIEncoding.UTF8.GetString(memStream.ToArray());  48             }  49         }  50     }

View Code

非对称加密

非对称加密的接收者和发送者都持有两个密钥,一个是对外公开的,称为公钥,一个是自行保管的,称为私钥。非对称加密的规则是由某人A的公钥加密的消息,只能由A的私钥进行解密;由A的私钥加密的消息只能由A的公钥解密。此时我们可以得出接收方、发送方有两个公钥两个私钥一共四个密钥,我们先看看两种简单的方式,这两种方式都是只使用两个密钥。

第一种模式只使用接收方的公钥和私钥,称为加密模式。

加密模式

在加密模式中,由消息的接收方发布公钥,持有私钥

步骤:

  1. 发送方使用接收者的公钥进行加密消息,然后发送。
  2. 接收方使用自己的私钥对消息进行解密。

消息的发送方能够确定消息只有预期的接收方可以解密(不保证第三方无法获得,但保证第三方无法解密)

认证模式

在认证模式中,由消息的发送方发布公钥,持有私钥

步骤:

  1. 发送者使用自己的私钥对消息进行加密,然后发送。
  2. 接收者使用发送者的公钥对消息进行解密。

消息的接收方可以确定消息是由谁发送的(消息的接收方可以确定消息的发送方)

算法是公开的,加密key和解密key是不能互相推导的  有了密文,没有解密key,也推导不出原文

加密解密速度不快 安全性好
公开加密key,保证数据的安全传递
公开解密key,保证数据的不可抵赖

RSA算法

它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。算法的名字以发明者的名字命名:Ron Rivest, Adi Shamir 和Leonard Adleman。但RSA的安全性一直未能得到理论上的证明。它经历了各种攻击,至今未被完全攻破

RSA 的安全性

RSA的安全性依赖于大数分解,但是否等同于大数分解一直未能得到理论上的证明,因为没有证明破解 RSA就一定需要作大数分解。假设存在一种无须分解大数的算法,那它肯定可以修改成为大数分解算法。目前, RSA 的一些变种算法已被证明等价于大数分解。不管怎样,分解n是最显然的攻击方法。现在,人们已能分解多个十进制位的大素数。因此,模数n 必须选大一些,因具体适用情况而定

RSA的速度

由于进行的都是大数计算,使得RSA最快的情况也比DES慢上倍,无论是软件还是硬件实现。速度一直是RSA的缺陷。一般来说只用于少量数据加密

 RSA的缺点:

A)产生密钥很麻烦,受到素数产生技术的限制,因而难以做到一次一密。B)分组长度太大,为保证安全性,n 至少也要 600 bits 以上,使运算代价很高,尤其是速度较慢,较对称密码算法慢几个数量级;且随着大数分解技术的发展,这个长度还在增加,不利于数据格式的标准化。目前,SET( Secure Electronic Transaction )协议中要求CA采用比特长的密钥,其他实体使用比特的密钥

 1     /// <summary>   2     /// RSA ECC   3     /// 可逆非对称加密   4     /// 非对称加密算法的优点是密钥管理很方便,缺点是速度慢。   5     /// </summary>   6     public class RsaEncrypt   7     {   8         /// <summary>   9         /// 获取加密/解密对  10         /// 给你一个,是无法推算出另外一个的  11         ///  12         /// Encrypt   Decrypt  13         /// </summary>  14         /// <returns>Encrypt   Decrypt</returns>  15         public static KeyValuePair<string, string> GetKeyPair()  16         {  17             RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();  18             string publicKey = RSA.ToXmlString(false);  19             string privateKey = RSA.ToXmlString(true);  20             return new KeyValuePair<string, string>(publicKey, privateKey);  21         }  22  23         /// <summary>  24         /// 加密:内容+加密key  25         ///  26         /// </summary>  27         /// <param name="content"></param>  28         /// <param name="encryptKey">加密key</param>  29         /// <returns></returns>  30         public static string Encrypt(string content, string encryptKey)  31         {  32             RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();  33             rsa.FromXmlString(encryptKey);  34             UnicodeEncoding ByteConverter = new UnicodeEncoding();  35             byte[] DataToEncrypt = ByteConverter.GetBytes(content);  36             byte[] resultBytes = rsa.Encrypt(DataToEncrypt, false);  37             return Convert.ToBase64String(resultBytes);  38         }  39  40         /// <summary>  41         /// 解密  内容+解密key  42         /// </summary>  43         /// <param name="content"></param>  44         /// <param name="decryptKey">解密key</param>  45         /// <returns></returns>  46         public static string Decrypt(string content, string decryptKey)  47         {  48             byte[] dataToDecrypt = Convert.FromBase64String(content);  49             RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();  50             RSA.FromXmlString(decryptKey);  51             byte[] resultBytes = RSA.Decrypt(dataToDecrypt, false);  52             UnicodeEncoding ByteConverter = new UnicodeEncoding();  53             return ByteConverter.GetString(resultBytes);  54         }  55  56  57         /// <summary>  58         /// 可以合并在一起的,每次产生一组新的密钥  59         /// </summary>  60         /// <param name="content"></param>  61         /// <param name="encryptKey">加密key</param>  62         /// <param name="decryptKey">解密key</param>  63         /// <returns>加密后结果</returns>  64         private static string Encrypt(string content, out string publicKey, out string privateKey)  65         {  66             RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider();  67             publicKey = rsaProvider.ToXmlString(false);  68             privateKey = rsaProvider.ToXmlString(true);  69  70             UnicodeEncoding ByteConverter = new UnicodeEncoding();  71             byte[] DataToEncrypt = ByteConverter.GetBytes(content);  72             byte[] resultBytes = rsaProvider.Encrypt(DataToEncrypt, false);  73             return Convert.ToBase64String(resultBytes);  74         }  75     }

View Code

数字签名

基本实现

数字签名实际上就是上面非对称加密时的认证模式,只不过做了一点点的改进,加入了散列算法。大家比较熟悉的散列算法可能就是MD5

那么如何通过引入散列函数来保证数据的完整性呢?也就是接收方能够确认消息确实是由发送方发来的,而没有在中途被修改过。具体的过程如下:

  1. 发送方将想要进行传递的消息进行一个散列运算,得到消息摘要。
  2. 发送方使用自己的私钥对摘要进行加密,将消息和加密后的摘要发送给接收方。
  3. 接收方使用发送方的公钥对消息和消息摘要进行解密(确认了发送方)。
  4. 接收方对收到的消息进行散列运算,得到一个消息摘要。
  5. 接收方将上一步获得的消息摘要与发送方发来的消息摘要进行对比。如果相同,说明消息没有被改动过;如果不同,说明消息已经被篡改。

高级实现

我们将其分为发送方和接收方两部分来看。先看看发送方需要执行的步骤:

  1. 将消息进行散列运算,得到消息摘要。
  2. 使用自己的私钥对消息摘要加密(认证模式:确保了接收方能够确认自己)。
  3. 使用接收方的公钥对消息进行加密(加密模式:确保了消息只能由期望的接收方解密)。
  4. 发送消息和消息摘要。

接下来我们看一下接收方所执行的步骤:

  1. 使用发送方的公钥对消息摘要进行解密(确认了消息是由谁发送的)。
  2. 使用自己的私钥对消息进行解密(安全地获得了实际应获得的信息)。
  3. 将消息进行散列运算,获得消息摘要。
  4. 将上一步获得的消息摘要 和 第一步解密的消息摘要进行对比(确认了消息是否被篡改)。

证书机制

与数字签名相关的一个概念就是证书机制了,证书是用来做什么呢?在上面的各种模式中,我们一直使用了这样一个假设,就是接收方或者发送方所持有的、对方的公钥总是正确的(确实是对方公布的)。而实际上除非对方手把手将公钥交给我们,否则如果不采取措施,双方在网络中传递公钥时,一样有可能被篡改。那么怎样解决这个问题呢?这时就需要证书机制了:可以引入一个公正的第三方,当某一方想要发布公钥时,它将自身的身份信息及公钥提交给这个第三方,第三方对其身份进行证实,如果没有问题,则将其信息和公钥打包成为证书(Certificate)。而这个公正的第三方,就是常说的证书颁发机构(Certificate Authority)。当我们需要获取公钥时,只需要获得其证书,然后从中提取出公钥就可以了

本文参考文档:

https://www.cnblogs.com/JimmyZhang/archive/2008/10/02/Cryptograph.html

https://www.iplaysoft.com/encrypt-arithmetic.html