`
sunxboy
  • 浏览: 2830447 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

用javascript与java进行RSA加密与解密

阅读更多

这几天一直做安全登录,网上查了好多资料,不尽如意。

具体实现思路如下:

1。服务端生成公钥与私钥,保存。

2。客户端在请求到登录页面后,随机生成一字符串。

3。后此随机字符串作为密钥加密密码,再用从服务端获取到的公钥加密生成的随机字符串。

4。将此两段密文传入服务端,服务端用私钥解出随机字符串,再用此私钥解出加密的密文。

这其中有一个关键是解决服务端的公钥,传入客户端,客户端用此公钥加密字符串后,后又能在服务端用私钥解出。

此文即为实现此步而作。

加密算法为RSA:

1。服务端的RSA  java实现。

/**
 * 
 */
package com.sunsoft.struts.util;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;

import javax.crypto.Cipher;



/**
 * RSA 工具类。提供加密,解密,生成密钥对等方法。
 * 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。
 * 
 */
public class RSAUtil {
	/**
	 * * 生成密钥对 *
	 * 
	 * @return KeyPair *
	 * @throws EncryptException
	 */
	public static KeyPair generateKeyPair() throws Exception {
		try {
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			final int KEY_SIZE = 1024;// 没什么好说的了,这个值关系到块加密的大小,可以更改,但是不要太大,否则效率会低
			keyPairGen.initialize(KEY_SIZE, new SecureRandom());
			KeyPair keyPair = keyPairGen.generateKeyPair();
			saveKeyPair(keyPair);
			return keyPair;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}
	
	public static KeyPair getKeyPair()throws Exception{
		FileInputStream fis = new FileInputStream("C:/RSAKey.txt");
		 ObjectInputStream oos = new ObjectInputStream(fis);
		 KeyPair kp= (KeyPair) oos.readObject();
		 oos.close();
		 fis.close();
		 return kp;
	}
	
	public static void saveKeyPair(KeyPair kp)throws Exception{
		
		 FileOutputStream fos = new FileOutputStream("C:/RSAKey.txt");
         ObjectOutputStream oos = new ObjectOutputStream(fos);
         //生成密钥
         oos.writeObject(kp);
         oos.close();
         fos.close();
	}

	/**
	 * * 生成公钥 *
	 * 
	 * @param modulus *
	 * @param publicExponent *
	 * @return RSAPublicKey *
	 * @throws Exception
	 */
	public static RSAPublicKey generateRSAPublicKey(byte[] modulus,
			byte[] publicExponent) throws Exception {
		KeyFactory keyFac = null;
		try {
			keyFac = KeyFactory.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
		} catch (NoSuchAlgorithmException ex) {
			throw new Exception(ex.getMessage());
		}

		RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(
				modulus), new BigInteger(publicExponent));
		try {
			return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
		} catch (InvalidKeySpecException ex) {
			throw new Exception(ex.getMessage());
		}
	}

	/**
	 * * 生成私钥 *
	 * 
	 * @param modulus *
	 * @param privateExponent *
	 * @return RSAPrivateKey *
	 * @throws Exception
	 */
	public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus,
			byte[] privateExponent) throws Exception {
		KeyFactory keyFac = null;
		try {
			keyFac = KeyFactory.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
		} catch (NoSuchAlgorithmException ex) {
			throw new Exception(ex.getMessage());
		}

		RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(
				modulus), new BigInteger(privateExponent));
		try {
			return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
		} catch (InvalidKeySpecException ex) {
			throw new Exception(ex.getMessage());
		}
	}

	/**
	 * * 加密 *
	 * 
	 * @param key
	 *            加密的密钥 *
	 * @param data
	 *            待加密的明文数据 *
	 * @return 加密后的数据 *
	 * @throws Exception
	 */
	public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(Cipher.ENCRYPT_MODE, pk);
			int blockSize = cipher.getBlockSize();// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024
			// 加密块大小为127
			// byte,加密后为128个byte;因此共有2个加密块,第一个127
			// byte第二个为1个byte
			int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
			int leavedSize = data.length % blockSize;
			int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
					: data.length / blockSize;
			byte[] raw = new byte[outputSize * blocksSize];
			int i = 0;
			while (data.length - i * blockSize > 0) {
				if (data.length - i * blockSize > blockSize)
					cipher.doFinal(data, i * blockSize, blockSize, raw, i
							* outputSize);
				else
					cipher.doFinal(data, i * blockSize, data.length - i
							* blockSize, raw, i * outputSize);
				// 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
				// ByteArrayOutputStream中,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了
				// OutputSize所以只好用dofinal方法。

				i++;
			}
			return raw;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * * 解密 *
	 * 
	 * @param key
	 *            解密的密钥 *
	 * @param raw
	 *            已经加密的数据 *
	 * @return 解密后的明文 *
	 * @throws Exception
	 */
	public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance("RSA",
					new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(cipher.DECRYPT_MODE, pk);
			int blockSize = cipher.getBlockSize();
			ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
			int j = 0;

			while (raw.length - j * blockSize > 0) {
				bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
				j++;
			}
			return bout.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * * *
	 * 
	 * @param args *
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		RSAPublicKey rsap = (RSAPublicKey) RSAUtil.generateKeyPair().getPublic();
		String test = "hello world";
		byte[] en_test = encrypt(getKeyPair().getPublic(),test.getBytes());
		byte[] de_test = decrypt(getKeyPair().getPrivate(),en_test);
		System.out.println(new String(de_test));
	}
}

 2.测试页面:

IndexAction.java

/*
 * Generated by MyEclipse Struts
 * Template path: templates/java/JavaClass.vtl
 */
package com.sunsoft.struts.action;

import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import com.sunsoft.struts.util.RSAUtil;

/** 
 * MyEclipse Struts
 * Creation date: 06-28-2008
 * 
 * XDoclet definition:
 * @struts.action validate="true"
 */
public class IndexAction extends Action {
	/*
	 * Generated Methods
	 */

	/** 
	 * Method execute
	 * @param mapping
	 * @param form
	 * @param request
	 * @param response
	 * @return ActionForward
	 */
	public ActionForward execute(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)throws Exception {
		
		RSAPublicKey rsap = (RSAPublicKey) RSAUtil.getKeyPair().getPublic();
		String module = rsap.getModulus().toString(16);
		String empoent = rsap.getPublicExponent().toString(16);
		System.out.println("module");
		System.out.println(module);
		System.out.println("empoent");
		System.out.println(empoent);
		request.setAttribute("m", module);
		request.setAttribute("e", empoent);
		return mapping.findForward("login");
	}
}

 通过此action进入登录页面,并传入公钥的 Modulus 与PublicExponent的hex编码形式。

3。登录页面 login.jsp

<%@ page language="java" pageEncoding="GBK"%>

<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html:html lang="true">
  <head>
    <html:base />
    
    <title>login</title>

	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
<script type="text/javascript" src="js/RSA.js"></script>
<script type="text/javascript" src="js/BigInt.js"></script>
<script type="text/javascript" src="js/Barrett.js"></script>
<script type="text/javascript">
function rsalogin()
{
   bodyRSA();
   var result = encryptedString(key, document.getElementById("pwd").value);
   //alert(result);
   loginForm.action="login.do?result="+result;
   loginForm.submit();
}
var key ;
function bodyRSA()
{
	setMaxDigits(130);
  	key = new RSAKeyPair("10001","","8c1cd09a04ed01aafe70dc84c5f32ae23a16fe8fc8898aba6797c5a9c708720de4f08dbf086af429fc51c0636208f56de20a8ab5686affd9bdfb643ae1e90d5617155c4867eef06b0884ba8ecd187907c7069ae3eed4f0155eeca6573411864035ae803ad8fd91a0cc479f27e41b19c13465ab30f3cfbfd14de56f49cbd09481"); 
  
}

</script>
  </head>
  
  <body >
    <html:form action="login" method="post" focus="username">
      <table border="0">
        <tr>
          <td>Login:</td>
          <td><html:text property="username" /></td>
        </tr>
        <tr>
          <td>Password:</td>
          <td><html:password property="password" styleId="pwd"/></td>
        </tr>
        <tr>
          <td colspan="2" align="center"><input type="button" value="SUBMIT" onclick="rsalogin();"/></td>
        </tr>
      </table>
    </html:form>
  </body>
</html:html>

 3.点击登录后,调用LoginAction.java

/*
 * Generated by MyEclipse Struts
 * Template path: templates/java/JavaClass.vtl
 */
package com.sunsoft.struts.action;

import java.math.BigInteger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import com.sunsoft.struts.util.RSAUtil;

/** 
 * MyEclipse Struts
 * Creation date: 06-28-2008
 * 
 * XDoclet definition:
 * @struts.action path="/login" name="loginForm" input="/login.jsp" scope="request" validate="true"
 * @struts.action-forward name="error" path="/error.jsp"
 * @struts.action-forward name="success" path="/success.jsp"
 */
public class LoginAction extends Action {
	/*
	 * Generated Methods
	 */

	/** 
	 * Method execute
	 * @param mapping
	 * @param form
	 * @param request
	 * @param response
	 * @return ActionForward
	 */
	public ActionForward execute(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response) throws Exception{
		//LoginForm loginForm = (LoginForm) form;
		String result = request.getParameter("result");
		System.out.println("原文加密后为:");
		System.out.println(result);
		byte[] en_result = new BigInteger(result, 16).toByteArray();
		System.out.println("转成byte[]"+new String(en_result));
		byte[] de_result = RSAUtil.decrypt(RSAUtil.getKeyPair().getPrivate(),en_result);
		System.out.println("还原密文:");
		
		System.out.println(new String(de_result));
		StringBuffer sb = new StringBuffer();
		sb.append(new String(de_result));
		System.out.println(sb.reverse().toString());
		return mapping.findForward("success");
	}
}

 因为发现解出的明文是倒序的,后面就用StringBuffer的reverse()来转换了一下。

4。login.jsp所调用的js

 

  • js.rar (6.4 KB)
  • 描述: login.jsp所调用的javascript,有: RSA.js BigInt.js Barrett.js
  • 下载次数: 2830
分享到:
评论
28 楼 xuniverse 2016-04-06  
hannibal005 写道
楼主,我问下 request.setAttribute("m", module); 
        request.setAttribute("e", empoent); 
这个的作用是把公钥的 Modulus 与PublicExponent传入login.jsp么?我在login.jsp没有看到接受函数,请问JS加密的时候需要这2个参数么?

我也想问这个问题,感觉都没用到这两个公钥参数啊
27 楼 kalogen 2015-10-29  
gtczr 写道
非常感谢,经过我自己的修改,已经完美实现。

发出来分享一下
26 楼 gtczr 2014-12-16  
非常感谢,经过我自己的修改,已经完美实现。
25 楼 baiyun_rain 2014-06-16  
[color=cyan][/color][size=small][/size][align=right][/align]
24 楼 lwl_radium 2014-03-06  
如大家所说,有些字符js加密后,java解密不了。产生不同的密钥,解密不了的字符串会不同
23 楼 wwilovehome 2013-05-28  
楼主,我用JAVA加密,JS解密出现问题,不知道是不是我书写有错。如果方便,希望能够写一个demo发我邮箱:wwilovehome@126.com,谢谢。
22 楼 nokelong 2013-05-21  
上面有二错误,bodyRSA中应该是key = new RSAKeyPair("${e}","","${m}");纠结了一天,
一个是encryptedString(key, encodeURIComponent(document.getElementById("pwd").value));用encodeURIComponent转码。大家相互学习
21 楼 nokelong 2013-05-20  
huangshazsw 写道
为什么我后台解密出来的是乱码啊,求解

20 楼 huangshazsw 2013-05-02  
为什么我后台解密出来的是乱码啊,求解
19 楼 mingren135 2013-04-19  
小心,new 那个provider会出现内存泄露的bug
18 楼 jin8000608172 2013-01-29  
楼上的问题我都解决了
17 楼 yangy608 2012-11-15  
有个bug,单独加密1或5是解不出来的
16 楼 awerty3450p 2012-05-14  
我按六楼的方法 已经解决JAVA解密不正确的问题 但是JAVA加密得到的代码不正确  求破的方法
15 楼 awerty3450p 2012-05-11  
博主你好,我有一个字符js加密 和 JAVA加密以后不一致  除了最后一位其他都差1 而另外的字符加密正常,百思不得其解
14 楼 Mov_webhobo 2012-04-19  
引用
因为发现解出的明文是倒序的,后面就用StringBuffer的reverse()来转换了一下。

没有看懂JS源码,不知道这个的原因是什么?

引用

3 楼 hdown 2009-07-21   引用
已经在用了,谢谢啦

不过加密个别中英文字符串的时候会抛异常的

真的假的?搞得不敢用了!
13 楼 a47313198 2011-12-26  
guzhixiong 写道
楼主,js 加密中文后,解密的是乱码,需要怎么解决呢,,试了好多方法没有解决,请问你有碰到过吗?是怎么解决的,请赐教


先使用 encodeURIComponent() 函数对中文进行编码,再加密试试。
解密后,使用 java.net.Decoder.decode(str, "UTF-8") 进行中文还原。
12 楼 hannibal005 2011-11-06  
还是觉得JS里面的加密方法有问题。。因为我java里面的源程序加密解密字符串13579都没问题
11 楼 hannibal005 2011-11-05  
这个方法很多字符都不能正常解密,比如13579 等等
10 楼 hannibal005 2011-11-05  
楼主,我问下 request.setAttribute("m", module); 
        request.setAttribute("e", empoent); 
这个的作用是把公钥的 Modulus 与PublicExponent传入login.jsp么?我在login.jsp没有看到接受函数,请问JS加密的时候需要这2个参数么?
9 楼 guzhixiong 2011-09-07  
楼主,js 加密中文后,解密的是乱码,需要怎么解决呢,,试了好多方法没有解决,请问你有碰到过吗?是怎么解决的,请赐教

相关推荐

Global site tag (gtag.js) - Google Analytics