博主最近手上这个项目呢(就是有上百个万恶的复杂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#对应的两个基础方法

          /// <summary>
/// 产生一组RSA公钥、私钥
/// </summary>
/// <returns></returns>
public static Dictionary<string, string> CreateRsaKeyPair()
{
var keyPair = new Dictionary<string, string>();
var rsaProvider = new RSACryptoServiceProvider();
RSAParameters parameter = rsaProvider.ExportParameters(true);
keyPair.Add("PUBLIC", BytesToHexString(parameter.Exponent) + "," + BytesToHexString(parameter.Modulus));
keyPair.Add("PRIVATE", rsaProvider.ToXmlString(true));
return keyPair;
} /// <summary>
/// RSA解密字符串
/// </summary>
/// <param name="encryptData">密文</param>
/// <param name="privateKey">私钥</param>
/// <returns>明文</returns>
public static string DecryptRSA(string encryptData, string privateKey)
{
string decryptData = "";
try
{
var provider = new RSACryptoServiceProvider();
provider.FromXmlString(privateKey); byte[] result = provider.Decrypt(HexStringToBytes(encryptData), false);
ASCIIEncoding enc = new ASCIIEncoding();
decryptData = enc.GetString(result);
}
catch (Exception e)
{
throw new Exception("RSA解密出错!", e);
}
return decryptData;
} private static string BytesToHexString(byte[] input)
{
StringBuilder hexString = new StringBuilder(); for (int i = ; i < input.Length; i++)
{
hexString.Append(String.Format("{0:X2}", input[i]));
}
return hexString.ToString();
} public static byte[] HexStringToBytes(string hex)
{
if (hex.Length == )
{
return new byte[] { };
} if (hex.Length % == )
{
hex = "" + hex;
} byte[] result = new byte[hex.Length / ]; for (int i = ; i < hex.Length / ; i++)
{
result[i] = byte.Parse(hex.Substring( * i, ), System.Globalization.NumberStyles.AllowHexSpecifier);
} return result;
}

注:

两个私有方法是进行16进制的转换,因为js前端rsa加密时要求的参数需要是16进制字符串。

其实博主认为比较好的方法是:后台不做转换,直接提供与接收普通字符串,由客户端按自身需要自己做类型转换。

博主这儿客户端就是一个web网站,正好后台以前又有这么两个转换方法,故在后台做了16进制转换。

下面贴出不做转换产生公钥私钥的代码(替换第10/11行代码):

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

我们还需要一个独立的获取RSA公钥的接口:

         /// <summary>
/// 获取RSA公钥
/// </summary>
/// <returns></returns>
[Route("api/UC/GetRsaPublicKey")]
[HttpGet]
[Anonymous]
public GetRsaPublicKeyResult GetRsaPublicKey()
{
var rsaKeys = Security.CreateRsaKeyPair(); var key = Guid.NewGuid().ToString();
//添加RSA密钥到缓存
CacheDataManager.DataInsert(key, rsaKeys["PRIVATE"], DateTime.Now.AddMinutes()); return new GetRsaPublicKeyResult()
{
Code = ,
RsaPublicKey = rsaKeys["PUBLIC"],
Key= key
};
}

那么我们的登录接口就该做成这样:

         /// <summary>
/// 用户登录
/// RSA加密密码
/// </summary>
/// <returns></returns>
[Route("api/UC/Login")]
[HttpPost]
[Anonymous]
public LoginResult Login([FromBody] LoginModel loginModel)
{
var privateKey = CacheDataManager.GetPrivateKey(loginModel.key);
if (!string.IsNullOrEmpty(privateKey))
{
// 移除缓存
CacheDataManager.RemoveKey(privateKey); if (string.IsNullOrEmpty(loginModel.phoneNumber))
{
throw new UserDisplayException("请输入用户名!");
} if (string.IsNullOrEmpty(loginModel.password))
{
throw new UserDisplayException("请输入密码!");
} var password = Security.DecryptRSA(loginModel.password, privateKey);
loginModel.password = password; var result = accountInfoService.User_Login(loginModel.phoneNumber, loginModel.password, loginModel.userType); // 产生令牌
var token = CreateToken(result.UUID, loginModel.userType.ToString()); return new LoginResult()
{
Code = ,
UserPrefect = result.UserPrefect,
Token = token,
IMName = result.IMName,
IMPassword = result.IMPassword,
LetterIntentCount = result.LetterIntentCount
};
}
else
{
throw new Exception("非法密钥key值!");
}
}

注:

1.我们需要客户端回传key值,以确认该用户使用公钥对应密钥

2.对密文进行RSA解密

那么我们再来看看前端界面应该怎么做

1.我们需要三个文件:Barrett.js    BigInt.js    RSA.js

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

2.在引用jquery后添加对三个文件的引用

     <script src="Libs/jquery/jquery-1.8.3.js"></script>
<script src="Libs/jquery.cookie.js"></script>
<script src="Libs/BigInt.js"></script>
<script src="Libs/RSA.js"></script>
<script src="Libs/Barrett.js"></script>

3.写一个获取公钥的js方法到common.js文件(尽量将这个方法的调用放在用户不经意之间,在用户触发登录事件之前执行一次,避免等到用户点击登录的时候再调用造成停顿 )

         // 获取RSA公钥
var getPublicKey=function () {
if(getCookie("publicKey")==null){
$.ajax({
url: "/api/UC/GetRsaPublicKey",
type: "get",
contentType: "application/x-www-form-urlencoded; charset=utf-8",
async: false,
data: {},
dataType: "json",
success: function (data) {
if (data.Code == ) {
var publicKey = data.RsaPublicKey + "," + data.Key;
setCookie("publicKey", publicKey,8);// 此处存储时间应该小于后台缓存时间
return publicKey;
} else {
Config.Method.JudgeCode(data, );
}
}
});
}else{
return getCookie("publicKey");
}
}

4.写一个通用的js加密方法到common.js

         // RSA加密
var rsaEncrypt: function (pwd) {
var publicKey=getPublicKey();
setMaxDigits();
var rsaKey = new RSAKeyPair(publicKey.split(",")[], "", publicKey.split(",")[]);
var pwdRtn = encryptedString(rsaKey, pwd);
return pwdRtn+","+publicKey.split(",")[2];
},

5.来看看我们在登录按钮的js方法中具体调用:

            var userName = $(".rencaibao_login_regist .login .userName").val();
var pwd = $(".rencaibao_login_regist .login .password").val();
if (!userName.length) {
alert("用户名不能为空!");
return;
}
if (!pwd.length) {
alert("密码不能为空!");
return;
}
if (!Config.Datas.RegPhone.test(userName)) {
alert("请输入正确的手机号!");
return;
}
if (!Config.Datas.PasswordVerification.test(pwd)) {
alert("密码格式错误!");
return;
} var pwd1 = Config.Method.rsaEncrypt(pwd); $.post(Config.Api.UC.Login, { 'phoneNumber': userName, 'password': pwd1.split(",")[0], 'userType': "Enterprise", 'key': publicKey.split(",")[1] }, function (data) {
publicKey = "";
if (data.Code == ) {
Config.Method.SetCookies(data.Token, userName, data.UserPrefect, data.LetterIntentCount);
$(".right_yixianghan a .imgDiv").html(data.LetterIntentCount);
_login_registEvent();
Config.Method.InitLoginInfo();
} else { Config.Method.JudgeCode(data, );
}
});

OK,至此我们就简单的实现了用JS进行RSA加密,c#解密的基本功能。

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

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

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

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

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

  1. javascript版前端页面RSA非对称加密解密

    最近由于项目需要做一个url传参,并在页面显示参数内容的需求,这样就会遇到一个url地址可能会被假冒, 并传递非法内容显示在页面的尴尬情况 比如xxx.shtml?server=xxx是坏人& ...

  2. Jquery 实现 “下次自动登录” 记住用户名密码功能

    转载自:http://blog.csdn.net/aspnet_lyc/article/details/12030039?utm_source=tuicool&utm_medium=refer ...

  3. 验证码的设计与记住我存储用户名密码cookie的技术及单选按钮选择登录人身份的实现

    login.jsp页面 <head> <script type="text/javascript" src="js/captcha.js"&g ...

  4. Javascript登录页面“记住密码”实现

    JS记住密码实现效果:  JavaScript Code  1234567891011121314151617181920212223242526272829303132   <!DOCTYPE ...

  5. cookie记住用户名密码

    <script src="js/jquery.cookie.js" type="text/javascript"></script> $ ...

  6. Django用户名密码错误提示

    from django.shortcuts import render # Create your views here. from django.shortcuts import render fr ...

  7. 【WCF】使用“用户名/密码”验证的合理方法

    我不敢说俺的方法是最佳方案,反正这世界上很多东西都是变动的,正像老子所说的——“反(返)者,道之动”.以往看到有些文章中说,为每个客户端安装证书嫌麻烦,就直接采用把用户名和密码塞在SOAP头中发送,然 ...

  8. C# 用SoapUI调试WCF服务接口(WCF中包含用户名密码的验证)

    问题描述: 一般调试wcf程序可以直接建一个单元测试,直接调接口. 但是,这次,我还要测试在接口内的代码中看接收到的用户名密码是否正确,所以,单一的直接调用接口方法行不通, 然后就想办法通过soapU ...

  9. [No00008F]PLSQL自动登录,记住用户名密码&日常使用技巧

    配置启动时的登录用户名和密码 这是个有争议的功能,因为记住密码会给带来数据安全的问题. 但假如是开发用的库,密码甚至可以和用户名相同,每次输入密码实在没什么意义,可以考虑让PLSQL Develope ...

随机推荐

  1. 对C语言islower、isupper、isdigit函数的测试

    今天朋友问起了这三个函数,我就帮忙测试了下,测试后发现谭浩强第四版课本附录上上讲的不是很严谨. 我们先看下这三个函数介绍: 谭浩强第四版课本附录第396页上这样介绍: 函数名 函数原型 功能 返回值 ...

  2. EXCEL中多级分类汇总空白字段填充

    使用场景,多级分类汇总后,在汇总的字段中显示空白,这样对我们直接取值做表带来十分不更(假像有5000条记录,1000条汇总项) 相关技术,INDIRECT函数,单元格定位功能. 在数据区域外任意一个单 ...

  3. CentOS 6.2 Eclipse CDT 开发环境搭建

    一.安装中文语言支撑 我当初安装CentOS6.2时选择了中文语言支持,有iBus和拼音输入法,有gnome和kde 没有安装的可以参考:CentOS英文环境下使用中文输入法  安装中文语言支持和输入 ...

  4. zip函数-Python 3

    zip函数接受任意多个(包括0个和1个)序列作为参数,返回一个tuple列表. zip函数在获取数据后,生成字典(dict)时比较好用. for examples: # Code based on P ...

  5. ASP.NET MVC4入门到精通系列目录汇总

    序言 最近公司在招.NET程序员,我发现好多来公司面试的.NET程序员居然都没有 ASP.NET MVC项目经验,其中包括一些工作4.5年了,甚至8年10年的,许多人给我的感觉是:工作了4.5年,We ...

  6. 【项目管理】图解GitHub基本操作

    一.注册并登陆到github网站 1.1.打开github网站首页(https://github.com/) 1.2.注册一个自己的github账号 创建账户后再验证自己的邮箱,然后就可以登陆到git ...

  7. 【大前端之前后分离01】JS前端渲染VS服务器端渲染

    前言 之前看了一篇文章:@Charlie.Zheng Web系统开发构架再思考-前后端的完全分离,文中论述了为何要前后分离,站在前端的角度来看,是很有必要的:但是如何说服团队使用前端渲染方案却是一个现 ...

  8. 【转】推荐10款最热门jQuery UI框架

    推荐10款最热门jQuery UI框架 原创 在进行Web开发时,并非所有的库都适合你的项目,但你仍需要收藏一些Web UI设计相关的库或框架,以在你需要的时候,加快你的开发效率.本文为你推荐10款非 ...

  9. MYSQL离线安装

    由于MySQL的广泛应用,MySQL的安装也就成了大家经常会碰到的问题.并且由于不是所有机器都可连接外网,所以MySQL的离线安装显得比较重要.而本文旨在介绍CentOS6.6下离线安装MySQL. ...

  10. sql语句查询经纬度范围(转载,源链接失效)

    MySQL性能调优 – 使用更为快速的算法进行距离 最近遇到了一个问题,通过不断的尝试最终将某句原本占据近1秒的查询优化到了0.01秒,效率提高了100倍. 问题是这样的,有一张存放用户居住地点经纬度 ...