前几天user有个要求,就是系统中所有的密码输入框的内容(password) 必须加密提交。即使用工具能抓取到request,但是抓取到的password 都是密文。
为此:找了很多解决方案,最终采用的如下
web 前端 javascript RSA加密提交数据,server端用java解密
采用的第三方组件及jar
1.jquery-1.6.js
2.jquery.jcryption-1.1.js
jar files:
3.bcprov-ext-jdk15on-148.jar
4.bcprov-jdk15on-148.jar
步骤:
第一:当系统在加载页面完成后,如果发现页面中包含密码框,则向server发起请求来获取key--用于js加密。此key要存于session中,以便server解密。
第二:当密码框在输入密码失焦时,调用js加密。在提交的时候,check password是否加密。阻止明文提交。
第三:server在收到请求后,对密文进行解密。
js code:
var keys; $(function(){ var hasPass = $('input:password'); var token = $('#csrfToken').val(); getKeys(); $(hasPass).each(function(index){ var currentItem = $(this); $(currentItem).focus(function(){ $(currentItem).val(""); }); $(currentItem).blur(function(){ var cVal = $(currentItem).val(); if($.trim(cVal) != "" && "undefined" != keys && null != keys){ $.jCryption.encrypt($(currentItem).val(), keys, function(encryptedPasswd) { $(currentItem).val(encryptedPasswd); }); } }); }); }); function getKeys() { $.jCryption.getKeys(URL,function(receivedKeys) { keys = receivedKeys; }); } function submitForm(form){ var flag = true; //check password is encrypt or not var Cpass; $(form).find("input:password").each(function(index){ var currPass = $(this); var value = $.trim($(currPass).val()); var reg = /^[A-Za-z0-9]{128}$/; if(value == ""){ flag = false; Cpass = $(currPass); }else if(!reg.test(value)){ //the reason maybe previous request gain public key failed //so please check - public key via ajax request.-(getKey(val)) //alert("key-->"+keys); alert("encrypt failed."); flag = false; } }); if(flag == true){ form.submit(); }else{ if(undefined != Cpass && null != Cpass && $(Cpass).val() == ""){ alert("Please key in your password."); $(Cpass).focus(); }else{//redirect to login page var url = window.location.href; //there can do clear form ... window.location.href = url+"?request=Login"; } } }
加密解密工具类
package util.jsRSAjava;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
/**
* @author hbkeepmoving@hotmail.com
*
*/
public class JCryptionUtil {
public static final java.security.Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
/**
* Constructor
*/
public JCryptionUtil() throws Exception {
java.security.Security.addProvider(provider);
}
/**
* Generates the Keypair with the given keyLength.
*
* @param keyLength
* length of key
* @return KeyPair object
* @throws RuntimeException
* if the RSA algorithm not supported
*/
public KeyPair generateKeypair(int keyLength) throws Exception {
try {
KeyPairGenerator kpg;
try {
kpg = KeyPairGenerator.getInstance("RSA");
} catch (Exception e) {
kpg = KeyPairGenerator.getInstance("RSA",provider);
}
kpg.initialize(keyLength);
KeyPair keyPair = kpg.generateKeyPair();
return keyPair;
} catch (NoSuchAlgorithmException e1) {
throw new RuntimeException("RSA algorithm not supported", e1);
} catch (Exception e) {
throw new Exception("other exceptions", e);
}
}
/**
* Decrypts a given string with the RSA keys
*
* @param encrypted
* full encrypted text
* @param keys
* RSA keys
* @return decrypted text
* @throws RuntimeException
* if the RSA algorithm not supported or decrypt operation failed
*/
public static String decrypt(String encrypted, KeyPair keys) throws Exception {
Cipher dec;
try {
try {
dec = Cipher.getInstance("RSA/NONE/NoPadding");
} catch (Exception e) {
dec = Cipher.getInstance("RSA/NONE/NoPadding",provider);
}
dec.init(Cipher.DECRYPT_MODE, keys.getPrivate());
} catch (GeneralSecurityException e) {
throw new RuntimeException("RSA algorithm not supported", e);
}
String[] blocks = encrypted.split("\\s");
StringBuffer result = new StringBuffer();
try {
for (int i = blocks.length - 1; i >= 0; i--) {
byte[] data = hexStringToByteArray(blocks[i]);
byte[] decryptedBlock = dec.doFinal(data);
result.append(new String(decryptedBlock));
}
} catch (GeneralSecurityException e) {
throw new RuntimeException("Decrypt error", e);
}
/**
* Some code is getting added in first 2 digits with Jcryption need to investigate
*/
return result.reverse().toString().substring(2);
}
/**
* Parse url string (Todo - better parsing algorithm)
*
* @param url
* value to parse
* @param encoding
* encoding value
* @return Map with param name, value pairs
*/
public static Map parse(String url, String encoding) {
try {
String urlToParse = URLDecoder.decode(url, encoding);
String[] params = urlToParse.split("&");
Map parsed = new HashMap();
for (int i = 0; i < params.length; i++) {
String[] p = params[i].split("=");
String name = p[0];
String value = (p.length == 2) ? p[1] : null;
parsed.put(name, value);
}
return parsed;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unknown encoding.", e);
}
}
/**
* Return public RSA key modulus
*
* @param keyPair
* RSA keys
* @return modulus value as hex string
*/
public static String getPublicKeyModulus(KeyPair keyPair) {
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
return publicKey.getModulus().toString(16);
}
/**
* Return public RSA key exponent
*
* @param keyPair
* RSA keys
* @return public exponent value as hex string
*/
public static String getPublicKeyExponent(KeyPair keyPair) {
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
return publicKey.getPublicExponent().toString(16);
}
/**
* Max block size with given key length
*
* @param keyLength
* length of key
* @return numeber of digits
*/
public static int getMaxDigits(int keyLength) {
return ((keyLength * 2) / 16) + 3;
}
/**
* Convert byte array to hex string
*
* @param bytes
* input byte array
* @return Hex string representation
*/
public static String byteArrayToHexString(byte[] bytes) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
result.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16)
.substring(1));
}
return result.toString();
}
/**
* Convert hex string to byte array
*
* @param data
* input string data
* @return bytes
*/
public static byte[] hexStringToByteArray(String data) {
int k = 0;
byte[] results = new byte[data.length() / 2];
for (int i = 0; i < data.length();) {
results[k] = (byte) (Character.digit(data.charAt(i++), 16) << 4);
results[k] += (byte) (Character.digit(data.charAt(i++), 16));
k++;
}
return results;
}
/**
* @param args
*/
// public static void main(String[] args) {
// JCryptionUtil jCryption = new JCryptionUtil();
// System.out.println(jCryption.toPublicKeyString());
// }
/**
* @return
*/
public String toPublicKeyString() throws Exception {
KeyPair keys = generateKeypair(512);
StringBuffer out = new StringBuffer();
String e = getPublicKeyExponent(keys);
String n = getPublicKeyModulus(keys);
String md = String.valueOf(getMaxDigits(512));
out.append("{\"e\":\"");
out.append(e);
out.append("\",\"n\":\"");
out.append(n);
out.append("\",\"maxdigits\":\"");
out.append(md);
out.append("\"}");
return out.toString();
}
}
生成key的serverlet 代码。很简单。
JCryptionUtil jCryptionUtil = new JCryptionUtil();
KeyPair keys = null;
keys = jCryptionUtil.generateKeypair(512);
request.getSession().setAttribute("keys", keys);
StringBuffer output = new StringBuffer();
String e = JCryptionUtil.getPublicKeyExponent(keys);
String n = JCryptionUtil.getPublicKeyModulus(keys);
String md = String.valueOf(JCryptionUtil.getMaxDigits(512));
output.append("{\"e\":\"");
output.append(e);
output.append("\",\"n\":\"");
output.append(n);
output.append("\",\"maxdigits\":\"");
output.append(md);
output.append("\"}");
output.toString();
response.getOutputStream().print(output.toString().replaceAll("\r", "").replaceAll("\n", "").trim());
解密的servlet中代码,当然也可在写在filter中
String pass = request.getParameter("pass");//login pass
KeyPair keys = (KeyPair) SessionUtility.getSession(request).getAttribute("keys");
String decryptedPass = JCryptionUtil.decrypt(pass, keys);
request.setAttribute("pass", decryptedPass);
到此,就处理了。
现在我不知道,还有没有更好的解决方法。。。。各位大神多指点哈。。
另外:如果自己project 会部署到其他系统(eg:unix.....etc),如果在获取key失败,检查一下server log。如果有securityPermission的exception的话。
请参考
http://hb-keepmoving.iteye.com/blog/1851179