兼容javascript和C#的RSA加密解密算法,對web提交的數據進行加密傳輸


  Web應用中往往涉及到敏感的數據,由於HTTP協議以明文的形式與服務器進行交互,因此可以通過截獲請求的數據包進行分析來盜取有用的信息。雖然https可以對傳輸的數據進行加密,但是必須要申請證書(一般都是收費的),成本較高。那么問題來了,如果對web提交的敏感數據進行加密呢?web應用中,前端的數據處理和交互基本上都是靠javascript來完成,后台的邏輯處理可以C#(java)等進行處理。

  微軟的C#中雖然有RSA算法,但是格式和OpenSSL生成的公鑰/私鑰文件格式並不兼容。這個也給貫通前后台的RSA加密解密帶來了難度。為了兼容OpenSSL生成的公鑰/私鑰文件格式,貫通javascript和C#的RSA加密解密算法,必須對C#內置的方法進行再度封裝。

    下面以登錄為例,用戶在密碼框輸入密碼后,javascript發送ajax請求時,對密碼先進行rsa加密后再發送,服務器接收到加密后的密碼后,先對其進行解密, 然后再驗證登錄是否成功。

  1  為了進行RSA加密解密,首先需要用openssl生成一對公鑰和私鑰(沒有的先下載openssl):

   1) 打開openssl.exe文件,輸入 genrsa -out openssl_rsa_priv.pem 1024

此命令在openssl.exe同目錄下生成openssl_rsa_private_key.pem文件。

  2) 生成公鑰 rsa  -in openssl_rsa__private.pem -pubout -out openssl_rsa__public.pem

  以上命令會創建如下的文件:

這個文件可以用文本編輯器進行打開,查看內容。

-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0w036ClSD0LvxPROMun0u022R OJlZE6P3m+gjq3gpi4n7lo8jhTqMqgccDbVJqnIfMzWS9O3lnlQXWTxJ3B4XJ52F AcriY5brOXUVgBLx5QMHLLd1gtJnmG4i7r4ytgX7XVKRnojR6zca1YnS0lbGGDF1 CGllB1riNrdksSQP+wIDAQAB -----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQC0w036ClSD0LvxPROMun0u022ROJlZE6P3m+gjq3gpi4n7lo8j hTqMqgccDbVJqnIfMzWS9O3lnlQXWTxJ3B4XJ52FAcriY5brOXUVgBLx5QMHLLd1 gtJnmG4i7r4ytgX7XVKRnojR6zca1YnS0lbGGDF1CGllB1riNrdksSQP+wIDAQAB AoGAIOyl6lIxXKULZoBKbEqXfIz0GwxlGg1ywyn5mW2lAGQzKMken0ioBnD9xIVW rOlHyhkIvBCyuC0jgfE2Avn93MlB3j0WRuXMFlJpCBlEklMilO9Zgmwl+vTB3VZb 8VzdrEEEUBio7LWP/KvSo+IFlNjDTKgAczbLTwAmj4w6g0ECQQDm4yxPdxcU2ywZ
7PyjIMM9qnSah9KcrjU8gjEyHsUpgTjhw1cx7Peo+vRiHqxDy1yaSu1BlwRR52pC jKNnl0QhAkEAyGx3NxEIiLk2oXGGbIMZ4P6geC8gYu01BiRNWVf0Yi7+sCH68eUP oI+G5bJ8bvzXpvHjQi0s2OlRfct/qtPQmwJBALa+2DONbxdy4lUi3lO/esk0QVaO aoTY3gomggnJkQRo4zzOABXkGaIF/6gp3u9J5uG4rFFd1m19XP2Pk0ZK1AECQBYi
lJAKW4zuF7CA3z3AxOzqckKTwdnrJL4G6FwDsMPfONWvCw4IJE+xSk64BbIkTpTr hhPa9WcHba6c+P6e4h0CQQDWeGMMpkqPG/w4afNCGmvRnM8vNkGUAmDGvCsfkTID
ijpKl5SD55hPHsWE5rsv1TLUpkWtrFBcg61bHwMUP3cv -----END RSA PRIVATE KEY-----

2 用jsencrypt對密碼進行加密:

首先需要導入js包文件

