当前位置: 首页 > 工具软件 > UV-Web > 使用案例 >

RSA加密传输用户名密码--web

咸星波
2023-12-01

Web登录时存在明文传输用户名和密码的安全漏洞,要求是:web客户端通信的数据包中不能包含明文的账号和密码,建议采用RSA加密算法解决此漏洞。

1、在WEB系统中编写工具类如下:

package com.gsww.jup.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;

public class RSAUtil {
        private static String RSAKeyStore = "RSAKey.txt";
        /**
         * 生成秘钥对
         * @return
         * @throws Exception
         */
        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();  
                               
                             System.out.println(keyPair.getPrivate());  
                             System.out.println(keyPair.getPublic());  
                               
                             saveKeyPair(keyPair);  
                             return keyPair;  
                           } catch (Exception e) {  
                                     throw new Exception(e.getMessage());  
                       }  

        }
        public static KeyPair getKeyPair() throws Exception {  
                FileInputStream fis = new FileInputStream(RSAKeyStore);  
                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(RSAKeyStore);  
                ObjectOutputStream oos = new ObjectOutputStream(fos);  
                oos.writeObject(kp);  
                oos.close();  
                fos.close();  
        }  
         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());  
        }  
    }
         
         
         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());  
            }  
        }  

         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());  
        }  
    }  
         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());  
        }  
    }  
}
该类属于工具类,提供加密解密的基础方法。该类用到第三方jar包,需要在pom文件中增加如下配置:
<dependency>
     <groupId>org.bouncycastle</groupId>
     <artifactId>bcprov-jdk16</artifactId>
     <version>1.46</version>
</dependency>
2、为了较小的影响现有业务,建立单独的控制器(可以是Servlet,JSP,springmvc的Controller,struts2的action等),用于将生成公钥,私钥,并可以将公钥返回至浏览器,用于JS加密。实例代码如下:

package com.gsww.jup.controller.sys;

import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.gsww.jup.controller.BaseController;
import com.gsww.jup.entity.sys.SysOperator;
import com.gsww.jup.service.sys.SysOperatorService;
import com.gsww.jup.util.RSAUtil;

@Controller
@RequestMapping(value = "/sys/mybatis")
public class MybatisController extends BaseController{
        private static Logger logger = LoggerFactory.getLogger(MybatisController.class);
        
        @Autowired
        private SysOperatorService sysOperatorService;
        
        @RequestMapping(value = "/show", method = RequestMethod.POST)
        @ResponseBody
        public  List<SysOperator> accountView(String userAcctId,Model model,HttpServletRequest request,HttpServletResponse response)  throws Exception {                
                
                List<SysOperator> list=sysOperatorService.findList(null);
                SysOperator sysOperator=sysOperatorService.findByOperatorId("1");
                return list;
        }
        
        @RequestMapping(value = "/getkey")  
    @ResponseBody 
        public Object getPublicKey(HttpServletRequest request)throws Exception{
                 Map<String,String> result = new HashMap<String,String>(); 
                 KeyPair kp = RSAUtil.generateKeyPair();
                 RSAPublicKey pubk = (RSAPublicKey) kp.getPublic();// 生成公钥
                 RSAPrivateKey prik = (RSAPrivateKey) kp.getPrivate();// 生成私钥
                 
                 String publicKeyExponent = pubk.getPublicExponent().toString(16);// 16进制
                 String publicKeyModulus = pubk.getModulus().toString(16);// 16进制
                 request.getSession().setAttribute("prik", prik);
                 result.put("pubexponent", publicKeyExponent);
                 result.put("pubmodules", publicKeyModulus);
                 return result;  
        }

}
说明,该被需要在过滤器中忽略,因为在登录之前要调用该方法。

3、登录页面,在完成非空校验后,以Ajax的方式从后台获取公钥,并对密码进行加密,将加密的数据通过登录方法提交至后台。
以公司JUP平台为例,改造如下:

