前几天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;
}