安全登录完整实现方案

思路如下:

1.从进入登录页面时,从服务器取得公钥。
2.在客户端 产生一20位的随机数,并用公钥加密此随机数。

3.用随机数加密密码。
4.将随机数的密文与密码的密文,传送到服务器。

5.服务器此私钥解出随机数,再用随机数解出密码密文。

 

具体实现:

(1).通过在登录页面通过dwr取得公钥以及需要的js.

<%@page contentType="text/html;charset=GBK"%>
<%-- DWR --%>
<script type='text/JavaScript' src='dwr/interface/vbaoCommon.js'></script>
<script type="text/javascript" src="dwr/engine.js"></script>
<script type="text/javascript" src="dwr/util.js"></script>

<%-- 验证 --%>
<script language=javascript src="js/verify.js"></script>

<%-- 软键盘 --%>
<link href="css/keyboard.css" rel="stylesheet" type="text/css">
<script language="JavaScript" type="text/JavaScript">
<!--

<%-- 保存全局key变量 --%>
var key;
var keys;

<%-- 进入页面时,请求服务器取得公钥,并生成需要的js --%>
vbaoCommon.getJSUrlForLogin("login",handleGetData); 

<%-- 生成javascript --%>
function createJS(jssrc){
 	var oHead = document.getElementsByTagName('HEAD').item(0);
    var oScript= document.createElement("script");
    oScript.type = "text/javascript";
    oScript.src=jssrc;
    oHead.appendChild(oScript); 
}

<%-- 生成指定长度的随机数  --%>
function radomStr(length)
{
  chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
  var str = "";

   for(x=0;x<length;x++)
   {
      i = Math.floor(Math.random() * 62);
      str += chars.charAt(i);
   }

   return str;
}

function createKey(){
  setMaxDigits(130);
  key =new RSAKeyPair(keys[1],"",keys[0]);
}

<%-- 处理从服务器返回的数据  --%>
function handleGetData(data){
  keys = data.split(",");
  for(var i=2;i<keys.length;i++)
  {
	  createJS(keys[i]);
  }
  setTimeout("createKey()",1000);
}

<%-- 校验验证码 --%>
function autoCheckAuthcode()
{
 var input = $("authcodeInput").value;
 if(input.length == 4)
 {
 	vbaoCommon.checkAuthcode(input,callBack);
 }
}
<%-- 根据返回值,判断输入验证是否正确 --%>
function callBack(boolean) {
  
  if(boolean) {
  	$("checkImg").innerHTML='<img src="images/check_right.gif" width="13" height="13">  ';
  }else {
  	$("checkImg").innerHTML='<img src="images/check_error.gif" width="13" height="13">  ';
  }
}

function formLoginSubmit()
{
  //去除用户名首尾的空格
  $("userName").value = $("userName").value.Trim();
  var userName =  $("userName").value;
  var pwd = $("password").value.Trim();
  var authcode = $("authcodeInput").value;
  ${"password"}.value=Math.random();
  
	var ram = radomStr(20);
	var en_pwd = window.btoa(VPAY.encrypt(pwd,ram));
	$("en_pwd").value = en_pwd;
	var en_ram = encryptedString(key,ram);
	$("en_ram").value = en_ram;

	pwd = "";
	en_pwd = "";
	en_ram = "";
	return true;
}

	//看不清时 更换验证码
	function getImageCode() 
	{ 
		var randomnum = Math.random(); 
		var getimagecode = $("chkcode");
		getimagecode.src = "/vbao/indexCheckImage.do?" + randomnum; 
		autoCheckAuthcode();
	}
//-->
</script>

