【转】 我的java web登录RSA加密

之前一直没关注过web应用登录密码加密的问题,这两天用appscan扫描应用,最严重的问题就是这个了,提示我明文发送密码。这个的确很不安全,以前也大概想过,但是没有具体研究过,都不了了之,这次借这个机会,终于搞定了这个问题。

首先,有不少帖子说在客户端用js对密码进行md5摘要,然后提交给登录处理的url。这种做法无非是自欺欺人,就算别人抓包抓不到你原始密码,用这个md5后的密码一样可以模拟登录系统,无非稍微安全了一点点,也就是直接通过登录页没法直接输入用户名密码来登录,但是人家的手段你知道有啥呢?用程序模拟登陆也不是什么太难的事情。

https当然也是个选择,但是对于一般应用来说,还需要生成密钥之类的,还需要拿去给那些认证机构签名,麻烦不说,银子是必须的。如果说让用户安装证书,应用系统还可以,网站就不太现实了,毕竟不是所有用户都有那么高的计算机操作水平,就算有,人家一用感觉这么麻烦,也不见得去操作。

这次专心搜索了1个小时,还是觉得非对称加密比较靠谱,有一些RSA加密的文章值得借鉴。这里向这些文章作者致敬,我参考可不只一篇文章,因为问题多多的。废话到此结束,说说我的处理方式吧。

加密解密的流程:

a)在login.jsp中,加入一段java代码,生成公钥和私钥,私钥对象保存在session中;公钥中,我把Exponent, modulus放到request的attribute中,并在页面上输出给js加密函数,用于密码加密。使用security.js的功能加密

var key = new RSAUtils.getKeyPair("${publicKeyExponent}", "", "${publicKeyModulus}");

       var reversedPwd = password.split("").reverse().join("");//js里面是反序的字符串,不知道为啥
       var encrypedPwd = RSAUtils.encryptedString(key,reversedPwd);

b)在点击提交按钮时,调用登陆js函数。这个函数是用ajax方式将用户名,密码提交给登陆处理url的。在提交之前,先利用a步骤中的公钥Exponent,和modulus,对密码进行加密,然后再发送给服务器端。

c)在登陆处理url中,(我是login.action),从session中取得私钥对象,对密码进行解密。随后的步骤都一样了,到库里去查询之类的,不细说了。

下面说说我处理的步骤和遇到的主要问题。

1.只能使用RSA这种非对称加密,才能让密码破解成为仅仅“理论上”的可能。所以决定使用这种加密方式。

2.寻找合适的客户端javascript加密代码。这个我是不太懂了,只能去找。最后找到了security.js。网上有些文章用的3个文件,BigInt.js,RSA.js还有个啥来着,Barrett.js这3个来实现,开始我用了。但是和服务端配合不了(我自己的问题),结果后来找到这个security.js,实际上是把这个3个js都封装到1个里面了,而且最后修改时间是2010年,比较新,就用这个吧。那3个js文件应该也是能用的。

3.在服务器端生成公钥和私钥,这个本来想对简单,代码可参考的很多。但是我遇到的问题不少,解密的时候总是出错。

问题一:在login.jsp中,公钥的Exponent,和modulus输出格式问题

开始总是什么:长度过大,必须以0开始之类的异常。我想到很可能是js加密和纯java加密那些地方不同导致的。后来发现,原来是我公钥的Exponent,和modulus输出直接用的toString()方法,实际上应该用toString(16),用16进制输出,因为在security.js中,那个

RSAUtils.getKeyPair(publicKeyExponent, "", ${publicKeyModulus);方法内部,明显是从16进制进行转换的。改完后应该是这样:

 String publicKeyExponent = publicKey.getPublicExponent().toString(16);//16进制
String publicKeyModulus = publicKey.getModulus().toString(16);//16进制
request.setAttribute("publicKeyExponent", publicKeyExponent);
request.setAttribute("publicKeyModulus", publicKeyModulus);

问题二:有个什么Padding之类的异常,是RSA算法中前面补齐的问题,原因时js和java默认的RSA算法不一致。

经过分析,用RSA其他的provider可以解决此问题,于是在生成密码对的代码中,使用了

KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());  

这个的补齐方式和js的就一致了。

问题三:provider的认证问题

刚用上,感觉能通过了,但是马上就是一个异常:jce cannot authenticate the provider bc。意思好理解,就是没经过认证。怎么让他通过呢,结果我在运行应用服务器的javahome\jre\lib\security\java.security文件中添加了如下代码:

security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider

11是序号,顺着它原来的加就可以了。
感觉上ok了,启动一下看看,还是那个问题。这个问题是我在jboss-eap-6.2上出现的。其他的应用服务器可能比较简单些(比如直接放到服务器的lib下)。

