RSA之JS于JAVA交互,废话不多上代码!

package org.boyz.rsa.rsajs;

/**
 * Created by on 2015/1/6.
 */

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
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;

/**
 * RSA 工具类。提供加密,解密,生成密钥对等方法。
 * 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。
 * RSA工具类,主要针对RSA.js使用
 * 用法:
 * 1、使用generateKeyPair()方法生成密钥文件
 * 2、公钥信息给客户端使用
 * 3、私钥信息给服务端使用
 * 4、客户端公钥加密传给服务端,服务端私钥解密。
 * 5、服务端私钥加密传给客户端,客户端公钥解密。
 * 6、私钥在任何情况下不能暴露于客户端。
 */
public class RSAUtil {
    // 生成密钥文件
    private static String RSAKeyStore = "C:/RSAKey.txt";
    /**
     * 生成密钥
     * @return
     * @throws Exception
     */
    public static KeyPair generateKeyPair() throws Exception {
        try {
            KeyPairGenerator keyPairGen =
                    KeyPairGenerator.getInstance("RSA",new BouncyCastleProvider());
            final int KEY_SIZE = 512; // 密钥大小
            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());
        }
    }

    /**
     * 实际使用中不会经常生成密钥,而是获取密钥。密钥可以定期更换。
     * @return
     * @throws Exception
     */
    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;
    }

    /**
     *
     * @param kp
     * @throws Exception
     */
    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();
    }

    /**
     * 生成公钥
     * @param modulus
     * @param publicExponent
     * @return
     * @throws Exception
     */
    public static RSAPublicKey generateRSAPublicKey(byte[] modulus,
                                                    byte[] publicExponent) throws Exception {
        KeyFactory keyFac = null;
        try {
            keyFac = KeyFactory.getInstance("RSA",new 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
     * @throws Exception
     */
    public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus,
                                                      byte[] privateExponent) throws Exception {
        KeyFactory keyFac = null;
        try {
            keyFac = KeyFactory.getInstance("RSA",
                    new 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 pk
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("RSA",
                    new 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 pk
     * @param raw
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {
        try {
            Cipher cipher =
                    Cipher.getInstance("RSA",new 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 pk
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(PrivateKey pk, byte[] data) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("RSA",
                    new 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 pk
     * @param raw
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(PublicKey pk, byte[] raw) throws Exception {
        try {
            Cipher cipher =
                    Cipher.getInstance("RSA",new 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));
    }
	/* 密钥信息
RSA Private CRT Key
            modulus: c1d48e176abd94cd1c575771f89d60932afaa98f6c0ba90667a199f5ca6306ece35695b55cac25583ce9161bcc996f0b329a6c7885c92377f119220fbe9d6c2f
    public exponent: 10001
   private exponent: 252361da50c464576c7fbbac85b339c6d8ec5042bfb3f83dd6eb5ac18276b8e3a2d0e87c6d5eb8c4dcbffc4c98be1a18d4796ed13191a9964dc364c136286f41
             primeP: f78c87c6a7127354f8dcd182e9ae0265ac51fc96a78629352de32a96f4c4e8e1
             primeQ: c8728c0f69c1efc0d05ac705872f220d433449e8ab6b5739f2bd395eeb49a70f
     primeExponentP: 137e8c9eb73f7bb7a0557b664cd2b83b9b836559d3dd7bd74542d372c9d9cbe1
     primeExponentQ: ec0574c5f1515a6d3ee8a4cfed8da21adbb7060fe148533cf885b6b7fd748c3
     crtCoefficient: a5a57bbd95a9b8fec4dcb53102accb4f531980499d9499e9b66b965a050db345

RSA Public Key
            modulus: c1d48e176abd94cd1c575771f89d60932afaa98f6c0ba90667a199f5ca6306ece35695b55cac25583ce9161bcc996f0b329a6c7885c92377f119220fbe9d6c2f
    public exponent: 10001

hello world
	 */



}


package org.boyz.rsa.rsajs;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

/**
 * Created by boyszh1982@163.com on 2015/1/6.
 * 针对 /rsa/Barrett.js
 *      /rsa/BigInt.js
 *      /rsa/RSA.js
 * 使用的RSA工具类
 */
public class RSAUtilForJS {

    public static void main(String[] args) throws Exception {
        String msg = "你好测试你好测试你好测试1111你好测试你好测试你好测试1111111111你好测试你好测试你好测试1111你好测试你好测试你好测试1111111111";
        String demsg = RSAUtilForJS.encryptToRsaJS(msg);
        System.out.println(demsg);
    }

    /**
     * 私钥解密RSA.js用公钥加密的数据
     * 应用于服务器端
     * @param input
     * @throws Exception
     */
    public static String decyrptFromRsaJS(String input) throws Exception {
        System.out.println("原始信息:" + input );
        String msg = "" ;
        String[] results = input.split(" ");
        for(int i=0 ;i<results.length ;i++) {
            String result = results[i];
            System.out.println("解密前["+i+"]=" + result);
            //byte[] en_result = new BigInteger(result, 16).toByteArray(); // 此方法有bug采用hexStringToBytes
            byte[] en_result = hexStringToBytes(result);
            byte[] de_result = RSAUtil.decrypt(RSAUtil.getKeyPair().getPrivate(), en_result);
            StringBuffer sbuff = new StringBuffer();
            sbuff.append(new String(de_result));
            String detmp = sbuff.reverse().toString();
            System.out.println("解密后["+i+"]="+detmp);
            msg += detmp;
        }
        System.out.println("DECODE前=" + msg);
        msg = URLDecoder.decode(msg, "UTF-8");
        System.out.println("DECODE后=" + msg);
        return msg ;
    }
    /**
     * 私钥加密数据,针对RSA.js。
     * 应用于服务器端
     * @param input
     * @throws Exception
     */
    public static String encryptToRsaJS(String input) throws Exception {
        System.out.println("原始信息:" + input );
        input = URLEncoder.encode(input, "UTF-8");
        System.out.println("URLEncoder:" + input );
        // 将input拆成长度为62的String加密,在JS中各个解密在拼接后使用decodeURIComponent
        // 62的取值详见RSA.js中this.chunkSize信息
        int splitLen = 62;
        int splitCnt = input.length() / splitLen ;
        java.util.ArrayList<String> splitArr = new java.util.ArrayList<String>();
        //循环处理62整数倍
        for(int i=0 ; i < splitCnt ; i++){
            String tmp = input.substring(splitLen * i , splitLen * (i + 1) );
            tmp = new StringBuffer().append(tmp).reverse().toString();
            tmp = toHexString(tmp);
            byte[] t = RSAUtil.encrypt(RSAUtil.getKeyPair().getPrivate() , hexStringToBytes(tmp)); // 私钥加密
            String hexStr = byte2hex(t);
            splitArr.add(hexStr);
        }
        //单独处理剩余字符串处理
        int splitRem = input.length()%splitLen;
        if( splitRem != 0 ){
            String tmp = input.substring(input.length() - splitRem);
            tmp = new StringBuffer().append(tmp).reverse().toString();
            tmp = toHexString(tmp);
            byte[] t = RSAUtil.encrypt(RSAUtil.getKeyPair().getPrivate() , hexStringToBytes(tmp)); // 私钥加密
            String hexStr = byte2hex(t);
            splitArr.add(hexStr);
        }
        // 打印测试
        String output = "";
        for(String str : splitArr){
            //System.out.print(str);
            //System.out.print(" ");
            output += str+" " ;
        }
        return output.substring(0, output.length() - 1);
    }

    /**
     * java字节码转字符串
     * @param b
     * @return
     */
    public static String byte2hex(byte[] b) { //一个字节的数,
        // 转成16进制字符串
        String hs = "";
        String tmp = "";
        for (int n = 0; n < b.length; n++) {
            //整数转成十六进制表示
            tmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
            if (tmp.length() == 1) {
                hs = hs + "0" + tmp;
            } else {
                hs = hs + tmp;
            }
        }
        tmp = null;
        //return hs.toUpperCase(); //转成大写 JS 端不需要转成大写
        return hs;
    }

    /**
     * 字符串转java字节码
     * @param b
     * @return
     */
    public static byte[] hex2byte(byte[] b) {
        if ((b.length % 2) != 0) {
            throw new IllegalArgumentException("长度不是偶数");
        }
        byte[] b2 = new byte[b.length / 2];
        for (int n = 0; n < b.length; n += 2) {
            String item = new String(b, n, 2);
            // 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个进制字节
            b2[n / 2] = (byte) Integer.parseInt(item, 16);
        }
        b = null;
        return b2;
    }

    public static String toHexString(String s) {
        String str = "";
        for (int i = 0; i < s.length(); i++) {
            int ch = (int) s.charAt(i);
            String s4 = Integer.toHexString(ch);
            str = str + s4;
        }
        return str;
    }

    /**
     * 16进制 To byte[]
     * @param hexString
     * @return byte[]
     */
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }
    /**
     * Convert char to byte
     * @param c char
     * @return byte
     */
    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }
}


<%--
  Created by IntelliJ IDEA.
  User: boyszh1982@163.com
  Date: 2015/1/6
  Time: 16:32
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title></title>
    <script type="text/javascript" src="RSA.js?version=<%=Math.random() %>"></script>
    <script type="text/javascript" src="BigInt.js?version=<%=Math.random() %>"></script>
    <script type="text/javascript" src="Barrett.js?version=<%=Math.random() %>"></script>
</head>
<body>
<p>原始数据:<div id="msg1"></div></p>
<p>转码数据:<div id="msg2"></div></p>
<p>加密数据:<div id="msg3"></div></p>
<input type="hidden" id="encryptMsg" name="encryptMsg" value=""/>
<input type="button" οnclick="encrypt()" value="encrypt">
<input type="button" οnclick="postEncryptMsg()" value="encrypt">
<script>
    function postEncryptMsg(){
        var encryptMsg = document.getElementById("encryptMsg").value;
        window.location.href="rsa_demo_2.jsp?msg="+encryptMsg;
    }
    function encrypt(){
        // rsa.js 公钥加密
        setMaxDigits(130); // 设置数值数据的长度,要在new RSAKeyPair()之前使用
        var modulus = "c1d48e176abd94cd1c575771f89d60932afaa98f6c0ba90667a199f5ca6306ece35695b55cac25583ce9161bcc996f0b329a6c7885c92377f119220fbe9d6c2f";
        var publicExponent = "10001"; // 公钥指数 即是客户端加密指数即也是客户端解密指数
        var encryptPublicKey = new RSAKeyPair(publicExponent,"",modulus); // 加密公钥
        var msg = "公钥指数 即是客户端";
        document.getElementById("msg1").innerHTML=msg;
        //print("原始数据:"+msg);
        msg = encodeURIComponent(msg);
        document.getElementById("msg2").innerHTML=msg;
        //print("转码数据:"+msg);
        msg = encryptedString(encryptPublicKey,msg);
        document.getElementById("msg3").innerHTML=msg;
        //print("加密数据:"+msg);

        document.getElementById("encryptMsg").value= msg ;
    }
    function print(msg){
        document.write(msg+"<br/>")
    }

</script>

</body>
</html>

<%--
  Created by IntelliJ IDEA.
  User: boyszh1982@163.com
  Date: 2015/1/6
  Time: 16:32
  To change this template use File | Settings | File Templates.
--%>
<%@ page import="org.boyz.rsa.rsajs.RSAUtilForJS" %>
<%@ page import="java.net.URLEncoder" %>
<%@ page import="java.net.URLDecoder" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<p>JS加密 -> JAVA解密 -> 结果</p>
<%
    String msg = request.getParameter("msg");
    out.print("传入信息:"+msg+"<br/>");
    msg = URLDecoder.decode(msg,"UTF-8");
    out.print("转码数据:"+msg+"<br/>");
    msg = RSAUtilForJS.decyrptFromRsaJS(msg);
    out.print("解密数据:"+msg+"<br/>");
%>
<hr/>
<p>JAVA加密 -> JS解密 -> 举例</p>
<%
    String msg2 = "梳理2014年境外媒体、外国政要学者对中国的关注热词,也正是世界读中国的一扇扇窗口。";
    out.print("原始信息:"+msg2+"<br/>");
    //msg2 = URLEncoder.encode(msg2,"UTF-8"); 此处注意,URL跳转已经转码不要再转码
    //out.print("转码信息:"+msg2+"<br/>");
    msg2 = RSAUtilForJS.encryptToRsaJS(msg2);
    out.print("加密信息:"+msg2+"<br/>");
%>
<input type="button" value="JS解密" οnclick="decryptRsaJS()" />
<script>
    function decryptRsaJS(){
        window.location.href = "rsa_demo_3.jsp?msg2=<%=msg2 %>";
    }

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

<%--
  Created by IntelliJ IDEA.
  User: boyszh1982@163.com
  Date: 2015/1/6
  Time: 16:32
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title></title>
    <script type="text/javascript" src="RSA.js?version=<%=Math.random() %>"></script>
    <script type="text/javascript" src="BigInt.js?version=<%=Math.random() %>"></script>
    <script type="text/javascript" src="Barrett.js?version=<%=Math.random() %>"></script>
</head>
<body>
<%
String msg2 = request.getParameter("msg2");
%>
<p>JAVA加密 -> JS解密 -> 结果</p>
<p><div id="demsg2_1"></div></p>
<p><div id="demsg2_2"></div></p>
<br/>
<input type="button" value="JS解密" οnclick="decrypt()"/>
<input type="button" value="再测一次" οnclick="javascript:window.location.href=rsa_demo_1.jsp"/>
<script>
    function decrypt(){
        // rsa.js 公钥解密
        setMaxDigits(130); // 设置数值数据的长度,要在new RSAKeyPair()之前使用
        var modulus = "c1d48e176abd94cd1c575771f89d60932afaa98f6c0ba90667a199f5ca6306ece35695b55cac25583ce9161bcc996f0b329a6c7885c92377f119220fbe9d6c2f";
        var publicExponent = "10001"; // 公钥指数 即是客户端加密指数即也是客户端解密指数
        var decryptPublicKey = new RSAKeyPair("",publicExponent,modulus); // 解密公钥
        var demsg2_1 = decryptedString(decryptPublicKey,"<%=msg2 %>");
        var demsg2_2 = decodeURIComponent(demsg2_1);
        document.getElementById("demsg2_1").innerHTML= "解密数据:"+demsg2_1 ;
        document.getElementById("demsg2_2").innerHTML= "转码数据:"+demsg2_2 ;
    }
</script>

</body>
</html>

资源下载路径:

http://download.csdn.net/detail/csto_sun/8333679

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

钩子水手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值