<table width="950" border="0" align="center" cellpadding="0"
		cellspacing="0">
  <tr>
    <td width="8" background="images/new_images/lineleft_bk_1.jpg"> </td>
    <td width="466" align="center" valign="top" bgcolor="#FFFFFF"><img src="images/new_images/1.gif" width="670" height="170"><img src="images/new_images/liucheng.gif" width="670" height="174"></td>
    <td align="left" valign="top" bgcolor="#FFFFFF">
    
    <table width="100%" height="344" border="0" cellpadding="0" cellspacing="0" bgcolor="#FFFFFF" class="kuang011">
      <tr>
        <td align="center" valign="top" background="images/new_images/login_05.jpg">
         <form name="logonForm" action="/vbao/logonVibao.do?method=login" method="post" οnsubmit="return formLoginSubmit();"">
        <table width="100%" border="0" cellpadding="0" cellspacing="0">
            <tr>
              <td height="42" colspan="3" align="right" background="images/new_images/login.jpg" class="font3"> </td>
              </tr>
                    
                      <tr>
                       
                        <td colspan="3" class="kuang2" align="center">
                        <font color="red">
                         	<html:errors property="error"/>
							</font>
                        </td>
							
                      </tr>
                     <tr>
                        <td width="30%" align="right" height="35" class="font1">登录方式: </td>
                        <td width="70%" align="left" class="font1" colspan="2">
                        <select name="checkType" class="kuang3" style="width: 135px;">
                        <option value="1">昵 称</option>
                        <option value="2">卡 号</option>
                        <option value="3">证 件 号</option>
                        </select>
                        </td>
                      </tr>
                      <tr>
                        <td width="30%" height="35" align="right" class="font1">帐    户: </td>
                        <td width="70%" align="left" class="font1" colspan="2">
                        <input name="userName" id="userName" type="text" class="kuang3" size="25" maxlength="25" style="width: 135px;"></td>
                      </tr>
                       <tr>
                        <td width="30%" height="35" align="right" class="font1">密    码: </td>
                        <td width="70%" align="left" class="font1" colspan="2">
                        <input name="password" id="password" type="password"  size="18" maxlength="12" style="width: 135px;" class="keyboardInput"></td>
                      </tr>
                      <tr>
                        <td width="90%" class="font2" colspan="2" height="45">  请将下边的文字输入框中</td>
                        <td></td>
                      </tr>
                      <tr>
                        <td colspan="3">
                               
                         <input name="authcode" alt="验证码" οnkeyup="javascript:autoCheckAuthcode();" style="height: 25px;padding: 5px;width: 60px;" maxlength="8" id="authcodeInput" 	value="" type="text" size="4"/>
									<img id="chkcode" src="/vbao/indexCheckImage.do" width="50" height="25" style="margin: -5px;"/>
						<a class="font1" href="javascript:getImageCode();">看不清</a>
						<em id="checkImg"></em>
						</td>
                      </tr>
					  <tr>
			              <td height="40" align="right" class="kuang03"> </td>
			              <td align="left" class="kuang03"> </td>
			            </tr>
                      <tr>
                      <td colspan="3" height="50">
                        
	                      <input type="submit"
	                      		id="loginSubmit"
								name="submitForm"
								style="background-image: url('images/zxzx_26.jpg'); cursor:pointer; border:0; width:54; height:24;"
								value=" "/>
         
           
                      <input type="hidden" name="en_pwd" id="en_pwd" value="" />
                      <input type="hidden" name="en_ram" id="en_ram" value="" />                      
                      </td>
                      </tr>
			
        </table></form>
        </td>
      </tr>
    </table>
    </td>
    <td width="8" background="images/new_images/lineright_bk_1.jpg"> </td>
  </tr>
</table>
 

(2).在服务端,取得公钥,并取得js地址,回传给客户端

public String getJSUrlForLogin(String sign) {
		// KeyTool kt = new KeyTool();
		RSAPublicKey dhp = null;
		try {
			if(pk == null)
				pk = RSAUtil.getKeyPair().getPublic();

			dhp = (RSAPublicKey) pk;
		} catch (Exception ex) {
			if (pk == null)
				try {
					KeyPair kp = RSAUtil.generateKeyPair();
					dhp = (RSAPublicKey)kp.getPublic();
				} catch (Exception e) {
					e.printStackTrace();
				}
			ex.printStackTrace();
		}
		String m_value = dhp.getModulus().toString(16);
	    String e_value = dhp.getPublicExponent().toString(16);
		return m_value + "," + e_value+",js/keyboard.js,js/RSA.js,js/xxtea.js,js/base64.js,js/BigInt.js,js/Barrett.js";
	}

 

