前几天user有个要求,就是系统中所有的密码输入框的内容(password) 必须加密提交。即使用工具能抓取到request,但是抓取到的password 都是密文。
首先了解:RSA加密算法的精髓是什么
非对称加密。 密钥不是一个,而是一对,A和B。使用A加密的密文只能使用B来解密。使用B加密的密文只能使用A来解密。选择其中的一个作为公钥,另一个作为私钥,可以用于许多非对称加密应用场合。比如电子证书、电子指纹、电子签名、握手协议等传统对称加密手段无法实现的功能。 其核心是基于“大数的分解质因数非常困难”这个数学难题。使得在得知A的人无法推算出B,B也无法推算出A。
为此:找了很多解决方案,最终采用的如下
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的话。
请参考