<!DOCTYPE html>
<%@ page contentType="text/html;charset=UTF-8"%>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
        <title>甘肃万维JAVA统一开发平台</title>
        <link rel="stylesheet" href="res/skin/default/css/login-jup.css">
    </head>
    <body>
        <div class="login-warp">
            <div class="main">
                <div class="logo"></div>
                <div class="login-form">
                    <form id="login-form">
                        <div class="form-title">甘肃万维JAVA统一开发平台 </div>
                        <ul>
                            <li>
                                <div class="input-content">
                                    <span class="icon uname"></span>
                                    <input type="text" class="input " id="userName" name="userName" placeholder="请输入您的帐号" value="admin">
                                </div>
                            </li>
                            <li>
                                <div class="input-content">
                                    <span class="icon upwd"></span>
                                    <input type="password" class="input " id="passWord" name="passWord" placeholder="请输入您的密码" value="111111">
                                </div>
                            </li>
                            <li>
                                <div class="input-content yzm-input">
                                    <span class="icon uyzm"></span>
                                    <input type="text" class="input input-yzm" name="kaptcha" id="kaptcha" placeholder="请输入验证码" >
                                </div>
                                <div class="input-verificationCode yzm-img">
                                    <img style="cursor:pointer;" id="kaptchaImage" src="kaptcha/image" title="看不清,点击换一张" />
                                </div>
                            </li>
                            <li>
                                <input class="input-submit" type="submit" name="" value="登录" />
                            </li>
                        </ul>
                    </form>
                </div>
            </div>
            <div class="footer">
                <p>版权所有:甘肃万维信息技术有限责任公司</p>
                <p> (c)2015 You company name.All rights reserve</p>
            </div>
        </div>
       
    <script type="text/javascript" src="res/skin/default/js/jquery-1.8.3.min.js"></script>
    <script type="text/javascript" src="res/skin/default/plugin/validate/jquery.validate.js"></script>
    <script type="text/javascript" src="res/skin/default/plugin/validate/jquery.validate.extension.js"></script>
    <script type="text/javascript" src="res/skin/default/plugin/rsa/BigInt.js"></script>
    <script type="text/javascript" src="res/skin/default/plugin/rsa/Barrett.js"></script>
    <script type="text/javascript" src="res/skin/default/plugin/rsa/RSA.js"></script>
    <script type="text/javascript">
    $(document).ready(function() {

              $(function() {
                            $("#kaptchaImage").click(function() {//生成验证码
                                    $(this).attr(
                                    'src',
                                    'kaptcha/image?'        + Math.floor(Math.random() * 100)).fadeIn();
                                    event.cancelBubble = true;
                            });
                    });
              
        $("#login-form").jupLogin();
    });



// 创建一个闭包  
(function($) {
  // 插件的定义  
  $.fn.jupLogin = function(options) {  
    
    // build main options before element iteration  
    var opts = $.extend({}, $.fn.jupLogin.defaults, options);  
    // iterate and reformat each matched element  
    return this.each(function() {  
      $this = $(this);
      init();
      $this.validate({
              debug : true ,
        onkeyup: false,
        onclick: false,
        // errorElement: "div",
        showErrors: function(errorMap, errorList) {
          showErrors(errorMap,errorList);
        },
        rules: {
          userName: "required" ,
          passWord : "required" ,
          kaptcha : "required" 
        },
        submitHandler : function(form) {
          //此处获取加密公钥
          $.post("sys/mybatis/getkey",function(data){
                  //data为获取到的公钥数据
                  var pubexponent =data.pubexponent;
                  var pubmodules =data.pubmodules;
                  setMaxDigits(200);  
                  var key = new RSAKeyPair(pubexponent, "", pubmodules); 
                  var password=$("#passWord").val();
                  var encrypedPwd = encryptedString(key, encodeURIComponent(password));  
                  $("#passWord").val(encrypedPwd);
                  var formData = {
                          "userName" : $(form).find("#userName").val() ,
                          "passWord" : $(form).find("#passWord").val() ,
                          "kaptcha" : $(form).find("#kaptcha").val()
                  };
                          //登录开始
                  $.ajax({
            url: 'login/sysLogin',
            type: 'POST',
            dataType: 'json',
            data: formData,
            success : function(data){
              //window.location.href= "sysLogin";
              if(data.ret != 0) {
                      if(data.ret == 1){
                              showErrors(
                              {userName : data.msg},
                              [{
                                      element : "input#kaptcha.input",
                                      message:data.msg
                              }]);
                      }else{
                              showErrors(
                              {userName : data.msg},
                              [{
                                      element : "input#userName.input",
                                      message:data.msg
                              }]);
                      }
                      
              }else {
                      window.location.href= "index";
              }
            }
          });
                          //登录结束
          });
        }
      });
      


    });  
  };  
  function init(){
    
  };
 
  function showErrors(errorMap, errorList) {
          $('.error-content').remove();
      $('.error').removeClass('error');
      $(errorList).each(function(index, el) {
        $(el.element).parent().addClass("error");
        var $content = $("<div  class='error-content error'> <span class='error-msg'>" + el.message +"</span></div>");
        $("body").append($content);
        $content.css('top',($(el.element).offset().top+2)).css('left',$(el.element).offset().left-$content.width()-70).show();
        shake($content,$content.offset().left);
      });
  };
  //抖动
  function shake(o,box_left){
      var $panel = $(o);
      //box_left = ($(window).width() -  $panel.width()) / 2;
      //$panel.css({'left': box_left,'position':'absolute'});
      for(var i=1; 4>=i; i++){
          $panel.animate({left:box_left-0.3*(40-10*i)},50);
          $panel.animate({left:box_left+0.3*(40-10*i)},50);
      }
  };

  // 私有函数:debugging  
  function debug($obj) {  
    if (window.console && window.console.log)  
      window.console.log('jupLogin selection count: ' + $obj.size());  
  };  
  // 定义暴露format函数  
  $.fn.jupLogin.format = function(txt) {  
    return '<strong>' + txt + '</strong>';  
  };  
  // 插件的defaults  
  $.fn.jupLogin.defaults = {  
   
  };  
  
// 闭包结束  
})(jQuery);   

    </script>
    </body>
