計算機網絡安全 —— 非對稱加密算法 RSA 和數字簽名(二)

一、非對稱加密算法基本概念 

       在對稱密鑰系統中,兩個參與者要共享同一個秘密密鑰。但怎樣才能做到這一點呢?一種是事先約定,另一種是用信使來傳送。在高度自動化的大型計算機網絡中,用信使來傳送密鑰顯然是不合適的。如果事先約定密鑰,就會給密鑰的管理和更換都帶來了極大的不便。當然我們可以使用複雜的密鑰分發中心(KeyDistributionCenter,KDC)來解決該問題,但採用公鑰密碼體制可以比較容易地解決這個問題。公鑰密碼體制的概念是由Stanford大學的研究人員Diffie與Hellman於1976年提出的。公鑰密碼體制使用不同的加密密鑰與解密密鑰

       由於加密密鑰不能用來解密,並且從加密密鑰不能推導出解密密鑰,因此加密密鑰可以公開。例如,參與者A可以在報紙上公布自己的加 密密鑰(即公鑰),而解密密鑰(即私鑰)自己秘密保存。任何參與都可以獲得該公鑰,並用來加密發送給參與者A的信息,而該信息只 能由A解密。可見採用公鑰密碼體制更易解決密鑰分發的問題。

       公鑰密碼體制有許多很好的特性,使得它不僅可以用於加密,還可以很方便地用於鑒別和數字簽名。但不幸的是,目前的公鑰密碼算法比對稱密碼算法要慢好幾個數量級。因此,對稱密碼被用於絕大部分加密,而公鑰密碼則通常用於會話密鑰的建立。例如,參與者A要發送 大量秘密信息給B。A首先選擇一個用於加密數據本身(如採用DES算法)的密鑰,由於該密鑰僅用於該次會話,被稱為會話密鑰。因為對稱密鑰由雙方共享,A必須將該會話密鑰通過秘密渠道告知B。為此,A用B的RSA公鑰加密該會話密鑰後發送給B。B收到加密的會話密 鑰後用自己的私鑰解密後得到會話密鑰。此後,A和B之間就可以用該會話密鑰加密通信的數據。具體流程如下圖:

二、數字簽名的基本概念

       在日常生活中,可以根據親筆簽名或印章來證明書信或文件的真實來源。但在計算機網絡中傳送的文電又如何蓋章呢?這就是數字簽名(digitalsignature)所要解決的問題。

       數字簽名必須保證以下三點:

  1.   接收方能夠核實發送方對報文的數字簽名;
  2.  發送方事後不能抵賴對報文的數字簽名;
  3.  任何人包括接收方都不能偽造對報文的簽名。

        現在已有多種實現數字簽名的方法。但採用公鑰算法要比採用對稱密鑰算法更容易實現。具體流程如下:

 

      我們知道公鑰密碼算法的計算代價非常大,對整個報文進行數字簽名是一件非常耗時的事情。更有效的方法是僅對報文摘要進行數字簽名。

      上述過程僅對報文進行了簽名,對報文X本身卻未保密。因為截獲DSKA(X)並知道發送方身份的任何人,通過查閱手冊即可獲得發送方的公鑰PKA,因而能得知電文內容。若採用下圖所示的方法,則可同時實現秘密通信和數字簽名。圖中SKA和SKB分別為A和B的私鑰,而PKA 和PKB分別為A和B的公鑰。具體流程如下:

