RSA在前端加密后端解密的应用

问题来源

本人要搭出一个系统的原型,需要账户登录功能,那最实用的密码加密算法肯定是RSA了,鉴于密钥使用的特殊性,所以在前端加密后端解密是最好的选择了。本文有两个可行的思路,主要详细写第一个(以JFinal框架为例)。

思路

为了避免密钥对传到前端被截获,所以密钥要在后端生成,并存于session里,然后把公钥传给前端供前端JS加密,前端ajax只传回账号和公钥加密密码,后端得到公钥加密密码后取得session中的私钥进行解密(一般来说密码最好再用对称加密处理一下再存进数据库里更妥,例如MD5)
  • 在一个会话期间只生成一次密钥对:这就需要使用HttpSessionListener来对会话的创建和销毁进行密钥对生成(记得在web.xml里注册监听器)
/**
 * 会话监听器
 * @author Wilson 2016/04/02
 *
 */
public class SessionListener implements  HttpSessionListener{
	private static final Logger logger = Logger.getLogger(SessionListener.class);
	    
    @Override
    public void sessionCreated(HttpSessionEvent hse) {
        try {
            Map<String,Object> keyPair = CryptoKit.initKey();
            hse.getSession().setAttribute("rsa_public_key", CryptoKit.getPublicKey(keyPair));
            hse.getSession().setAttribute("rsa_private_key", CryptoKit.getPrivateKey(keyPair));
        } catch (Exception e) {
            logger.error(e);
        }
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent hse) {
        
    }
}
  • 在功能里创建密钥对,每次调用页面都重新生成一对,同样放在session里(记得判断和清除上一次的密钥对),可能比上一个方法更为保密,但确实不方便,见仁见智把
/**
 * 登陆控制器
 * @author Wilson 2017/04/03
 *
 */
public class LoginController extends Controller{
	public void index() throws Exception {
		HttpSession session = getSession();
		
		session.removeAttribute("rsa_public_key");
		session.removeAttribute("rsa_private_key");
		
		Map<String,Object> keyPair = CryptoKit.initKey();
		String pubKey = CryptoKit.getPublicKey(keyPair);
        session.setAttribute("rsa_public_key", pubKey);
        session.setAttribute("rsa_private_key", CryptoKit.getPrivateKey(keyPair));
		setAttr("rsa_public_key",pubKey);
		renderFreeMarker("Login.html");
	}
}

步骤

  • 先设计好加密工具类(这里借用了网上的代码,并改用common.codec来处理base64)
/**
 * 密文工具
 * @author Wilson 2017/04/01
 *
 */
public class CryptoKit {
	public static final String KEY_ALGORITHM = "RSA";  
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";  
    private static final String PUBLIC_KEY = "RSAPublicKey";  
    private static final String PRIVATE_KEY = "RSAPrivateKey";  
    private static final int MAX_ENCRYPT_BLOCK = 117; //RSA最大加密明文大小
    private static final int MAX_DECRYPT_BLOCK = 128; //RSA最大解密密文大小
    
    public static final String KEY_SHA = "SHA";  
    public static final String KEY_MD5 = "MD5";
	
	private CryptoKit(){}
	
    public static void main(String[] args) throws Exception{
    	Map<String, Object> keyPair =  initKey();
    	String privateKey = getPrivateKey(keyPair);
    	String publicKey = getPublicKey(keyPair);

    	System.err.println("公钥加密——私钥解密"); 
		String source = "这是一行没有任何意义的文字,你看完了等于没看,不是吗?"; 
		System.out.println("\r加密前文字:\r\n" + source); 
		byte[] data = source.getBytes(); 
		byte[] encodedData = encryptByPublicKey(data, publicKey);
		byte[] encodedData2 = encryptByPublicKey(data, publicKey);
		System.out.println("加密后文字:\r\n" + new String(encodedData));
		System.out.println("加密后文字:\r\n" + new String(encodedData2));
		byte[] decodedData = decryptByPrivateKey(encodedData, privateKey); 
		String target = new String(decodedData); 
		System.out.println("解密后文字: \r\n" + target);
		
		
		System.err.println("私钥加密——公钥解密"); 
		byte[] encodedDa = encryptByPrivateKey(data, privateKey); 
		System.out.println("加密后:\r\n" + new String(encodedDa)); 
		byte[] decodedDa = decryptByPublicKey(encodedDa, publicKey); 
		String tar = new String(decodedDa); 
		System.out.println("解密后: \r\n" + tar); 
		
		
		System.err.println("私钥签名——公钥验证签名"); 
		String sign = sign(encodedData, privateKey); 
		System.err.println("签名:\r" + sign); 
		boolean status = verify(encodedData, publicKey, sign); 
		System.err.println("验证结果:\r" + status);  
    }
    
    /** 
     * BASE64解密 
     *  
     * @param key 
     * @return 
     * @throws Exception 
     */  
    public static byte[] decryptBASE64(String key) {  
        return Base64.decodeBase64(key);
    }  
   
    /** 
     * BASE64加密 
     *  
     * @param key 
     * @return 
     * @throws Exception 
     */  
    public static String encryptBASE64(byte[] key) {  
        return Base64.encodeBase64String(key); 
    }  
   