</html>


这里,要用到三个JS文件
Barrett.js
BigInt.js
RSA.js
具体见附件;
4、提交到后台在控制器中需要做解密处理,具体如下:
/**
	 * ajax登录
	 * @return
	 * @throws Exception
	 */
	public void login4() {
		response.setContentType("text/xml;charset=UTF-8");   
		response.setHeader("Cache-Control", "no-cache");   
    	PrintWriter out = null;
    	Map<String, Object> message = new HashMap();
		
		    try {
		    	out=response.getWriter();
		    //	String session_authcode="1234";//测试环境
					//客户端验证码验证  正式环境
		    	String session_authcode =String.valueOf(session.getAttribute(Constants.SESSION_USER_PHONE_AUTHCODE));
		          
					if("".equals(session_authcode)||session_authcode==null||validateCode==null||"".equals(validateCode)){
						//out.write("短信密码不能为空,请输入正确短信密码");
						//rentJavascript("$.alert2('短信验证码不能为空,请输入短信验证码',function(){location.href='login.jsp'; });");
						//return null;
						message.put("success", false);
						message.put("message", "短信验证码不能为空,请输入短信验证码");
					}else{
						if (session_authcode.length()>0 && !session_authcode.equals(validateCode)) {
							// rentJavascript("$.alert2('短信验证码输入错误,请重新输入',function(){location.href='login.jsp'; });");
							// return null;
							 message.put("success", false);
								message.put("message", "短信验证码输入错误,请重新输入");
						}else if(validateCode.length()==0 || session_authcode.length() == 0){
						//	out.write("短信密码不能为空,请输入正确短信密码!");
							//rentJavascript("$.alert2('短信验证码不能为空,请输入正确短信验证码!',function(){location.href='login.jsp'});");
							//return null;
							 message.put("success", false);
							 message.put("message", "短信验证码不能为空,请输入正确短信验证码!");
						}else {
							
							//正常登陆
							//RSA解密
							RSAPrivateKey prik = (RSAPrivateKey) request.getSession().getAttribute("prik");
							 try{

								 byte[] en_result = new BigInteger(loginName, 16).toByteArray();  

								 byte[] de_result = RSAUtil.decrypt(prik, en_result);  

								 StringBuffer sb = new StringBuffer();  

								 sb.append(new String(de_result));  

								 loginName= sb.reverse().toString(); 
								 //loginName = java.net.URLEncoder.encode(loginName);
								 loginName = java.net.URLDecoder.decode(loginName,"UTF-8");
								 
								 byte[] en_identityCode = new BigInteger(identityCode, 16).toByteArray();  

								 byte[] de_identityCode = RSAUtil.decrypt(prik, en_identityCode);  

								 StringBuffer sb1 = new StringBuffer();  

								 sb1.append(new String(de_identityCode));  

								 identityCode= sb1.reverse().toString(); 

							} catch(Exception ex){

								ex.printStackTrace();
							}
							Bj3typePerson user=sysUserService.findBj3typePerson(loginName,identityCode);
							if (user == null) {
								//rentJavascript("$.alert2('账号错误或不存在,请重新输入!',function(){location.href='login.jsp'});");
								//return null;
								 message.put("success", false);
								 message.put("message", "账号错误或用户不存在,请重新输入!");
							}else if ("00".equals(user.getPersonType())) { //管理员登录
								//rentJavascript("$.alert2('系统将自动跳转至符合权限的登录界面',function(){location.href='admin_login.jsp'});");
							//return null;
								message.put("success", false);
								message.put("ptype", "00");
								message.put("message", "系统将自动跳转至符合权限的登录界面");
							}else{
								   session.setAttribute(Constants.SESSION_USER_TYPE, user.getPersonType());
									session.setAttribute(Constants.SESSION_USER_KEY, user.getAssessId());
									session.setAttribute(Constants.SESSION_USER_IP, request.getRemoteAddr());
									session.setAttribute(Constants.SESSION_USER_NAME, user.getName());
									session.setAttribute(Constants.SESSION_USER_MOBILE, user.getPhone());
									session.setAttribute(Constants.SESSION_USER_SETID,"297e672589a9e5210159a9f5999c0032");
									session.setAttribute(Constants.SESSION_IDENTITY_CODE, user.getIdentityCode());
									/*UserLoginLog loginLog=new UserLoginLog();
									loginLog.setLoginTime(TimeHelper.getCurrentTime());
									loginLog.setUserId(user.getAssessId());
									loginLog.setLoginType(Constants.PC_LOGIN);
									userLoginLogService.saveLoginLog(loginLog);*/
									OnLineUser onLineUser = (OnLineUser) ctx.getApplication().get("onlineuser");
									if (onLineUser==null){
										onLineUser=new OnLineUser();
									}
									onLineUser.addUser(user.getAssessId());
									ctx.getApplication().put("onlineuser", onLineUser);
									onlineService.onlinUserUpdate(user.getAssessId(), request.getRemoteAddr());
									//rentJavascript("function(){location.href='main/main.jsp'});");
									message.put("success", true);
									//return "login";
								
							}
						}
						
					}
						
			
			} catch (Exception e) {
				e.printStackTrace();
				//rentJavascript("alert('登录失败,请重新登录!');location.href='login.jsp';");
				//out.write("登录失败,请重新登录!");
				try {
					out=response.getWriter();
					 message.put("success", false);
					 message.put("message", "操作异常,请联系管理员");
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				//return null;
			}finally{
				
				out.println(JSONUtil.writeMapJSON(message));
				out.flush();
				out.close();
			}
		
		
	}

附件:前面用到的3个js(Barrett.js、BigInt.js、RSA.js),CSDN博客不能上传附件,很无奈……只能把代码直接粘这里了

Barrett.js

function BarrettMu(m)
{
	this.modulus = biCopy(m);
	this.k = biHighIndex(this.modulus) + 1;
	var b2k = new BigInt();
	b2k.digits[2 * this.k] = 1; // b2k = b^(2k)
	this.mu = biDivide(b2k, this.modulus);
	this.bkplus1 = new BigInt();
	this.bkplus1.digits[this.k + 1] = 1; // bkplus1 = b^(k+1)
	this.modulo = BarrettMu_modulo;
	this.multiplyMod = BarrettMu_multiplyMod;
	this.powMod = BarrettMu_powMod;
}

function BarrettMu_modulo(x)
{
	var q1 = biDivideByRadixPower(x, this.k - 1);
	var q2 = biMultiply(q1, this.mu);
	var q3 = biDivideByRadixPower(q2, this.k + 1);
	var r1 = biModuloByRadixPower(x, this.k + 1);
	var r2term = biMultiply(q3, this.modulus);
	var r2 = biModuloByRadixPower(r2term, this.k + 1);
	var r = biSubtract(r1, r2);
	if (r.isNeg) {
		r = biAdd(r, this.bkplus1);
	}
	var rgtem = biCompare(r, this.modulus) >= 0;
	while (rgtem) {
		r = biSubtract(r, this.modulus);
		rgtem = biCompare(r, this.modulus) >= 0;
	}
	return r;
}

function BarrettMu_multiplyMod(x, y)
{
	/*
	x = this.modulo(x);
	y = this.modulo(y);
	*/
	var xy = biMultiply(x, y);
	return this.modulo(xy);
}

function BarrettMu_powMod(x, y)
{
	var result = new BigInt();
	result.digits[0] = 1;
	var a = x;
	var k = y;
	while (true) {
		if ((k.digits[0] & 1) != 0) result = this.multiplyMod(result, a);
		k = biShiftRight(k, 1);
		if (k.digits[0] == 0 && biHighIndex(k) == 0) break;
		a = this.multiplyMod(a, a);
	}
	return result;
}

BigInt.js

// BigInt, a suite of routines for performing multiple-precision arithmetic in
// JavaScript.
//
// Copyright 1998-2005 David Shapiro.
//
// You may use, re-use, abuse,
// copy, and modify this code to your liking, but please keep this header.
// Thanks!
//
// Dave Shapiro
// dave@ohdave.com

// IMPORTANT THING: Be sure to set maxDigits according to your precision
// needs. Use the setMaxDigits() function to do this. See comments below.
//
// Tweaked by Ian Bunning
// Alterations:
// Fix bug in function biFromHex(s) to allow
// parsing of strings of length != 0 (mod 4)

// Changes made by Dave Shapiro as of 12/30/2004:
//
// The BigInt() constructor doesn't take a string anymore. If you want to
// create a BigInt from a string, use biFromDecimal() for base-10
// representations, biFromHex() for base-16 representations, or
// biFromString() for base-2-to-36 representations.
//
// biFromArray() has been removed. Use biCopy() instead, passing a BigInt
// instead of an array.
//
// The BigInt() constructor now only constructs a zeroed-out array.
// Alternatively, if you pass <true>, it won't construct any array. See the
// biCopy() method for an example of this.
//
// Be sure to set maxDigits depending on your precision needs. The default
// zeroed-out array ZERO_ARRAY is constructed inside the setMaxDigits()
// function. So use this function to set the variable. DON'T JUST SET THE
// VALUE. USE THE FUNCTION.
//
// ZERO_ARRAY exists to hopefully speed up construction of BigInts(). By
// precalculating the zero array, we can just use slice(0) to make copies of
// it. Presumably this calls faster native code, as opposed to setting the
// elements one at a time. I have not done any timing tests to verify this
// claim.

// Max number = 10^16 - 2 = 9999999999999998;
//               2^53     = 9007199254740992;

var biRadixBase = 2;
var biRadixBits = 16;
var bitsPerDigit = biRadixBits;
var biRadix = 1 << 16; // = 2^16 = 65536
var biHalfRadix = biRadix >>> 1;
var biRadixSquared = biRadix * biRadix;
var maxDigitVal = biRadix - 1;
var maxInteger = 9999999999999998; 

// maxDigits:
// Change this to accommodate your largest number size. Use setMaxDigits()
// to change it!
//
// In general, if you're working with numbers of size N bits, you'll need 2*N
// bits of storage. Each digit holds 16 bits. So, a 1024-bit key will need
//
// 1024 * 2 / 16 = 128 digits of storage.
//

var maxDigits;
var ZERO_ARRAY;
var bigZero, bigOne;

function setMaxDigits(value)
{
	maxDigits = value;
	ZERO_ARRAY = new Array(maxDigits);
	for (var iza = 0; iza < ZERO_ARRAY.length; iza++) ZERO_ARRAY[iza] = 0;
	bigZero = new BigInt();
	bigOne = new BigInt();
	bigOne.digits[0] = 1;
}

setMaxDigits(20);

// The maximum number of digits in base 10 you can convert to an
// integer without JavaScript throwing up on you.
var dpl10 = 15;
// lr10 = 10 ^ dpl10
var lr10 = biFromNumber(1000000000000000);

function BigInt(flag)
{
	if (typeof flag == "boolean" && flag == true) {
		this.digits = null;
	}
	else {
		this.digits = ZERO_ARRAY.slice(0);
	}
	this.isNeg = false;
}

function biFromDecimal(s)
{
	var isNeg = s.charAt(0) == '-';
	var i = isNeg ? 1 : 0;
	var result;
	// Skip leading zeros.
	while (i < s.length && s.charAt(i) == '0') ++i;
	if (i == s.length) {
		result = new BigInt();
	}
	else {
		var digitCount = s.length - i;
		var fgl = digitCount % dpl10;
		if (fgl == 0) fgl = dpl10;
		result = biFromNumber(Number(s.substr(i, fgl)));
		i += fgl;
		while (i < s.length) {
			result = biAdd(biMultiply(result, lr10),
			               biFromNumber(Number(s.substr(i, dpl10))));
			i += dpl10;
		}
		result.isNeg = isNeg;
	}
	return result;
}

function biCopy(bi)
{
	var result = new BigInt(true);
	result.digits = bi.digits.slice(0);
	result.isNeg = bi.isNeg;
	return result;
}

function biFromNumber(i)
{
	var result = new BigInt();
	result.isNeg = i < 0;
	i = Math.abs(i);
	var j = 0;
	while (i > 0) {
		result.digits[j++] = i & maxDigitVal;
		i = Math.floor(i / biRadix);
	}
	return result;
}

function reverseStr(s)
{
	var result = "";
	for (var i = s.length - 1; i > -1; --i) {
		result += s.charAt(i);
	}
	return result;
}

var hexatrigesimalToChar = new Array(
 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
 'u', 'v', 'w', 'x', 'y', 'z'
);

function biToString(x, radix)
	// 2 <= radix <= 36
{
	var b = new BigInt();
	b.digits[0] = radix;
	var qr = biDivideModulo(x, b);
	var result = hexatrigesimalToChar[qr[1].digits[0]];
	while (biCompare(qr[0], bigZero) == 1) {
		qr = biDivideModulo(qr[0], b);
		digit = qr[1].digits[0];
		result += hexatrigesimalToChar[qr[1].digits[0]];
	}
	return (x.isNeg ? "-" : "") + reverseStr(result);
}

function biToDecimal(x)
{
	var b = new BigInt();
	b.digits[0] = 10;
	var qr = biDivideModulo(x, b);
	var result = String(qr[1].digits[0]);
	while (biCompare(qr[0], bigZero) == 1) {
		qr = biDivideModulo(qr[0], b);
		result += String(qr[1].digits[0]);
	}
	return (x.isNeg ? "-" : "") + reverseStr(result);
}

var hexToChar = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                          'a', 'b', 'c', 'd', 'e', 'f');

