zoukankan      html  css  js  c++  java
  • 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#解密的基本功能。

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

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

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

     

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

  • 相关阅读:
    第二阶段站立会议第十天
    第二阶段站立会议第九天
    第二阶段站立会议第八天
    第二阶段站立会议第七天
    第二阶段站立会议第六天
    第二阶段站立会议第五天
    第二阶段站立会议第四天
    第二冲刺阶段个人进度10
    第二冲刺阶段个人进度09
    第二冲刺阶段个人进度08
  • 原文地址:https://www.cnblogs.com/csqb-511612371/p/4898437.html
Copyright © 2011-2022 走看看