于是查jboss的资料,终于找到了,说是在jboss中,不能让这个provider的jar包在应用的lib下,需要使用全局的jar包。如果使用maven,那必须让这个包的scope是provided;(反正别让BouncyCastle这个jar包打入到war里面就可以)。需要在eap6.2下进行配置:

在jboss_home/modules下建立目录org\bouncycastle\main,在main目录中,放入bcprov-jdk16-1.46.jar,并加入module的配置文件module.xml,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.bouncycastle">
<resources>
<resource-root path="bcprov-jdk16-1.46.jar"/>
</resources>
<dependencies>
<module name="javax.api" slot="main" export="true"/>
</dependencies>
</module>

还需要在web应用的META-INF\MANIFEST.MF中加入Dependencies: org.bouncycastle

我是maven工程,需要配置pom:

<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>${version.war.plugin}</version>
<configuration>
<!-- Java EE 6 doesn't require web.xml, Maven needs to catch up! -->
<failOnMissingWebXml>true</failOnMissingWebXml>
<version>3.0</version>
<archive>
<manifestEntries>
<Dependencies>org.bouncycastle</Dependencies>
</manifestEntries>
</archive>
</configuration> </plugin>

主要是archive节点的配置,这样打包后MANIFEST.MF的内容就会变了

不能传附件可咋整呢?贴代码吧:

login.jsp

<script src="js/lib/security.js" type="text/javascript"></script>
<script type="text/javascript">
<%
HashMap<String, Object> map = RSAUtils.getKeys();
//生成公钥和私钥
RSAPublicKey publicKey = (RSAPublicKey) map.get("public");
RSAPrivateKey privateKey = (RSAPrivateKey) map.get("private"); session.setAttribute("privateKey", privateKey);//私钥保存在session中,用于解密 //公钥信息保存在页面,用于加密
String publicKeyExponent = publicKey.getPublicExponent().toString(16);
String publicKeyModulus = publicKey.getModulus().toString(16);
request.setAttribute("publicKeyExponent", publicKeyExponent);
request.setAttribute("publicKeyModulus", publicKeyModulus);
%>
function login() {
//登录
var username = $("#txtUsername").val();
var password = $("#txtPassword").val();
var randCode = $("#txtRandCode").val();
var rememberMeObj = document.getElementById("cbRememberMe");
var rem = rememberMeObj.checked ? "1" : ""; RSAUtils.setMaxDigits(200);
//setMaxDigits(256);
var key = new RSAUtils.getKeyPair("${publicKeyExponent}", "", "${publicKeyModulus}"); var encrypedPwd = RSAUtils.encryptedString(key,password);
$.post("login.action", { username: username, password: encrypedPwd, randcode: randCode, rememberme: rem }, function (jsonData) {
//请求完成
//如果为true,证明该用户已经下载过,显示已下载提示,否则直接下载
if (jsonData.success == true) {
//登录成功
window.location.href = 'index.jsp';
} else {
//提示错误信息divErrors
var ettText = "";
if (username == "") {
ettText="用户名不能为空";
}
else if (password == "") {
ettText="密码不能为空";
}
else if (randCode == "") {
ettText="验证码不能为空";
}
else {
ettText=jsonData.msg;
}
alert(ettText);
}
});
}</script>

login.action

RSAPrivateKey privateKey = (RSAPrivateKey)request.getSession().getAttribute("privateKey");
String descrypedPwd = RSAUtils.decryptByPrivateKey(password, privateKey); //解密后的密码,password是提交过来的密码

RSAUtils.java