function digitToHex(n)
{
	var mask = 0xf;
	var result = "";
	for (i = 0; i < 4; ++i) {
		result += hexToChar[n & mask];
		n >>>= 4;
	}
	return reverseStr(result);
}

function biToHex(x)
{
	var result = "";
	var n = biHighIndex(x);
	for (var i = biHighIndex(x); i > -1; --i) {
		result += digitToHex(x.digits[i]);
	}
	return result;
}

function charToHex(c)
{
	var ZERO = 48;
	var NINE = ZERO + 9;
	var littleA = 97;
	var littleZ = littleA + 25;
	var bigA = 65;
	var bigZ = 65 + 25;
	var result;

	if (c >= ZERO && c <= NINE) {
		result = c - ZERO;
	} else if (c >= bigA && c <= bigZ) {
		result = 10 + c - bigA;
	} else if (c >= littleA && c <= littleZ) {
		result = 10 + c - littleA;
	} else {
		result = 0;
	}
	return result;
}

function hexToDigit(s)
{
	var result = 0;
	var sl = Math.min(s.length, 4);
	for (var i = 0; i < sl; ++i) {
		result <<= 4;
		result |= charToHex(s.charCodeAt(i))
	}
	return result;
}

function biFromHex(s)
{
	var result = new BigInt();
	var sl = s.length;
	for (var i = sl, j = 0; i > 0; i -= 4, ++j) {
		result.digits[j] = hexToDigit(s.substr(Math.max(i - 4, 0), Math.min(i, 4)));
	}
	return result;
}