(3).根据地址动态生成javascript

<%-- 生成javascript --%>
function createJS(jssrc){
 	var oHead = document.getElementsByTagName('HEAD').item(0);
    var oScript= document.createElement("script");
    oScript.type = "text/javascript";
    oScript.src=jssrc;
    oHead.appendChild(oScript); 
}

function createKey(){
  setMaxDigits(130);
  key =new RSAKeyPair(keys[1],"",keys[0]);
}

<%-- 处理从服务器返回的数据  --%>
function handleGetData(data){
  keys = data.split(",");
  for(var i=2;i<keys.length;i++)
  {
	  createJS(keys[i]);
  }
  setTimeout("createKey()",1000);
}
 

(4).生成随机串,用此加密密码,发送回服务器

<%-- 生成指定长度的随机数  --%>
function radomStr(length)
{
  chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
  var str = "";

   for(x=0;x<length;x++)
   {
      i = Math.floor(Math.random() * 62);
      str += chars.charAt(i);
   }

   return str;
}

function formLoginSubmit()
{
  //去除用户名首尾的空格
  $("userName").value = $("userName").value.Trim();
  var userName =  $("userName").value;
  var pwd = $("password").value.Trim();
  var authcode = $("authcodeInput").value;
  ${"password"}.value=Math.random();
  
	var ram = radomStr(20);
	var en_pwd = window.btoa(VPAY.encrypt(pwd,ram));
	$("en_pwd").value = en_pwd;
	var en_ram = encryptedString(key,ram);
	$("en_ram").value = en_ram;

	pwd = "";
	en_pwd = "";
	en_ram = "";
	return true;
}
 

(5).服务端的解密过程

String en_ram = lf.getEn_ram();
		byte[] de_pwd = null;
		try {
			PrivateKey pk = RSAUtil.getKeyPair().getPrivate();
			//用密钥解出随机数,再用随机数解出密文。
			byte[] ram_byte = RSAUtil.decrypt(pk, new BigInteger(en_ram,16).toByteArray());
			String ram_String = new StringBuffer().append(new String(ram_byte)).reverse().toString();
			
			byte[] temp = Base64.decode(lf.getEn_pwd().getBytes());
			de_pwd = XXTEA.decrypt(temp,ram_String.getBytes());
		} catch (Exception e) {
			e.printStackTrace();
		}
		ui.setPASSWORD(new String(de_pwd));
 

(6).相关工具类
RSAUtil.java

package com.sztelecom.vbao.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;




/*************************************************
 Copyright (C), 2008-2009, SunSoft Team Tech. Co., Ltd.
 File name:           RSAUtil.java
 Description:         RSA 工具类。提供加密,解密,生成密钥对等方法。
 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。
 Others:         
 Function List:       

 History:             

 *************************************************/

/**
 * Author: sunbeam Version: 1.0 Date: 2008-6-25
 */
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());
		}
	}

	

}

 XXTEA.java

/*************************************************
  Copyright (C), 2008-2009, SunSoft Team Tech. Co., Ltd.
  File name:           XXTEA.java
  Description:         
  Others:         
  Function List:       
    
  History:             
   
*************************************************/

package com.sztelecom.vbao.util;

import java.nio.charset.Charset;


/**
 * Author:   sunbeam    
 * Version:  1.0 
 * Date: 2008-6-24
 */
public class XXTEA {
	/**
     * Encrypt data with key.
     *
     * @param data
     * @param key
     * @return
     */
    public   static byte [] encrypt ( byte [] data , byte [] key ) {
        if   ( data . length == 0 ) {
            return   data ;
        }
        return   toByteArray ( encrypt ( toIntArray ( data , true ) , toIntArray ( key , false )) , false ) ;
    }
 
    /**
     * Decrypt data with key.
     *
     * @param data
     * @param key
     * @return
     */
    public   static byte [] decrypt ( byte [] data , byte [] key ) {
        if   ( data . length == 0 ) {
            return   data ;
        }
        return   toByteArray ( decrypt ( toIntArray ( data , false ) , toIntArray ( key , false )) , true ) ;
    }
 
