c#与JavaScript实现对用户名、密码进行RSA非对称加密

原文:c#与JavaScript实现对用户名、密码进行RSA非对称加密

博主最近手上这个项目呢(就是有上百个万恶的复杂excel需要解析的那个项目,参见博客:http://www.cnblogs.com/csqb-511612371/p/4885930.html),由于是一个内网项目,安全性要求很低,不需要做什么报文加密。

但是总觉得用户名密码都是明文传输,略微有点坑甲方...

想了想,那就做个RSA加密,把用户名、密码做密文传输吧...至于为什么是RSA,因为也想趁机学习一下,DES、MD5什么的以前都做过了,不想又复制粘贴敷衍了事,怎么说领导还给了3天时间呢...

咱可是有原则的程序员。

 

首先要感谢博客园一些前辈们相关的一些文章,让博主一个只知道RSA基本概念的人在很短的时间内就成功实现了JS进行加密,C#进行解密的一个过程。

大概看了10来篇文章,感觉差不多了才开始写的自己的代码...
很难再具体回忆到从哪一篇文章获益最大,只能在此统一表示感谢!

 

写代码之前大概整理出一个整体流程:

0.后台实现两个基础方法:

(1)CreateRsaKeyPair()方法,产生一对RSA私钥公钥,并配以唯一的键值key

(2)DecryptRSA()方法,对密文进行RSA解密

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#解密的基本功能。

对安全性这一块一直没什么研究,这次的项目又不要求...  

一直想学习学习大神们对于整个安全性做的操作,在博客园找了很久也没找到特别详细通俗易懂的....

在此,也恳请各位大神能分享一下自己这方面的经验,感激不尽。

 

原创文章,代码都是从自己项目里贴出来的。转载请注明出处哦,亲~~~

时间: 2024-10-19 05:42:06

c#与JavaScript实现对用户名、密码进行RSA非对称加密的相关文章

javascript用户登录用户名密码验证

管理帐号: 密码:   验证码:    

JQuery记住用户名密码实现下次自动登录功能

  这篇文章主要介绍了JQuery记住用户名密码实现下次自动登录功能,本文直接给出实现代码,需要的朋友可以参考下 Jquery将用户名密码存储到cookie中 需要导入jquery.js和jquery.cookie.js ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <html> <head> &l

破解路由器用户名密码争夺局域网控制权

要是有个好网管,能对网络做出合理规化还好,但如果出租屋房东舍不得花钱买好路由器,自已又不会管网络,那就麻烦了,任由这个出租屋的网络乱成一团.最后就只能自已出马解决问题.你想问怎么解决?很简单,破解路由器的管理员用户名密码,拿到路由器的管理权限,把那些不听话的全踢出去.或是做限速限连接数限制.再者,对局域网内的所有电脑MAC地址与路由器进行双向绑定,也能避免ARP类型的内网攻击.498)this.w idth=498;' onmousewheel = 'javascript:return big(

javascript字典探测用户名工具_javascript技巧

<html> <head> <style> body         { font-size: 10pt; background-color:#D4D0C8 } td           { font-size: 9pt } </style> </head> <script language="javascript"> /* 程序标题:javascript字典探测用户名工具 发布时间:2006年8月 文章作者:翟振

想实现网页自动填写用户名密码WebBrowser的问题

问题描述 我想实现自动填用户名密码,并且提交的程序,以前再其他网页上做过,而且成功了.但是遇到另一个网页就不好使了,得到的vDoc.All.length=0,vDoc.all为空我的程序代码如下:DimvDoc,vTagDimiAsIntegervDoc=WebBrowser1.DocumentFori=0TovDoc.All.length-1'检测所有标签IfUCase(vDoc.All(i).tagName)="INPUT"Then'找到input标签vTag=vDoc.All(i

密码找回-获取ie自动保存用户名密码

问题描述 获取ie自动保存用户名密码 IPStore接口获取IE自动保存的用户名密码(最后一句怎么修改): //动态加载pstorec.dll,得到PStoreCreateInstance函数地址 typedef HRESULT (WINAPI tPStoreCreateInstance)(IPStore * DWORD DWORD DWORD);HMODULE hpsDLL; hpsDLL = LoadLibrary(""pstorec.dll""); tPSto

Win8系统网页无法保存用户名密码怎么办?

  Win8系统网页无法保存用户名密码怎么办? 1.检查IE是否设置了退出时删除浏览器历史记录的选项 打开IE浏览器,点击工具,选择Internet选项,查看"常规"页面是否勾选了"退出时删除浏览器历史记录",如果勾选了就取消,点击"设置"按钮,将"检查储存的页面的较新版本"设置为"自动",然后点击确定. 2.在缓存和数据库项中,勾选"允许使用网站缓存和数据库". www.xitongh

一段VB.NET代码,生成邮件,发送邮件,支持SMTP验证用户名密码.

smtp|发送邮件 可以生成邮件,可以发送邮件,稍做修改就可以写成一个com组件,在ASP里调用.以后我会整理成一个完整的. -------------------------------------------------------------------------------- '-------------------------------------------------'生成基本邮件格式(包括附件),发送邮件到SMTP服务器,'只能发送到发件人SMTP服务器(需验证),直接投递功

WCF的用户名密码认证

以前我们用WebService做分布式系统的时候,认证是个麻烦的问题,通常的做法是继承一个SoapHeader,把用户名和密码放到里面,每调用一个方法都要把用户名和密码传递给服务器端来验证 ,效率相当低,代码编写相当的麻烦,而且还不安全! WCF支持多种认证技术,例如Windowns认证.X509证书.Issued Tokens.用户名密码认证等,在跨Windows域分布的系统中,用户名密码认证还是比较常用的,要实现用户名密码认证,就必须需要X509证书,为什么呢?因为我们需要X509证书这种非