package com.myapp.util;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.HashMap;
import javax.crypto.Cipher; public class RSAUtils {
/**
* 生成公钥和私钥
* @throws NoSuchAlgorithmException
*
*/
public static HashMap<String, Object> getKeys() throws NoSuchAlgorithmException{
HashMap<String, Object> map = new HashMap<String, Object>();
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
map.put("public", publicKey);
map.put("private", privateKey);
return map;
}
/**
* 使用模和指数生成RSA公钥
*
*
* @param modulus
* 模
* @param exponent
* 指数
* @return
*/
public static RSAPublicKey getPublicKey(String modulus, String exponent) {
try {
BigInteger b1 = new BigInteger(modulus);
BigInteger b2 = new BigInteger(exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 使用模和指数生成RSA私钥 * /None/NoPadding】
*
* @param modulus
* 模
* @param exponent
* 指数
* @return
*/
public static RSAPrivateKey getPrivateKey(String modulus, String exponent) {
try {
BigInteger b1 = new BigInteger(modulus);
BigInteger b2 = new BigInteger(exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 公钥加密
*
* @param data
* @param publicKey
* @return
* @throws Exception
*/
public static String encryptByPublicKey(String data, RSAPublicKey publicKey)
throws Exception {
Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 模长
int key_len = publicKey.getModulus().bitLength() / 8;
// 加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi = "";
//如果明文长度大于模长-11则要分组加密
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
} /**
* 私钥解密
*
* @param data
* @param privateKey
* @return
* @throws Exception
*/
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey)
throws Exception {
Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
//模长
int key_len = privateKey.getModulus().bitLength() / 8;
byte[] bytes = data.getBytes();
byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
//System.err.println(bcd.length);
//如果密文长度大于模长则要分组解密
String ming = "";
byte[][] arrays = splitArray(bcd, key_len);
for(byte[] arr : arrays){
ming += new String(cipher.doFinal(arr));
}
return ming;
}
/**
* ASCII码转BCD码
*
*/
public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {
byte[] bcd = new byte[asc_len / 2];
int j = 0;
for (int i = 0; i < (asc_len + 1) / 2; i++) {
bcd[i] = asc_to_bcd(ascii[j++]);
bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4));
}
return bcd;
}
public static byte asc_to_bcd(byte asc) {
byte bcd; if ((asc >= '0') && (asc <= '9'))
bcd = (byte) (asc - '0');
else if ((asc >= 'A') && (asc <= 'F'))
bcd = (byte) (asc - 'A' + 10);
else if ((asc >= 'a') && (asc <= 'f'))
bcd = (byte) (asc - 'a' + 10);
else
bcd = (byte) (asc - 48);
return bcd;
}
/**
* BCD转字符串
*/
public static String bcd2Str(byte[] bytes) {
char temp[] = new char[bytes.length * 2], val; for (int i = 0; i < bytes.length; i++) {
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); val = (char) (bytes[i] & 0x0f);
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
}
return new String(temp);
}
/**
* 拆分字符串
*/
public static String[] splitString(String string, int len) {
int x = string.length() / len;
int y = string.length() % len;
int z = 0;
if (y != 0) {
z = 1;
}
String[] strings = new String[x + z];
String str = "";
for (int i=0; i<x+z; i++) {
if (i==x+z-1 && y!=0) {
str = string.substring(i*len, i*len+y);
}else{
str = string.substring(i*len, i*len+len);
}
strings[i] = str;
}
return strings;
}
/**
*拆分数组
*/
public static byte[][] splitArray(byte[] data,int len){
int x = data.length / len;
int y = data.length % len;
int z = 0;
if(y!=0){
z = 1;
}
byte[][] arrays = new byte[x+z][];
byte[] arr;
for(int i=0; i<x+z; i++){
arr = new byte[len];
if(i==x+z-1 && y!=0){
System.arraycopy(data, i*len, arr, 0, y);
}else{
System.arraycopy(data, i*len, arr, 0, len);
}
arrays[i] = arr;
}
return arrays;
}
public static void main(String[] args) throws Exception{
HashMap<String, Object> map = getKeys();
//生成公钥和私钥
RSAPublicKey publicKey = (RSAPublicKey) map.get("public");
RSAPrivateKey privateKey = (RSAPrivateKey) map.get("private"); //模
String modulus = publicKey.getModulus().toString();
System.out.println("pubkey modulus="+modulus);
//公钥指数
String public_exponent = publicKey.getPublicExponent().toString();
System.out.println("pubkey exponent="+public_exponent);
//私钥指数
String private_exponent = privateKey.getPrivateExponent().toString();
System.out.println("private exponent="+private_exponent);
//明文
String ming = "111";
//使用模和指数生成公钥和私钥
RSAPublicKey pubKey = RSAUtils.getPublicKey(modulus, public_exponent);
RSAPrivateKey priKey = RSAUtils.getPrivateKey(modulus, private_exponent);
//加密后的密文
String mi = RSAUtils.encryptByPublicKey(ming, pubKey);
System.err.println("mi="+mi);
//解密后的明文
String ming2 = RSAUtils.decryptByPrivateKey(mi, priKey);
System.err.println("ming2="+ming2);
}
}

最后,那个js的RSA实现我是在如下链接下载的:

http://www.oschina.net/code/snippet_1611_4789

【转】 我的java web登录RSA加密的更多相关文章

  1. IOS, Android, Java Web Rest : RSA 加密和解密问题

    IOS, Android, Java Web Rest :  RSA 加密和解密问题 一对公钥私钥可以使用 OpenSSL创建, 通常 1024位长度够了. 注意: 1. 公钥私钥是BASE64编码的 ...

  2. Java Web项目RSA加密

    最近做的一个项目,服务器为Java,采用SSH框架,客户端为Android和IOS.当用户登录时,从客户端向服务器提交用户名和密码.这就存在一个问题,如果数据包在网络上被其他人截取了,密码就有可能泄露 ...

  3. [更新]一份包含: 采用RSA JWT(Json Web Token, RSA加密)的OAUTH2.0,HTTP BASIC,本地数据库验证,Windows域验证,单点登录的Spring Security配置文件

    没有任何注释,表怪我(¬_¬) 更新: 2016.05.29: 将AuthorizationServer和ResourceServer分开配置 2016.05.29: Token获取采用Http Ba ...

  4. web登录密码加密

    文章:如何实现登录页面密码加密 文章:用RSA加密实现Web登录密码加密传输 文章:web登录用户名密码加密 知乎文章:Web前端密码加密是否有意义? 文章:记录一次黑客模拟攻击 成功拿到淘宝账号和密 ...

  5. C# Java间进行RSA加密解密交互

    原文:C# Java间进行RSA加密解密交互 这里,讲一下RSA算法加解密在C#和Java之间交互的问题,这两天纠结了很久,也看了很多其他人写的文章,颇受裨益,但没能解决我的实际问题,终于,还是被我捣 ...

  6. C# Java间进行RSA加密解密交互(二)

    原文:C# Java间进行RSA加密解密交互(二) 接着前面一篇文章C# Java间进行RSA加密解密交互,继续探讨这个问题. 在前面,虽然已经实现了C# Java间进行RSA加密解密交互,但是还是与 ...

  7. C# Java间进行RSA加密解密交互(三)

    原文:C# Java间进行RSA加密解密交互(三) 接着前面一篇C# Java间进行RSA加密解密交互(二)说吧,在上篇中为了实现 /** * RSA加密 * @param text--待加密的明文 ...

  8. java web登录界面 源代码

    大概流程: 在java web项目中 导入sqljdbc4的包 在java Resources中完成java代码 在webContent 下建立一个存放自己写jsp的文件夹 将sqljdbc4和jst ...

  9. 用RSA加密实现Web登录密码加密传输

    通常我们做一个Web应用程序的时候都需要登录,登录就要输入用户名和登录密码,并且,用户名和登录密码都是明文传输的,这样就有可能在中途被别人拦截,尤其是在网吧等场合. 这里顺带一个小插曲,我以前有家公司 ...

随机推荐

  1. 转js调优

    随着网络的发展,网速和机器速度的提高,越来越多的网站用到了丰富客户端技术.而现在Ajax则是最为流行的一种方式.javascript是一种解释型 语言,所以能无法达到和C/Java之类的水平,限制了它 ...

  2. 【参考】JDBC执行存储过程的四种情况

    [1].只有输入IN参数,没有输出OUT参数 [2].既有输入IN参数,也有输出OUT参数,输出是简单值(非列表) [3].既有输入IN参数,也有输出OUT参数,输出是列表 [4].输入输出参数是同一 ...

  3. HDU 1312 Red and Black【DFS】

    搜索虐我千万遍@_@-----一道搜索的水题,WA了好多好多次@_@发现是n,m搞反了-_- 题意-- 给出m行 n列的矩形,其中从@出发,不能跳到#,只能跳到'.'问最多能够跳到多少块'.' 直接搜 ...

  4. Python3基础笔记---序列化

    1.json模块   菜鸟教程 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写. import json json.dumps json ...

  5. FCC编程题之中级算法篇(中)

    介绍 接着上次的中级算法题 目录 1. Missing letters 2. Boo who 3. Sorted Union 4. Convert HTML Entities 5. Spinal Ta ...

  6. 【xsy2440】【GDOI2016】疯狂动物城

    感受一下这恐怖的题目长度~~~ 其实题意很裸,但是作为GDOI的一道防AK题,自然有他优秀的地方. 简化题意:给出一棵树,要求支持三个操作: 1.修改点值 2.询问点$x$到$y$之间的一些东东 3. ...

  7. 树上倍增求LCA

    大概思想就是,节点$i$的第$2^{j}$个父节点是他第$2^{j-1}$个父亲的第$2^{j-1}$个父亲 然后可以$O(nlogn)$时间内解决…… 没了? //fa[i][j]表示i的第2^j个 ...

  8. 入门python:《Python编程快速上手让繁琐工作自动化》中英文PDF+代码

    入门推荐学习<python编程快速上手>前6章是python的基础知识,通俗易懂地讲解基础,初学者容易犯错的地方,都会指出来.从第三章开始,每章都有一个实践项目,用来巩固前面所学的知识. ...

  9. django orm 基本

    1 modle基本数据类型 class Test(models.Model): """测试学习用""" Auto = models.Auto ...

  10. C++容器(三):pair类型

    pair类型 在开始介绍关联容器之前,我们有必要了解一种与之相关的标准库类型–pair类型. 操作 含义 pair<T1, T2> p1 创建一个空的pair对象,它的两个元素分别为T1和 ...