一、非對稱加密算法基本概念
在對稱密鑰系統中,兩個參與者要共享同一個秘密密鑰。但怎樣才能做到這一點呢?一種是事先約定,另一種是用信使來傳送。在高度自動化的大型計算機網絡中,用信使來傳送密鑰顯然是不合適的。如果事先約定密鑰,就會給密鑰的管理和更換都帶來了極大的不便。當然我們可以使用復雜的密鑰分發中心(KeyDistributionCenter,KDC)來解決該問題,但采用公鑰密碼體制可以比較容易地解決這個問題。公鑰密碼體制的概念是由Stanford大學的研究人員Diffie與Hellman於1976年提出的。公鑰密碼體制使用不同的加密密鑰與解密密鑰。
由於加密密鑰不能用來解密,並且從加密密鑰不能推導出解密密鑰,因此加密密鑰可以公開。例如,參與者A可以在報紙上公布自己的加 密密鑰(即公鑰),而解密密鑰(即私鑰)自己秘密保存。任何參與都可以獲得該公鑰,並用來加密發送給參與者A的信息,而該信息只 能由A解密。可見采用公鑰密碼體制更易解決密鑰分發的問題。
公鑰密碼體制有許多很好的特性,使得它不僅可以用於加密,還可以很方便地用於鑒別和數字簽名。但不幸的是,目前的公鑰密碼算法比對稱密碼算法要慢好幾個數量級。因此,對稱密碼被用於絕大部分加密,而公鑰密碼則通常用於會話密鑰的建立。例如,參與者A要發送 大量秘密信息給B。A首先選擇一個用於加密數據本身(如采用DES算法)的密鑰,由於該密鑰僅用於該次會話,被稱為會話密鑰。因為對稱密鑰由雙方共享,A必須將該會話密鑰通過秘密渠道告知B。為此,A用B的RSA公鑰加密該會話密鑰后發送給B。B收到加密的會話密 鑰后用自己的私鑰解密后得到會話密鑰。此后,A和B之間就可以用該會話密鑰加密通信的數據。具體流程如下圖:

二、數字簽名的基本概念
在日常生活中,可以根據親筆簽名或印章來證明書信或文件的真實來源。但在計算機網絡中傳送的文電又如何蓋章呢?這就是數字簽名(digitalsignature)所要解決的問題。
數字簽名必須保證以下三點:
- *接收方能夠核實發送方對報文的數字簽名;*
- *發送方事后不能抵賴對報文的數字簽名;*
- *任何人包括接收方都不能偽造對報文的簽名。*
現在已有多種實現數字簽名的方法。但采用公鑰算法要比采用對稱密鑰算法更容易實現。具體流程如下:

我們知道公鑰密碼算法的計算代價非常大,對整個報文進行數字簽名是一件非常耗時的事情。更有效的方法是僅對報文摘要進行數字簽名。
上述過程僅對報文進行了簽名,對報文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 /// https://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 /// https://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 }
代碼示例:https://github.com/Dwayne112401/encryption
相關內容:計算機網絡安全 —— 對稱加密算法 DES (一)、計算機網絡安全 —— 報文摘要算法 ME5 (三)、計算機網絡安全 —— 實體鑒別與生成大隨機數(四)
