前几天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 [email protected] * */ 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