    /** 
     * MD5加密 (信息摘要不可逆)
     *  
     * @param data 
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptMD5(byte[] data) throws Exception {  
        MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);  
        md5.update(data);  
        return md5.digest();  
    }  
   
    /** 
     * SHA加密
     *  
     * @param data 
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptSHA(byte[] data) throws Exception {  
        MessageDigest sha = MessageDigest.getInstance(KEY_SHA);  
        sha.update(data);  
   
        return sha.digest();
    }  
    
    /** 
     * 用私钥对信息生成数字签名 
     *  
     * @param data 加密数据 
     * @param privateKey 私钥 
     * @return 
     * @throws Exception 
     */  
    public static String sign(byte[] data, String privateKey) throws Exception {  
        // 解密由base64编码的私钥  
        byte[] keyBytes = decryptBASE64(privateKey);  
   
        // 构造PKCS8EncodedKeySpec对象  
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
   
        // KEY_ALGORITHM 指定的加密算法  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
   
        // 取私钥匙对象  
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);  
   
        // 用私钥对信息生成数字签名  
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initSign(priKey);  
        signature.update(data);  
   
        return encryptBASE64(signature.sign());  
    }  
   
    /** 
     * 校验数字签名 
     *  
     * @param data 加密数据 
     * @param publicKey 公钥 
     * @param sign 数字签名
     * @return 校验成功返回true 失败返回false 
     * @throws Exception 
     */  
    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {  
   
        // 解密由base64编码的公钥  
        byte[] keyBytes = decryptBASE64(publicKey);  
   
        // 构造X509EncodedKeySpec对象  
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);  
   
        // KEY_ALGORITHM 指定的加密算法  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
   
        // 取公钥匙对象  
        PublicKey pubKey = keyFactory.generatePublic(keySpec);  
   
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initVerify(pubKey);  
        signature.update(data);  
   
        // 验证签名是否正常  
        return signature.verify(decryptBASE64(sign));  
    }  
   
    /** 
     * 用私钥解密 
     * @param data 
     * @param key (BASE64) 
     * @return 
     * @throws Exception 
     */  
    public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {      
        // 取得私钥  
    	byte[] keyBytes = decryptBASE64(key);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); 
        
    	// 对数据解密  
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, privateKey);  
        
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
            else cache = cipher.doFinal(data, offSet, inputLen - offSet);
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }  
   
    /** 
     * 用公钥解密 
     * @param data 
     * @param key (BASE64)
     * @return 
     * @throws Exception 
     */  
    public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {  
        // 取得公钥  
    	byte[] keyBytes = decryptBASE64(key);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key publicKey = keyFactory.generatePublic(x509KeySpec);  
   
        // 对数据解密  
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, publicKey);  
        
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }  
   
    /** 
     * 用公钥加密 
     *  
     * @param data 
     * @param key (BASE64)
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {  
        // 对公钥解密  
        byte[] keyBytes = decryptBASE64(key);  
   
        // 取得公钥  
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key publicKey = keyFactory.generatePublic(x509KeySpec);  
   
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            else cache = cipher.doFinal(data, offSet, inputLen - offSet);
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }  
   
    /** 
     * 用私钥加密 
     *  
     * @param data 
     * @param key (BASE64)
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {  
        // 对密钥解密  
        byte[] keyBytes = decryptBASE64(key);  
   
        // 取得私钥  
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);  
   
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);  

        return cipher.doFinal(data);  
    }  
   
    /** 
     * 取得私钥 
     *  
     * @param keyMap 
     * @return 
     * @throws Exception 
     */  
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {  
    	RSAPrivateKey key = (RSAPrivateKey) keyMap.get(PRIVATE_KEY);
        return encryptBASE64(key.getEncoded());  
    }  
   
    /** 
     * 取得公钥 
     *  
     * @param keyMap 
     * @return 
     * @throws Exception 
     */  
    public static String getPublicKey(Map<String, Object> keyMap) throws Exception {  
    	RSAPublicKey key = (RSAPublicKey) keyMap.get(PUBLIC_KEY);  
        return encryptBASE64(key.getEncoded());  
    }  
   
    /** 
     * 初始化密钥对 
     *  
     * @return 
     * @throws Exception 
     */  
    public static Map<String, Object> initKey() throws Exception {  
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);  
        keyPairGen.initialize(1024);  
   
        KeyPair keyPair = keyPairGen.generateKeyPair();  
   
        // 公钥  
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  
   
        // 私钥  
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();  
   
        Map<String, Object> keyMap = new HashMap<String, Object>(2);  
   
        keyMap.put(PUBLIC_KEY, publicKey);  
        keyMap.put(PRIVATE_KEY, privateKey);  
        return keyMap;  
    } 
}
  • 前端JS,利用了jsencrypt.js来进行加密,通过后端的公钥便可以进行密码的加密(这里是requireJS的写法)
<script>
require([
         'jquery',
         '${base}/res/plugin/cryptico/jsencrypt.min.js'
         ], function($,jsencrypt) {
	var t = '${rsa_public_key}';
	var encrypt = new jsencrypt.JSEncrypt();
    encrypt.setPublicKey(t);
    var encrypted = encrypt.encrypt("123456");
	$.ajax({
		url: '${base}/index/login',
		data: {user:'admin',password:encrypted},
		success: function(data){
			
		}
	});
});
</script>
  • 后端java
/**
 * 登陆控制器
 * @author Wilson 2017/04/03
 *
 */
public class LoginController extends Controller{
	public void index() {
		String pubKey = getSessionAttr("rsa_public_key");
		setAttr("rsa_public_key",pubKey);
		renderFreeMarker("Login.html");
	}
	
	/**
	 * 登陆
	 * @throws Exception 
	 */
	public void login() throws Exception {
		String user = getPara("user");
		String password = getPara("password");
		String priKey = getSessionAttr("rsa_private_key");
		String text2 = new String(CryptoKit.decryptByPrivateKey(CryptoKit.decryptBASE64(password), priKey));
		//TO DO
		renderNull();
	}
}








  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值