<script src="dist/js/jsencrypt.js"></script>

 

 1 var encrypt = new JSEncrypt();  2 var pubkey = "-----BEGIN PUBLIC KEY----- \  3  MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAj0dPnBMf3Z4VT1B8Ee6bjKNs \  4  hlYj7xvGijAa8RCdmGR7mrtrExnk8mdUlwdcS05gc4SSFOyWJcYtKUHpWn8/pkS0 \  5  vgGOl9Bzn0Xt9hiqTb3pZAfykNrMDGZMgJgfD6KTnfzVUAOupvxjcGkcoj6/vV5I \  6  eMcx8mT/z3elfsDSjQIDAQAB \  7     -----END PUBLIC KEY-----";  8 encrypt.setPublicKey(pubkey);  9 var encrypted = encrypt.encrypt($('#txtpwd').val()); 10 //console.log(encrypted);
11 $.ajax({ 12     type: "POST", 13     url: "http://localhost:24830/services/rsa_pem.ashx", 14     data: { "pwd": encrypted }, 15     dataType: "Json", 16     error: function (xhr, status, error) { 17            // alert(error);
18             $("#txtInfo").text(' 請求服務器失敗!'); 19             $(that).text('登 錄'); 20             $(that).attr('disabled', false); 21  }, 22     success: function (json) { 23        
24         if (uid == "admin" && json.data=="000") { 25             window.location.href = "index.html"; 26  } 27         else { 28             $("#txtInfo").text(' 用戶名或者密碼錯誤!'); 29             $(that).text('登 錄'); 30             $(that).attr('disabled', false); 31  } 32  } 33 });

3 后台用C#進行解密

 1 using System;  2 using System.Collections.Generic;  3 using System.IO;  4 using System.Linq;  5 using System.Security.Cryptography;  6 using System.Text;  7 using System.Threading.Tasks;  8 
 9 namespace CMCloud.SaaS  10 {  11     public class RSACryptoService  12  {  13         private RSACryptoServiceProvider _privateKeyRsaProvider;  14         private RSACryptoServiceProvider _publicKeyRsaProvider;  15 
 16         /// <summary>
 17         /// RSA解密  18         /// </summary>
 19         /// <param name="cipherText"></param>
 20         /// <returns></returns>
 21         public string Decrypt(string cipherText)  22  {  23             if (_privateKeyRsaProvider == null)  24  {  25                 throw new Exception("_privateKeyRsaProvider is null");  26  }  27             return Decrypt2(cipherText);  28  }  29         /// <summary>
 30         /// RSA加密  31         /// </summary>
 32         /// <param name="text"></param>
 33         /// <returns></returns>
 34         public string Encrypt(string text)  35  {  36             if (_publicKeyRsaProvider == null)  37  {  38                 throw new Exception("_publicKeyRsaProvider is null");  39  }  40             return Encrypt2(text);  41             //return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(Encoding.UTF8.GetBytes(text), false));
 42 
 43  }  44         private string Encrypt2(string text)  45  {  46 
 47 
 48             Byte[] PlaintextData = Encoding.UTF8.GetBytes(text);  49             int MaxBlockSize = _publicKeyRsaProvider.KeySize / 8 - 11;//加密塊最大長度限制
 50 
 51             if (PlaintextData.Length <= MaxBlockSize)  52  {  53                 return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(PlaintextData, false));  54  }  55             else
 56  {  57                 using (MemoryStream PlaiStream = new MemoryStream(PlaintextData))  58                 using (MemoryStream CrypStream = new MemoryStream())  59  {  60                     Byte[] Buffer = new Byte[MaxBlockSize];  61                     int BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);  62 
 63                     while (BlockSize > 0)  64  {  65                         Byte[] ToEncrypt = new Byte[BlockSize];  66                         Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize);  67 
 68                         Byte[] Cryptograph = _publicKeyRsaProvider.Encrypt(ToEncrypt, false);  69                         CrypStream.Write(Cryptograph, 0, Cryptograph.Length);  70 
 71                         BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);  72  }  73 
 74                     return Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None);  75  }  76  }  77 
 78 
 79 
 80 
 81  }  82 
 83         private string Decrypt2(string ciphertext)  84  {  85 
 86 
 87             Byte[] CiphertextData = Convert.FromBase64String(ciphertext);  88             int MaxBlockSize = _privateKeyRsaProvider.KeySize / 8;    //解密塊最大長度限制
 89 
 90             if (CiphertextData.Length <= MaxBlockSize)  91                 return System.Text.Encoding.UTF8.GetString(_privateKeyRsaProvider.Decrypt(CiphertextData, false));  92 
 93             using (MemoryStream CrypStream = new MemoryStream(CiphertextData))  94             using (MemoryStream PlaiStream = new MemoryStream())  95  {  96                 Byte[] Buffer = new Byte[MaxBlockSize];  97                 int BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);  98 
 99                 while (BlockSize > 0) 100  { 101                     Byte[] ToDecrypt = new Byte[BlockSize]; 102                     Array.Copy(Buffer, 0, ToDecrypt, 0, BlockSize); 103 
104                     Byte[] Plaintext = _privateKeyRsaProvider.Decrypt(ToDecrypt, false); 105                     PlaiStream.Write(Plaintext, 0, Plaintext.Length); 106 
107                     BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize); 108  } 109 
110                 return System.Text.Encoding.UTF8.GetString(PlaiStream.ToArray()); 111  } 112  } 113         public RSACryptoService(string privateKey, string publicKey = null) 114  { 115             if (!string.IsNullOrEmpty(privateKey)) 116  { 117                 _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey); 118  } 119 
120             if (!string.IsNullOrEmpty(publicKey)) 121  { 122                 _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publicKey); 123  } 124  } 125 
126         
127 
128         private RSACryptoServiceProvider CreateRsaProviderFromPrivateKey(string privateKey) 129  { 130             var privateKeyBits = System.Convert.FromBase64String(privateKey); 131 
132             var RSA = new RSACryptoServiceProvider(); 133             var RSAparams = new RSAParameters(); 134 
135             using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits))) 136  { 137                 byte bt = 0; 138                 ushort twobytes = 0; 139                 twobytes = binr.ReadUInt16(); 140                 if (twobytes == 0x8130) 141  binr.ReadByte(); 142                 else if (twobytes == 0x8230) 143  binr.ReadInt16(); 144                 else
145                     throw new Exception("Unexpected value read binr.ReadUInt16()"); 146 
147                 twobytes = binr.ReadUInt16(); 148                 if (twobytes != 0x0102) 149                     throw new Exception("Unexpected version"); 150 
151                 bt = binr.ReadByte(); 152                 if (bt != 0x00) 153                     throw new Exception("Unexpected value read binr.ReadByte()"); 154 
155                 RSAparams.Modulus = binr.ReadBytes(GetIntegerSize(binr)); 156                 RSAparams.Exponent = binr.ReadBytes(GetIntegerSize(binr)); 157                 RSAparams.D = binr.ReadBytes(GetIntegerSize(binr)); 158                 RSAparams.P = binr.ReadBytes(GetIntegerSize(binr)); 159                 RSAparams.Q = binr.ReadBytes(GetIntegerSize(binr)); 160                 RSAparams.DP = binr.ReadBytes(GetIntegerSize(binr)); 161                 RSAparams.DQ = binr.ReadBytes(GetIntegerSize(binr)); 162                 RSAparams.InverseQ = binr.ReadBytes(GetIntegerSize(binr)); 163  } 164 
165  RSA.ImportParameters(RSAparams); 166             return RSA; 167  } 168 
169         private int GetIntegerSize(BinaryReader binr) 170  { 171             byte bt = 0; 172             byte lowbyte = 0x00; 173             byte highbyte = 0x00; 174             int count = 0; 175             bt = binr.ReadByte(); 176             if (bt != 0x02) 177                 return 0; 178             bt = binr.ReadByte(); 179 
180             if (bt == 0x81) 181                 count = binr.ReadByte(); 182             else
183                 if (bt == 0x82) 184  { 185                 highbyte = binr.ReadByte(); 186                 lowbyte = binr.ReadByte(); 187                 byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; 188                 count = BitConverter.ToInt32(modint, 0); 189  } 190             else
191  { 192                 count = bt; 193  } 194 
195             while (binr.ReadByte() == 0x00) 196  { 197                 count -= 1; 198  } 199             binr.BaseStream.Seek(-1, SeekOrigin.Current); 200             return count; 201  } 202 
203         private RSACryptoServiceProvider CreateRsaProviderFromPublicKey(string publicKeyString) 204  { 205             // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
206             byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; 207             byte[] x509key; 208             byte[] seq = new byte[15]; 209             int x509size; 210 
211             x509key = Convert.FromBase64String(publicKeyString); 212             x509size = x509key.Length; 213 
214             // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------
215             using (MemoryStream mem = new MemoryStream(x509key)) 216  { 217                 using (BinaryReader binr = new BinaryReader(mem))  //wrap Memory Stream with BinaryReader for easy reading
218  { 219                     byte bt = 0; 220                     ushort twobytes = 0; 221 
222                     twobytes = binr.ReadUInt16(); 223                     if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
224                         binr.ReadByte();    //advance 1 byte
225                     else if (twobytes == 0x8230) 226                         binr.ReadInt16();   //advance 2 bytes
227                     else
228                         return null; 229 
230                     seq = binr.ReadBytes(15);       //read the Sequence OID
231                     if (!CompareBytearrays(seq, SeqOID))    //make sure Sequence for OID is correct
232                         return null; 233 
234                     twobytes = binr.ReadUInt16(); 235                     if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
236                         binr.ReadByte();    //advance 1 byte
237                     else if (twobytes == 0x8203) 238                         binr.ReadInt16();   //advance 2 bytes
239                     else
240                         return null; 241 
242                     bt = binr.ReadByte(); 243                     if (bt != 0x00)     //expect null byte next
244                         return null; 245 
246                     twobytes = binr.ReadUInt16(); 247                     if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
248                         binr.ReadByte();    //advance 1 byte
249                     else if (twobytes == 0x8230) 250                         binr.ReadInt16();   //advance 2 bytes
251                     else
252                         return null; 253 
254                     twobytes = binr.ReadUInt16(); 255                     byte lowbyte = 0x00; 256                     byte highbyte = 0x00; 257 
258                     if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
259                         lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus
260                     else if (twobytes == 0x8202) 261  { 262                         highbyte = binr.ReadByte(); //advance 2 bytes
263                         lowbyte = binr.ReadByte(); 264  } 265                     else
266                         return null; 267                     byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order
268                     int modsize = BitConverter.ToInt32(modint, 0); 269 
270                     int firstbyte = binr.PeekChar(); 271                     if (firstbyte == 0x00) 272                     {   //if first byte (highest order) of modulus is zero, don't include it
273                         binr.ReadByte();    //skip this null byte
274                         modsize -= 1;   //reduce modulus buffer size by 1
275  } 276 
277                     byte[] modulus = binr.ReadBytes(modsize);   //read the modulus bytes
278 
279                     if (binr.ReadByte() != 0x02)            //expect an Integer for the exponent data
280                         return null; 281                     int expbytes = (int)binr.ReadByte();        // should only need one byte for actual exponent data (for all useful values)
282                     byte[] exponent = binr.ReadBytes(expbytes); 283 
284                     // ------- create RSACryptoServiceProvider instance and initialize with public key -----
285                     RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); 286                     RSAParameters RSAKeyInfo = new RSAParameters(); 287                     RSAKeyInfo.Modulus = modulus; 288                     RSAKeyInfo.Exponent = exponent; 289  RSA.ImportParameters(RSAKeyInfo); 290 
291                     return RSA; 292  } 293 
294  } 295  } 296 
297         private bool CompareBytearrays(byte[] a, byte[] b) 298  { 299             if (a.Length != b.Length) 300                 return false; 301             int i = 0; 302             foreach (byte c in a) 303  { 304                 if (c != b[i]) 305                     return false; 306                 i++; 307  } 308             return true; 309  } 310  } 311 }

       雖然將公鑰暴露在js文件中,但是如果需要解密得到明文,必須需要私鑰(這個存儲在后台,不容易獲取)。

    調試運行,可以看到獲取的密碼是加密后的數據,然后在后台可以進行解密獲取到明文。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM