前几天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代码:
需要一个全局变量 var publicKeys;
// 1.获取公钥 function getPublicKey() { $.jCryption.getKeys(getPath() + '/service/ajax/encrypt/getkey', function( receivedKeys) { publicKeys = receivedKeys; }); } // 2.加密密码,如果公钥为空则返回空字符串 function encrypt(password) { if ($.trim(password) != "" && "undefined" != publicKeys && null != publicKeys) { return $.jCryption.synchronousEncrypt(password, publicKeys); } else { return ""; } } // 3.获取公钥,同步版 function getPublicKeySync() { $.jCryption.getKeysSync(getPath() + '/service/ajax/encrypt/getkey', function( receivedKeys) { publicKeys = receivedKeys; }); }
Java后端工具类:
package com.taikang.util; 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 创建人 * @version V1.0 创建时间:2014-8-7 下午2:52:52 */ 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); } /** * 用给定的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); } } /** * 根据RSA密钥解密字符串 * @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); } } /** * 获得密钥模数 * * @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); } /** * 获得密钥指数 * * @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; } /** * @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(); } /** * @param args */ public static void main(String[] args) { JCryptionUtil jCryption; try { jCryption = new JCryptionUtil(); System.out.println(jCryption.toPublicKeyString()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
服务器端链接:
/** * 获取加密前台传输字符串用的公钥 * @return 公钥的json,包含指数,模数和有效位数 * @author 项赢 V1.0 2014-08-08 15:24 */ @RequestMapping("/ajax/encrypt/getkey") public @ResponseBody JSONObject getPublicKey(HttpSession session){ //初始化 JSONObject keysJson = new JSONObject(); try { LogUtil.info(LogConstants.App,"获取公钥(Get public keys)"); // 初始化工具类 JCryptionUtil jCryptionUtil = new JCryptionUtil(); // 生成密钥 KeyPair keys = jCryptionUtil.generateKeypair(512); // 保存密钥到session session.setAttribute("keys", keys); // 提取公钥 String e = JCryptionUtil.getPublicKeyExponent(keys); String n = JCryptionUtil.getPublicKeyModulus(keys); String md = String.valueOf(JCryptionUtil.getMaxDigits(512)); LogUtil.info(LogConstants.App,"公钥(Public keys):\ne:" + e + "\nn:" + n + "\nmaxdigits:" + md); // 填充返回结果 keysJson.put("e", e); keysJson.put("n", n); keysJson.put("maxdigits", md); } catch (Exception e){ e.printStackTrace(); } return keysJson; } /** * 解密前台传输字符串 * @param keys,session中的密钥 * @param encryptedPassword,加密过的前台传输字符串 * @return 解密完成的字符串,解密出错时返回空串 * @author 项赢 V1.0 2014-08-08 16:45 */ public String decrypt(KeyPair keys, String encryptedString){ String decryptedString = ""; try { LogUtil.info(LogConstants.App,"解密字符串(Decrypt encrypted string)"); decryptedString = JCryptionUtil.decrypt(encryptedString, keys); } catch (Exception e) { LogUtil.info(LogConstants.App,"解密字符串时发生异常(Exception on decrypting string)"); e.printStackTrace(); } return decryptedString; }