function biFromString(s, radix)
{
	var isNeg = s.charAt(0) == '-';
	var istop = isNeg ? 1 : 0;
	var result = new BigInt();
	var place = new BigInt();
	place.digits[0] = 1; // radix^0
	for (var i = s.length - 1; i >= istop; i--) {
		var c = s.charCodeAt(i);
		var digit = charToHex(c);
		var biDigit = biMultiplyDigit(place, digit);
		result = biAdd(result, biDigit);
		place = biMultiplyDigit(place, radix);
	}
	result.isNeg = isNeg;
	return result;
}

function biDump(b)
{
	return (b.isNeg ? "-" : "") + b.digits.join(" ");
}

function biAdd(x, y)
{
	var result;

	if (x.isNeg != y.isNeg) {
		y.isNeg = !y.isNeg;
		result = biSubtract(x, y);
		y.isNeg = !y.isNeg;
	}
	else {
		result = new BigInt();
		var c = 0;
		var n;
		for (var i = 0; i < x.digits.length; ++i) {
			n = x.digits[i] + y.digits[i] + c;
			result.digits[i] = n % biRadix;
			c = Number(n >= biRadix);
		}
		result.isNeg = x.isNeg;
	}
	return result;
}

function biSubtract(x, y)
{
	var result;
	if (x.isNeg != y.isNeg) {
		y.isNeg = !y.isNeg;
		result = biAdd(x, y);
		y.isNeg = !y.isNeg;
	} else {
		result = new BigInt();
		var n, c;
		c = 0;
		for (var i = 0; i < x.digits.length; ++i) {
			n = x.digits[i] - y.digits[i] + c;
			result.digits[i] = n % biRadix;
			// Stupid non-conforming modulus operation.
			if (result.digits[i] < 0) result.digits[i] += biRadix;
			c = 0 - Number(n < 0);
		}
		// Fix up the negative sign, if any.
		if (c == -1) {
			c = 0;
			for (var i = 0; i < x.digits.length; ++i) {
				n = 0 - result.digits[i] + c;
				result.digits[i] = n % biRadix;
				// Stupid non-conforming modulus operation.
				if (result.digits[i] < 0) result.digits[i] += biRadix;
				c = 0 - Number(n < 0);
			}
			// Result is opposite sign of arguments.
			result.isNeg = !x.isNeg;
		} else {
			// Result is same sign.
			result.isNeg = x.isNeg;
		}
	}
	return result;
}


function biHighIndex(x)
{
	var result = x.digits.length - 1;
	while (result > 0 && x.digits[result] == 0) --result;
	return result;
}

function biNumBits(x)
{
	var n = biHighIndex(x);
	var d = x.digits[n];
	var m = (n + 1) * bitsPerDigit;
	var result;
	for (result = m; result > m - bitsPerDigit; --result) {
		if ((d & 0x8000) != 0) break;
		d <<= 1;
	}
	return result;
}

function biMultiply(x, y)
{
	var result = new BigInt();
	var c;
	var n = biHighIndex(x);
	var t = biHighIndex(y);
	var u, uv, k;

	for (var i = 0; i <= t; ++i) {
		c = 0;
		k = i;
		for (j = 0; j <= n; ++j, ++k) {
			uv = result.digits[k] + x.digits[j] * y.digits[i] + c;
			result.digits[k] = uv & maxDigitVal;
			c = uv >>> biRadixBits;
			//c = Math.floor(uv / biRadix);
		}
		result.digits[i + n + 1] = c;
	}
	// Someone give me a logical xor, please.
	result.isNeg = x.isNeg != y.isNeg;
	return result;
}

function biMultiplyDigit(x, y)
{
	var n, c, uv;

	result = new BigInt();
	n = biHighIndex(x);
	c = 0;
	for (var j = 0; j <= n; ++j) {
		uv = result.digits[j] + x.digits[j] * y + c;
		result.digits[j] = uv & maxDigitVal;
		c = uv >>> biRadixBits;
		//c = Math.floor(uv / biRadix);
	}
	result.digits[1 + n] = c;
	return result;
}

function arrayCopy(src, srcStart, dest, destStart, n)
{
	var m = Math.min(srcStart + n, src.length);
	for (var i = srcStart, j = destStart; i < m; ++i, ++j) {
		dest[j] = src[i];
	}
}

