计算机网络安全 —— 非对称加密算法 RSA 和数字签名(二)
一、非对称加密算法基本概念
在对称密钥系统中,两个参与者要共享同一个秘密密钥。但怎样才能做到这一点呢?一种是事先约定,另一种是用信使来传送。在高度自动化的大型计算机网络中,用信使来传送密钥显然是不合适的。如果事先约定密钥,就会给密钥的管理和更换都带来了极大的不便。当然我们可以使用复杂的密钥分发中心(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 /// //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 (一)