    /**
     * Encrypt data with key.
     *
     * @param v
     * @param k
     * @return
     */
    public   static int [] encrypt ( int [] v , int [] k ) {
        int   n = v . length - 1 ;
        if   ( n < 1 ) {
            return   v ;
        }
        if   ( k . length < 4 ) {
            int []   key = new int [ 4 ] ;
            System . arraycopy ( k , 0 , key , 0 , k . length ) ;
            k = key ;
        }
        int   z = v [ n ] , y = v [ 0 ] , delta = 0x9E3779B9 , sum = 0 , e ;
        int   p , q = 6 + 52 / ( n + 1 ) ;
        while   ( q -- > 0 ) {
            sum = sum + delta ;
            e = sum >>> 2 & 3 ;
            for   ( p = 0 ; p < n ; p ++ ) {
                y = v [ p + 1 ] ;
                z = v [ p ] += ( z >>> 5 ^ y << 2 ) + ( y >>> 3 ^ z << 4 ) ^ ( sum ^ y ) + ( k [ p & 3 ^ e ] ^ z ) ;
            }
            y = v [ 0 ] ;
            z = v [ n ] += ( z >>> 5 ^ y << 2 ) + ( y >>> 3 ^ z << 4 ) ^ ( sum ^ y ) + ( k [ p & 3 ^ e ] ^ z ) ;
        }
        return   v ;
    }
 
    /**
     * Decrypt data with key.
     *
     * @param v
     * @param k
     * @return
     */
    public   static int [] decrypt ( int [] v , int [] k ) {
        int   n = v . length - 1 ;
        if   ( n < 1 ) {
            return   v ;
        }
        if   ( k . length < 4 ) {
            int []   key = new int [ 4 ] ;
            System . arraycopy ( k , 0 , key , 0 , k . length ) ;
            k = key ;
        }
        int   z = v [ n ] , y = v [ 0 ] , delta = 0x9E3779B9 , sum , e ;
        int   p , q = 6 + 52 / ( n + 1 ) ;
        sum = q * delta ;
        while   ( sum != 0 ) {
            e = sum >>> 2 & 3 ;
            for   ( p = n ; p > 0 ; p -- ) {
                z = v [ p - 1 ] ;
                y = v [ p ] -= ( z >>> 5 ^ y << 2 ) + ( y >>> 3 ^ z << 4 ) ^ ( sum ^ y ) + ( k [ p & 3 ^ e ] ^ z ) ;
            }
            z = v [ n ] ;
            y = v [ 0 ] -= ( z >>> 5 ^ y << 2 ) + ( y >>> 3 ^ z << 4 ) ^ ( sum ^ y ) + ( k [ p & 3 ^ e ] ^ z ) ;
            sum = sum - delta ;
        }
        return   v ;
    }
 
    /**
     * Convert byte array to int array.
     *
     * @param data
     * @param includeLength
     * @return
     */
    private   static int [] toIntArray ( byte [] data , boolean includeLength ) {
        int   n = ((( data . length & 3 ) == 0 ) ? ( data . length >>> 2 )
                : (( data . length >>> 2 ) + 1 )) ;
        int []   result ;
        if   ( includeLength ) {
            result = new   int [ n + 1 ] ;
            result [ n ] = data . length ;
        }   else {
            result = new   int [ n ] ;
        }
        n = data . length ;
        for   ( int i = 0 ; i < n ; i ++ ) {
            result [ i >>> 2 ] |= ( 0x000000ff & data [ i ]) << (( i & 3 ) << 3 ) ;
        }
        return   result ;
    }
 
    /**
     * Convert int array to byte array.
     *
     * @param data
     * @param includeLength
     * @return
     */
    private   static byte [] toByteArray ( int [] data , boolean includeLength ) {
        int   n ;
        if   ( includeLength ) {
            n = data [ data . length - 1 ] ;
        }   else {
            n = data . length << 2 ;
        }
 
        byte []   result = new byte [ n ] ;
        for   ( int i = 0 ; i < n ; i ++ ) {
            result [ i ] = ( byte )   ( data [ i >>> 2 ] >>> (( i & 3 ) << 3 )) ;
        }
        return   result ;
    }
    
   
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值