RSA加密安全登录

 

我们平时做用户登录表单提交,用户名密码都是明文直接POST到后端,这样很容易被别人监听到(如一些收手机端端的流量监测app)。注:包括使用MD5等哈希函数处理后的数据,这里也算做明文(现在MD5爆破网站已经很多了~)。

对安全性要求较高的网站,比如银行和大型企业等都会使用HTTPS对其进行加密通讯。但是由于效率原因,使用HTTPS的代价是及其昂贵的,对于访问量稍大的网站就会造成严重的性能瓶颈。

解决方法一般只能采用专门的SSL硬件加速设备如F5的BIGIP等。所以很多网站选择了模拟SSL的做法,使用RSA来对密码等安全信息进行公钥加密,服务端用私钥解密。通常是对密码进行加密。

 

参考这篇文字,http://sunxboy.iteye.com/blog/209156,实现了登录密码rsa的加密。

实现方法:

1)服务端定时生成RSA密钥对(1台服务器负责生成供整个集群使用)

2)用户访问登录页时服务端返回公钥的exponent和module,rsa的js库要用到。

3)用户提交表单时,加密密码字段,提交表单,明文密码字段不传输

4)服务端获取私钥,解密加密后的密码

优化点:

1)密钥只在一定的时间内有效,如:一天更换一次密钥,但在密钥切换时还需保证上次的密钥的有效。通过增加时间戳参数可以实现。


RSA加密安全登录_第1张图片
 2)把加密后的密码放在密码框,提交表单,而浏览器有个记住密码的功能,这样就把加密后的密码记住了,而加密后的数据不是每次都一样的,呵呵问题来了。我通过增加一个隐藏字段,把加密后的密码放在这个字段,然后通过js移除原有密码框的name属性,再提交表单,就巧妙的解决了这个问题。

 

3)原文有个解密方法,里面一行代码会造成内存泄露,属于用法错误

  1. Cipher cipher = Cipher.getInstance("RSA",  
  2.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());  

 

Cipher.getInstance()不要每次都用new的Provider对象,因为一次线上问题,查看源码发现泄露的根源:

RSA加密安全登录_第2张图片
再看SunJCE_b.a(),

RSA加密安全登录_第3张图片
 

SunJCE_b中的f、e分别是

private static final Map e = new IdentityHashMap();
private static final Map f = new IdentityHashMap();

f最后移除了provider,但e一直在增加provider,provider作为IdentityHashMap的key,每次new时都不一样,然后。。就没有然后了。

IdentityHashMap:This class implements the Map interface with a hash table, using reference-equality in place of object-equality when comparing keys (and values). In other words, in an IdentityHashMap, two keys k1 and k2 are considered equal if and only if (k1==k2). (In normal Map implementations (like HashMap) two keys k1 and k2 are considered equal if and only if (k1==null ? k2==null : k1.equals(k2)).) 

This class is not a general-purpose Map implementation! While this class implements the Map interface, it intentionally violates Map's general contract, which mandates the use of the equals method when comparing objects. This class is designed for use only in the rare cases wherein reference-equality semantics are required. 

 

4)将加密后的16进制字符串转成字节数组方法不对:

byte[] enResult = new BigInteger(enc, 16).toByteArray();

 应该这样

public static byte[] hexStringToBytes(String hexString) {  
	    if (hexString == null || hexString.equals("")) {  
	        return null;  
	    }  
	    hexString = hexString.toUpperCase();  
	    int length = hexString.length() / 2;  
	    char[] hexChars = hexString.toCharArray();  
	    byte[] d = new byte[length];  
	    for (int i = 0; i < length; i++) {  
	        int pos = i * 2;  
	        d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));  
	    }  
	    return d;  
	}  
	/**  
	 * Convert char to byte  
	 * @param c char  
	 * @return byte  
	 */  
	 private static byte charToByte(char c) {  
	    return (byte) "0123456789ABCDEF".indexOf(c);  
	}

 参考http://blog.csdn.net/lich0000/article/details/8629018,否则遇到某些字符串无法正确解密

 

5)客户端js加密方法:

setMaxDigits(130);  
var key = new RSAKeyPair(exponent,"",publicModule); 
var encPass = encryptedString(key,pass)

 

6)最后还遇到一个问题,更新了线上的js后,js后最好跟一个时间戳,这样不需要用户手动清空缓存。另外手动打开chrome浏览器,自动化脚本打开chrome浏览器访问的js缓存是不同的,手动提交表单正常,自动化脚本提交表单还用的是老的js,好像是浏览器的不同实例引起的,具体是啥策略一时没闹明白???

 

 

 

你可能感兴趣的:(JAVA,web,Javascript)