二、.NET使用 RSA 算法

       RSA 的私鑰或者公鑰可以由算法自動生成,也可以讀取證書生成,同時我們可以使用 RSA 算法完成數字簽名,具體代碼如下:

  1 using System;
  2 using System.IO;
  3 using System.Security.Cryptography;
  4 using System.Security.Cryptography.X509Certificates;
  5 using System.Text;
  6 
  7 namespace encryption.rsa
  8 {
  9     /// <summary>
 10     /// //cloud.tencent.com/developer/article/1054441
 11     /// </summary>
 12     public class RsaAlgorithm
 13     {
 14         public Encoding Encoding { get; set; }
 15         public string PrivateKey { get;private set; }
 16         public string PublicKey { get;private set; }
 17 
 18         private  RSACryptoServiceProvider _rsa;
 19         private int _keySize;
 20         #region .ctor
 21 
 22         public RsaAlgorithm(int keySize=512)
 23         {
 24             _keySize = keySize;
 25             _rsa = new RSACryptoServiceProvider() { KeySize = _keySize };
 26             Encoding = Encoding.UTF8;
 27             PrivateKey = _rsa.ToXmlString(true);
 28             PublicKey = _rsa.ToXmlString(false);
 29         }
 30 
 31         #endregion
 32 
 33         #region 創建RSA
 34 
 35         /// <summary>
 36         /// 創建加密RSA
 37         /// </summary>
 38         /// <param name="publicKey">公鑰</param>
 39         /// <returns></returns>
 40         public  RSACryptoServiceProvider CreateEncryptRSA(string publicKey)
 41         {
 42             try
 43             {
 44                 _rsa = new RSACryptoServiceProvider() { KeySize = _keySize };
 45                 _rsa.FromXmlString(publicKey);
 46                 PublicKey = publicKey;
 47                 PrivateKey = null;
 48                 return _rsa;
 49             }
 50             catch (CryptographicException ex)
 51             {
 52                 throw ex;
 53             }
 54         }
 55 
 56         /// <summary>
 57         /// 根據字符串創建解密RSA
 58         /// </summary>
 59         /// <param name="privateKey">私鑰</param>
 60         /// <returns></returns>
 61         public RSACryptoServiceProvider CreateDecryptRSA(string privateKey)
 62         {
 63             try
 64             {
 65                 _rsa = new RSACryptoServiceProvider() { KeySize = _keySize };
 66                 _rsa.FromXmlString(privateKey);
 67                 PublicKey = null;
 68                 PrivateKey = privateKey;
 69                 return _rsa;
 70             }
 71             catch (CryptographicException ex)
 72             {
 73                 throw ex;
 74             }
 75         }
 76 
 77         /// <summary>
 78         /// 根據安全證書創建加密RSA
 79         /// </summary>
 80         /// <param name="certfile">公鑰文件</param>
 81         /// <returns></returns>
 82         public RSACryptoServiceProvider X509CertCreateEncryptRSA(string certfile)
 83         {
 84             try
 85             {
 86                 if (File.Exists(certfile)==false)
 87                 {
 88                     throw new ArgumentNullException(certfile, "加密證書未找到");
 89                 }
 90                 X509Certificate2 x509Cert = new X509Certificate2(certfile);
 91                 _rsa = (RSACryptoServiceProvider)x509Cert.PublicKey.Key;
 92                 return _rsa;
 93             }
 94             catch (CryptographicException ex)
 95             {
 96                 throw ex;
 97             }
 98         }
 99 
100         /// <summary>
101         /// 根據私鑰文件創建解密RSA
102         /// </summary>
103         /// <param name="keyfile">私鑰文件</param>
104         /// <param name="password">訪問含私鑰文件的密碼</param>
105         /// <returns></returns>
106         public RSACryptoServiceProvider X509CertCreateDecryptRSA(string keyfile, string password)
107         {
108             try
109             {
110                 if (File.Exists(keyfile)==false)
111                 {
112                     throw new ArgumentNullException(keyfile, "解密證書未找到");
113                 }
114                 X509Certificate2 x509Cert = new X509Certificate2(keyfile, password);
115                 _rsa = (RSACryptoServiceProvider)x509Cert.PrivateKey;
116                 return _rsa;
117             }
118             catch (CryptographicException ex)
119             {
120                 throw ex;
121             }
122         }
123 
124         #endregion
125 
126 
127         #region 加密
128 
129         /// <summary>
130         /// RSA 加密
131         /// </summary>
132         /// <param name="dataToEncrypt">待加密數據</param>
133         /// <returns></returns>
134         public string Encrypt(string dataToEncrypt)
135         {
136             byte[] bufferBytes = Encoding.GetBytes(dataToEncrypt);
137             return Convert.ToBase64String(this.Encrypt(bufferBytes));
138         }
139 
140         /// <summary>
141         /// RSA 加密
142         /// </summary>
143         /// <param name="dataToEncrypt">待加密數據</param>
144         /// <returns></returns>
145         public byte[] Encrypt(byte[] dataToEncrypt)
146         {
147             byte[] data = null;
148             int blockLen = _rsa.KeySize / 8 - 11;
149             if (dataToEncrypt.Length <= blockLen)
150             {
151                 return _rsa.Encrypt(dataToEncrypt, false);
152             }
153 
154             using (var dataStream = new MemoryStream(dataToEncrypt))
155             using (var enStream = new MemoryStream())
156             {
157                 Byte[] buffer = new Byte[blockLen];
158                 int len = dataStream.Read(buffer, 0, blockLen);
159 
160                 while (len > 0)
161                 {
162                     Byte[] block = new Byte[len];
163                     Array.Copy(buffer, 0, block, 0, len);
164 
165                     Byte[] enBlock = _rsa.Encrypt(block, false);
166                     enStream.Write(enBlock, 0, enBlock.Length);
167 
168                     len = dataStream.Read(buffer, 0, blockLen);
169                 }
170 
171                 data = enStream.ToArray();
172             }
173 
174             return data;
175         }
176 
177         #endregion
178 
179 
180         #region 解密
181 
182         /// <summary>
183         /// RSA 解密
184         /// </summary>
185         /// <param name="encryptedData">待解密數據<see cref="string"/></param>
186         /// <returns></returns>
187         public string Decrypt(string encryptedData)
188         {
189             string str = null;
190             byte[] buffer = Convert.FromBase64String(encryptedData);
191             return Encoding.GetString(this.Decrypt(buffer));
192         }
193 
194         /// <summary>
195         /// RSA 解密
196         /// </summary>
197         /// <param name="encryptedData">待解密數據(byte數組)<see cref="byte"/></param>
198         /// <returns></returns>
199         public byte[] Decrypt(byte[] encryptedData)
200         {
201             byte[] data = null;
202             int blockLen = _rsa.KeySize / 8;
203             if (encryptedData.Length <= blockLen)
204             {
205                 return _rsa.Decrypt(encryptedData, false);
206             }
207 
208             using (var dataStream = new MemoryStream(encryptedData))
209             using (var deStream = new MemoryStream())
210             {
211                 Byte[] buffer = new Byte[blockLen];
212                 int len = dataStream.Read(buffer, 0, blockLen);
213 
214                 while (len > 0)
215                 {
216                     Byte[] block = new Byte[len];
217                     Array.Copy(buffer, 0, block, 0, len);
218 
219                     Byte[] deBlock = _rsa.Decrypt(block, false);
220                     deStream.Write(deBlock, 0, deBlock.Length);
221 
222                     len = dataStream.Read(buffer, 0, blockLen);
223                 }
224 
225                 data = deStream.ToArray();
226             }
227 
228             return data;
229         }
230 
231         #endregion
232 
233         #region 簽名與驗簽
234         /// <summary>
235         ///  RSA 簽名
236         /// //docs.microsoft.com/zh-tw/dotnet/api/system.security.cryptography.rsacryptoserviceprovider.signdata?view=net-5.0
237         /// </summary>
238         /// <param name="hash">報文摘要算法</param>
239         /// <param name="str">報文數據</param>
240         /// <returns></returns>
241         public string Sign(string hash, string str)
242         {
243             byte[] data = Encoding.GetBytes(str);
244             byte[] sign = _rsa.SignData(data, hash);
245             return Convert.ToBase64String(sign);
246         }
247 
248         /// <summary>
249         /// 簽名
250         /// </summary>
251         /// <param name="hash">報文摘要算法</param>
252         /// <param name="data">報文數據</param>
253         /// <returns></returns>
254         public string Sign(string hash, byte[] data)
255         {
256             byte[] sign = _rsa.SignData(data, hash);
257             return Convert.ToBase64String(sign);
258         }
259 
260         /// <summary>
261         /// 驗簽
262         /// </summary>
263         /// <param name="data">報文數據</param>
264         /// <param name="hash">報文摘要算法</param>
265         /// <param name="sign">簽名</param>
266         /// <returns></returns>
267         public bool VerifySign(byte[] data, string hash,string sign)
268         {
269             byte[] signBytes = Convert.FromBase64String(sign);
270             return _rsa.VerifyData(data, hash, signBytes);
271         }
272 
273         /// <summary>
274         /// 驗簽
275         /// </summary>
276         /// <param name="data">報文數據</param>
277         /// <param name="hash">報文摘要算法</param>
278         /// <param name="sign">簽名</param>
279         /// <returns></returns>
280         public bool VerifySign(string data, string hash, string sign)
281         {
282             return VerifySign(Encoding.GetBytes(data),hash,sign);
283         }
284         #endregion
285     }
286 }

