这几天一直做安全登录,网上查了好多资料,不尽如意。
具体实现思路如下:
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
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