var highBitMasks = new Array(0x0000, 0x8000, 0xC000, 0xE000, 0xF000, 0xF800,
                             0xFC00, 0xFE00, 0xFF00, 0xFF80, 0xFFC0, 0xFFE0,
                             0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE, 0xFFFF);

function biShiftLeft(x, n)
{
	var digitCount = Math.floor(n / bitsPerDigit);
	var result = new BigInt();
	arrayCopy(x.digits, 0, result.digits, digitCount,
	          result.digits.length - digitCount);
	var bits = n % bitsPerDigit;
	var rightBits = bitsPerDigit - bits;
	for (var i = result.digits.length - 1, i1 = i - 1; i > 0; --i, --i1) {
		result.digits[i] = ((result.digits[i] << bits) & maxDigitVal) |
		                   ((result.digits[i1] & highBitMasks[bits]) >>>
		                    (rightBits));
	}
	result.digits[0] = ((result.digits[i] << bits) & maxDigitVal);
	result.isNeg = x.isNeg;
	return result;
}

var lowBitMasks = new Array(0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F,
                            0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF,
                            0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF);

function biShiftRight(x, n)
{
	var digitCount = Math.floor(n / bitsPerDigit);
	var result = new BigInt();
	arrayCopy(x.digits, digitCount, result.digits, 0,
	          x.digits.length - digitCount);
	var bits = n % bitsPerDigit;
	var leftBits = bitsPerDigit - bits;
	for (var i = 0, i1 = i + 1; i < result.digits.length - 1; ++i, ++i1) {
		result.digits[i] = (result.digits[i] >>> bits) |
		                   ((result.digits[i1] & lowBitMasks[bits]) << leftBits);
	}
	result.digits[result.digits.length - 1] >>>= bits;
	result.isNeg = x.isNeg;
	return result;
}

function biMultiplyByRadixPower(x, n)
{
	var result = new BigInt();
	arrayCopy(x.digits, 0, result.digits, n, result.digits.length - n);
	return result;
}

function biDivideByRadixPower(x, n)
{
	var result = new BigInt();
	arrayCopy(x.digits, n, result.digits, 0, result.digits.length - n);
	return result;
}

function biModuloByRadixPower(x, n)
{
	var result = new BigInt();
	arrayCopy(x.digits, 0, result.digits, 0, n);
	return result;
}

function biCompare(x, y)
{
	if (x.isNeg != y.isNeg) {
		return 1 - 2 * Number(x.isNeg);
	}
	for (var i = x.digits.length - 1; i >= 0; --i) {
		if (x.digits[i] != y.digits[i]) {
			if (x.isNeg) {
				return 1 - 2 * Number(x.digits[i] > y.digits[i]);
			} else {
				return 1 - 2 * Number(x.digits[i] < y.digits[i]);
			}
		}
	}
	return 0;
}

function biDivideModulo(x, y)
{
	var nb = biNumBits(x);
	var tb = biNumBits(y);
	var origYIsNeg = y.isNeg;
	var q, r;
	if (nb < tb) {
		// |x| < |y|
		if (x.isNeg) {
			q = biCopy(bigOne);
			q.isNeg = !y.isNeg;
			x.isNeg = false;
			y.isNeg = false;
			r = biSubtract(y, x);
			// Restore signs, 'cause they're references.
			x.isNeg = true;
			y.isNeg = origYIsNeg;
		} else {
			q = new BigInt();
			r = biCopy(x);
		}
		return new Array(q, r);
	}

	q = new BigInt();
	r = x;

	// Normalize Y.
	var t = Math.ceil(tb / bitsPerDigit) - 1;
	var lambda = 0;
	while (y.digits[t] < biHalfRadix) {
		y = biShiftLeft(y, 1);
		++lambda;
		++tb;
		t = Math.ceil(tb / bitsPerDigit) - 1;
	}
	// Shift r over to keep the quotient constant. We'll shift the
	// remainder back at the end.
	r = biShiftLeft(r, lambda);
	nb += lambda; // Update the bit count for x.
	var n = Math.ceil(nb / bitsPerDigit) - 1;

	var b = biMultiplyByRadixPower(y, n - t);
	while (biCompare(r, b) != -1) {
		++q.digits[n - t];
		r = biSubtract(r, b);
	}
	for (var i = n; i > t; --i) {
    var ri = (i >= r.digits.length) ? 0 : r.digits[i];
    var ri1 = (i - 1 >= r.digits.length) ? 0 : r.digits[i - 1];
    var ri2 = (i - 2 >= r.digits.length) ? 0 : r.digits[i - 2];
    var yt = (t >= y.digits.length) ? 0 : y.digits[t];
    var yt1 = (t - 1 >= y.digits.length) ? 0 : y.digits[t - 1];
		if (ri == yt) {
			q.digits[i - t - 1] = maxDigitVal;
		} else {
			q.digits[i - t - 1] = Math.floor((ri * biRadix + ri1) / yt);
		}

		var c1 = q.digits[i - t - 1] * ((yt * biRadix) + yt1);
		var c2 = (ri * biRadixSquared) + ((ri1 * biRadix) + ri2);
		while (c1 > c2) {
			--q.digits[i - t - 1];
			c1 = q.digits[i - t - 1] * ((yt * biRadix) | yt1);
			c2 = (ri * biRadix * biRadix) + ((ri1 * biRadix) + ri2);
		}

		b = biMultiplyByRadixPower(y, i - t - 1);
		r = biSubtract(r, biMultiplyDigit(b, q.digits[i - t - 1]));
		if (r.isNeg) {
			r = biAdd(r, b);
			--q.digits[i - t - 1];
		}
	}
	r = biShiftRight(r, lambda);
	// Fiddle with the signs and stuff to make sure that 0 <= r < y.
	q.isNeg = x.isNeg != origYIsNeg;
	if (x.isNeg) {
		if (origYIsNeg) {
			q = biAdd(q, bigOne);
		} else {
			q = biSubtract(q, bigOne);
		}
		y = biShiftRight(y, lambda);
		r = biSubtract(y, r);
	}
	// Check for the unbelievably stupid degenerate case of r == -0.
	if (r.digits[0] == 0 && biHighIndex(r) == 0) r.isNeg = false;

	return new Array(q, r);
}