四、測試代碼與效果

       測試代碼如下:

 1   static void Main(string[] args)
 2         {
 3             {
 4                 Console.WriteLine("-----------------------------------------------------RSA 字符串加密與解密以及簽名與驗簽--------------------------------------------------");
 5                 var input = "公鑰密碼體制中,目前最著名的是由美國三位科學家Rivest, Shamir 和 Adleman 於1976年提出,並在1978年正式發表的RSA 算法。";
 6                 Console.Write($"加密內容:{input}\r\n");
 7                 var rsa = new RsaAlgorithm();
 8 
 9                 Console.WriteLine($"RSA私鑰:\r\n{rsa.PrivateKey}\r\n");
10                 var encrypt = rsa.Encrypt(input);
11                 Console.WriteLine($"RSA加密後內容:\r\n{encrypt}\r\n");
12                 var sign = rsa.Sign("SHA1", input);
13                 Console.WriteLine($"RSA生成數字簽名[SHAI]:\r\n{sign}\r\n");
14 
15                 Console.WriteLine($"RSA公鑰:\r\n{rsa.PublicKey}\r\n");
16                 var decrypt = rsa.Decrypt(encrypt);
17                 Console.WriteLine($"RSA解密後內容:\r\n{decrypt}\r\n");
18                 string signResult = rsa.VerifySign(decrypt, "SHA1", sign) ? "驗簽通過" : "驗簽未通過";
19                 Console.WriteLine($"RSA進行鑒別數字簽名:{signResult}");
20             }
21 
22             {
23                 Console.WriteLine("-----------------------------------------------------RSA 文件加密與解密--------------------------------------------------");
24                 var input = System.IO.File.ReadAllBytes(@"C:\Users\97460\Desktop\1.rar");
25                 Console.Write($"加密內容:{Convert.ToBase64String(input)}\r\n");
26                 var rsa = new RsaAlgorithm(1024);
27 
28                 Console.WriteLine($"RSA私鑰:\r\n{rsa.PrivateKey}\r\n");
29                 var encrypt = rsa.Encrypt(input);
30                 Console.WriteLine($"RSA加密後內容:\r\n{Convert.ToBase64String(encrypt)}\r\n");
31 
32                 Console.WriteLine($"RSA公鑰:\r\n{rsa.PublicKey}\r\n");
33                 var decrypt = rsa.Decrypt(encrypt);
34                 Console.WriteLine($"RSA解密後內容:\r\n{Convert.ToBase64String(decrypt)}\r\n");
35                 System.IO.File.WriteAllBytes("1.rar", decrypt);
36             }
37 
38             {
39                 Console.WriteLine("-----------------------------------------------------RSA 使用證書加密與解密字符串--------------------------------------------------");
40                 var input = "公鑰密碼體制中,目前最著名的是由美國三位科學家Rivest, Shamir 和 Adleman 於1976年提出,並在1978年正式發表的RSA 算法。";
41                 Console.Write($"加密內容:{input}\r\n");
42 
43                 // 證書加密
44                 var rsaEncrypt = new RsaAlgorithm();
45                 rsaEncrypt.X509CertCreateEncryptRSA(@"RSAKey.cer");
46                 Console.WriteLine($"RSA私鑰:\r\n{rsaEncrypt.PrivateKey}\r\n");
47                 var encrypt = rsaEncrypt.Encrypt(input);
48                 Console.WriteLine($"RSA加密後內容:\r\n{encrypt}\r\n");
49 
50                 // 證書解密
51                 var rsaDecrypt = new RsaAlgorithm(1024);
52                 rsaDecrypt.X509CertCreateDecryptRSA(@"RSAKey.pfx", "888888");
53                 Console.WriteLine($"RSA公鑰:\r\n{rsaEncrypt.PublicKey}\r\n");
54                 var decrypt = rsaDecrypt.Decrypt(encrypt);
55                 Console.WriteLine($"RSA解密後內容:\r\n{decrypt}\r\n");
56             }
57             Console.ReadKey();
58         }

 代碼示例://github.com/Dwayne112401/encryption

相關內容:計算機網絡安全 —— 對稱加密算法 DES (一)

Tags: