c#與JavaScript實現對用戶名、密碼進行RSA非對稱加密

博主最近手上這個項目呢(就是有上百個萬惡的複雜excel須要解析的那個項目,參見博客:http://www.cnblogs.com/csqb-511612371/p/4885930.html),因爲是一個內網項目,安全性要求很低,不須要作什麼報文加密。html

可是總以爲用戶名密碼都是明文傳輸,略微有點坑甲方...前端

想了想,那就作個RSA加密,把用戶名、密碼作密文傳輸吧...至於爲何是RSA,由於也想趁機學習一下,DES、MD5什麼的之前都作過了,不想又複製粘貼敷衍了事,怎麼說領導還給了3天時間呢...jquery

咱但是有原則的程序員。git

 

首先要感謝博客園一些前輩們相關的一些文章,讓博主一個只知道RSA基本概念的人在很短的時間內就成功實現了JS進行加密,C#進行解密的一個過程。程序員

大概看了10來篇文章,感受差很少了纔開始寫的本身的代碼...
很難再具體回憶到從哪一篇文章獲益最大,只能在此統一表示感謝!web

 

寫代碼以前大概整理出一個總體流程:ajax

0.後臺實現兩個基礎方法:json

(1)CreateRsaKeyPair()方法,產生一對RSA私鑰公鑰,並配以惟一的鍵值keyc#

(2)DecryptRSA()方法,對密文進行RSA解密api

1.用戶訪問客戶端,客戶端向服務器請求獲取一個RSA公鑰以及鍵值key,存儲在本地

2.用戶在本地公鑰失效前發起登陸請求,則使用已有公鑰對用戶密碼進行加密;若已過時則執行1後再加密

3.客戶端將密文與key一塊兒傳回後臺

4.後臺經過key找到緩存裏面的私鑰,對密文進行解密

 

OK,咱們先來看看c#對應的兩個基礎方法

 1          /// <summary>
 2         /// 產生一組RSA公鑰、私鑰
 3         /// </summary>
 4         /// <returns></returns>
 5         public static Dictionary<string, string> CreateRsaKeyPair()
 6         {
 7             var keyPair = new Dictionary<string, string>();
 8             var rsaProvider = new RSACryptoServiceProvider(1024);
 9             RSAParameters parameter = rsaProvider.ExportParameters(true);
10             keyPair.Add("PUBLIC", BytesToHexString(parameter.Exponent) + "," + BytesToHexString(parameter.Modulus));
11             keyPair.Add("PRIVATE", rsaProvider.ToXmlString(true));
12             return keyPair;
13         }
14 
15         /// <summary>
16         /// RSA解密字符串
17         /// </summary>
18         /// <param name="encryptData">密文</param>
19         /// <param name="privateKey">私鑰</param>
20         /// <returns>明文</returns>
21         public static string DecryptRSA(string encryptData, string privateKey)
22         {
23             string decryptData = "";
24             try
25             {
26                 var provider = new RSACryptoServiceProvider();
27                 provider.FromXmlString(privateKey);
28 
29                 byte[] result = provider.Decrypt(HexStringToBytes(encryptData), false);
30                 ASCIIEncoding enc = new ASCIIEncoding();
31                 decryptData = enc.GetString(result);
32             }
33             catch (Exception e)
34             {
35                 throw new Exception("RSA解密出錯!", e);
36             }
37             return decryptData;
38         }
39 
40         private static string BytesToHexString(byte[] input)
41         {
42             StringBuilder hexString = new StringBuilder(64);
43 
44             for (int i = 0; i < input.Length; i++)
45             {
46                 hexString.Append(String.Format("{0:X2}", input[i]));
47             }
48             return hexString.ToString();
49         }
50 
51         public static byte[] HexStringToBytes(string hex)
52         {
53             if (hex.Length == 0)
54             {
55                 return new byte[] { 0 };
56             }
57 
58             if (hex.Length % 2 == 1)
59             {
60                 hex = "0" + hex;
61             }
62 
63             byte[] result = new byte[hex.Length / 2];
64 
65             for (int i = 0; i < hex.Length / 2; i++)
66             {
67                 result[i] = byte.Parse(hex.Substring(2 * i, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
68             }
69 
70             return result;
71         }

注:

兩個私有方法是進行16進制的轉換,由於js前端rsa加密時要求的參數須要是16進制字符串。

其實博主認爲比較好的方法是:後臺不作轉換,直接提供與接收普通字符串,由客戶端按自身須要本身作類型轉換。

博主這兒客戶端就是一個web網站,正好後臺之前又有這麼兩個轉換方法,故在後臺作了16進制轉換。

下面貼出不作轉換產生公鑰私鑰的代碼(替換第10/11行代碼):

1 keyPair.Add("PUBLIC", rsaProvider.ToXmlString(false));
2 keyPair.Add("PRIVATE", rsaProvider.ToXmlString(true));

 

咱們還須要一個獨立的獲取RSA公鑰的接口:

 1         /// <summary>
 2         /// 獲取RSA公鑰
 3         /// </summary>
 4         /// <returns></returns>
 5         [Route("api/UC/GetRsaPublicKey")]
 6         [HttpGet]
 7         [Anonymous]
 8         public GetRsaPublicKeyResult GetRsaPublicKey()
 9         {
10             var rsaKeys = Security.CreateRsaKeyPair();
11 
12              var key = Guid.NewGuid().ToString();
13             //添加RSA密鑰到緩存
14             CacheDataManager.DataInsert(key, rsaKeys["PRIVATE"], DateTime.Now.AddMinutes(10));
15 
16             return new GetRsaPublicKeyResult()
17             {
18                 Code = 0,
19                 RsaPublicKey = rsaKeys["PUBLIC"],
20                 Key= key
21             };
22         }

 

那麼咱們的登陸接口就該作成這樣:

 1         /// <summary>
 2         /// 用戶登陸
 3         /// RSA加密密碼
 4         /// </summary>
 5         /// <returns></returns>
 6         [Route("api/UC/Login")]
 7         [HttpPost]
 8         [Anonymous]
 9         public LoginResult Login([FromBody] LoginModel loginModel)
10         {
11             var privateKey = CacheDataManager.GetPrivateKey(loginModel.key);
12             if (!string.IsNullOrEmpty(privateKey))
13             {
14                 // 移除緩存
15                 CacheDataManager.RemoveKey(privateKey);
16 
17                 if (string.IsNullOrEmpty(loginModel.phoneNumber))
18                 {
19                     throw new UserDisplayException("請輸入用戶名!");
20                 }
21 
22                 if (string.IsNullOrEmpty(loginModel.password))
23                 {
24                     throw new UserDisplayException("請輸入密碼!");
25                 }
26 
27                 var password = Security.DecryptRSA(loginModel.password, privateKey);
28                 loginModel.password = password;
29 
30                 var result = accountInfoService.User_Login(loginModel.phoneNumber, loginModel.password, loginModel.userType);
31 
32                 // 產生令牌
33                 var token = CreateToken(result.UUID, loginModel.userType.ToString());
34 
35                 return new LoginResult()
36                 {
37                     Code = 0,
38                     UserPrefect = result.UserPrefect,
39                     Token = token,
40                     IMName = result.IMName,
41                     IMPassword = result.IMPassword,
42                     LetterIntentCount = result.LetterIntentCount
43                 };
44             }
45             else
46             {
47                 throw new Exception("非法密鑰key值!");
48             }
49         }

注:

1.咱們須要客戶端回傳key值,以確認該用戶使用公鑰對應密鑰

2.對密文進行RSA解密

 

那麼咱們再來看看前端界面應該怎麼作

1.咱們須要三個文件:Barrett.js    BigInt.js    RSA.js

下載地址:http://download.csdn.net/detail/cb511612371/9202207

2.在引用jquery後添加對三個文件的引用

1     <script src="Libs/jquery/jquery-1.8.3.js"></script>
2     <script src="Libs/jquery.cookie.js"></script>
3     <script src="Libs/BigInt.js"></script>
4     <script src="Libs/RSA.js"></script>
5     <script src="Libs/Barrett.js"></script>

3.寫一個獲取公鑰的js方法到common.js文件(儘可能將這個方法的調用放在用戶不經意之間,在用戶觸發登陸事件以前執行一次,避免等到用戶點擊登陸的時候再調用形成停頓 )

 1         // 獲取RSA公鑰
 2         var getPublicKey=function () {
 3             if(getCookie("publicKey")==null){
 4               $.ajax({
 5                 url: "/api/UC/GetRsaPublicKey",
 6                 type: "get",
 7                 contentType: "application/x-www-form-urlencoded; charset=utf-8",
 8                 async: false,
 9                 data: {},
10                 dataType: "json",
11                 success: function (data) {
12                     if (data.Code == 0) {
13                         var publicKey = data.RsaPublicKey + "," + data.Key;
setCookie("publicKey", publicKey,8);// 此處存儲時間應該小於後臺緩存時間
return publicKey;
14 } else { 15 Config.Method.JudgeCode(data, 1); 16 } 17 } 18 });
}else{
return getCookie("publicKey");
}
19 }

4.寫一個通用的js加密方法到common.js

1         // RSA加密
2         var rsaEncrypt: function (pwd) {
3             var publicKey=getPublicKey();
4             setMaxDigits(129);
5             var rsaKey = new RSAKeyPair(publicKey.split(",")[0], "", publicKey.split(",")[1]);
6             var pwdRtn = encryptedString(rsaKey, pwd);
7             return pwdRtn+","+publicKey.split(",")[2];
8         },

5.來看看咱們在登陸按鈕的js方法中具體調用:

 1            var userName = $(".rencaibao_login_regist .login .userName").val();
 2             var pwd = $(".rencaibao_login_regist .login .password").val();
 3             if (!userName.length) {
 4                 alert("用戶名不能爲空!");
 5                 return;
 6             }
 7             if (!pwd.length) {
 8                 alert("密碼不能爲空!");
 9                 return;
10             }
11             if (!Config.Datas.RegPhone.test(userName)) {
12                 alert("請輸入正確的手機號!");
13                 return;
14             }
15             if (!Config.Datas.PasswordVerification.test(pwd)) {
16                 alert("密碼格式錯誤!");
17                 return;
18             }
19             
20             var pwd1 = Config.Method.rsaEncrypt(pwd);
21 
22             $.post(Config.Api.UC.Login, { 'phoneNumber': userName, 'password': pwd1.split(",")[0], 'userType': "Enterprise", 'key': publicKey.split(",")[1] }, function (data) {
23                 publicKey = "";
24                 if (data.Code == 0) {
25                     Config.Method.SetCookies(data.Token, userName, data.UserPrefect, data.LetterIntentCount);
26                     $(".right_yixianghan a .imgDiv").html(data.LetterIntentCount);
27                     _login_registEvent();
28                     Config.Method.InitLoginInfo();
29                 } else {

33 Config.Method.JudgeCode(data, 0); 34 } 35 });

 

OK,至此咱們就簡單的實現了用JS進行RSA加密,c#解密的基本功能。

對安全性這一塊一直沒什麼研究,此次的項目又不要求...  

一直想學習學習大神們對於整個安全性作的操做,在博客園找了好久也沒找到特別詳細通俗易懂的....

在此,也懇請各位大神能分享一下本身這方面的經驗,感激涕零。

 

原創文章,代碼都是從本身項目裏貼出來的。轉載請註明出處哦,親~~~

相關文章
相關標籤/搜索