function biDivide(x, y)
{
	return biDivideModulo(x, y)[0];
}

function biModulo(x, y)
{
	return biDivideModulo(x, y)[1];
}

function biMultiplyMod(x, y, m)
{
	return biModulo(biMultiply(x, y), m);
}

function biPow(x, y)
{
	var result = bigOne;
	var a = x;
	while (true) {
		if ((y & 1) != 0) result = biMultiply(result, a);
		y >>= 1;
		if (y == 0) break;
		a = biMultiply(a, a);
	}
	return result;
}

function biPowMod(x, y, m)
{
	var result = bigOne;
	var a = x;
	var k = y;
	while (true) {
		if ((k.digits[0] & 1) != 0) result = biMultiplyMod(result, a, m);
		k = biShiftRight(k, 1);
		if (k.digits[0] == 0 && biHighIndex(k) == 0) break;
		a = biMultiplyMod(a, a, m);
	}
	return result;
}

RSA.js

// RSA, a suite of routines for performing RSA public-key computations in
// JavaScript.
//
// Requires BigInt.js and Barrett.js.
//
// Copyright 1998-2005 David Shapiro.
//
// You may use, re-use, abuse, copy, and modify this code to your liking, but
// please keep this header.
//
// Thanks!
// 
// Dave Shapiro
// dave@ohdave.com 

function RSAKeyPair(encryptionExponent, decryptionExponent, modulus)
{
	this.e = biFromHex(encryptionExponent);
	this.d = biFromHex(decryptionExponent);
	this.m = biFromHex(modulus);
	// We can do two bytes per digit, so
	// chunkSize = 2 * (number of digits in modulus - 1).
	// Since biHighIndex returns the high index, not the number of digits, 1 has
	// already been subtracted.
	this.chunkSize = 2 * biHighIndex(this.m);
	this.radix = 16;
	this.barrett = new BarrettMu(this.m);
}

function twoDigit(n)
{
	return (n < 10 ? "0" : "") + String(n);
}

function encryptedString(key, s)
	// Altered by Rob Saunders (rob@robsaunders.net). New routine pads the
	// string after it has been converted to an array. This fixes an
	// incompatibility with Flash MX's ActionScript.
{
	var a = new Array();
	var sl = s.length;
	var i = 0;
	while (i < sl) {
		a[i] = s.charCodeAt(i);
		i++;
	}

	while (a.length % key.chunkSize != 0) {
		a[i++] = 0;
	}

	var al = a.length;
	var result = "";
	var j, k, block;
	for (i = 0; i < al; i += key.chunkSize) {
		block = new BigInt();
		j = 0;
		for (k = i; k < i + key.chunkSize; ++j) {
			block.digits[j] = a[k++];
			block.digits[j] += a[k++] << 8;
		}
		var crypt = key.barrett.powMod(block, key.e);
		var text = key.radix == 16 ? biToHex(crypt) : biToString(crypt, key.radix);
		result += text + " ";
	}
	return result.substring(0, result.length - 1); // Remove last space.
}

function decryptedString(key, s)
{
	var blocks = s.split(" ");
	var result = "";
	var i, j, block;
	for (i = 0; i < blocks.length; ++i) {
		var bi;
		if (key.radix == 16) {
			bi = biFromHex(blocks[i]);
		}
		else {
			bi = biFromString(blocks[i], key.radix);
		}
		block = key.barrett.powMod(bi, key.d);
		for (j = 0; j <= biHighIndex(block); ++j) {
			result += String.fromCharCode(block.digits[j] & 255,
			                              block.digits[j] >> 8);
		}
	}
	// Remove trailing null, if any.
	if (result.charCodeAt(result.length - 1) == 0) {
		result = result.substring(0, result.length - 1);
	}
	return result;
}





 类似资料: