vue+java 使用 RSA 加解密

vue+java 使用 RSA 加解密
大部分代码使用以下大神连接,参考连接但前后端不相通,进行了近一步封装。

简单介绍:

最近了解vue,感觉功能很强大。加上有个朋友成考毕业设计是“简易聊天工具”,涉及到加密传输,设想使用RSA+AES混合加密,具体就是客户端在第一访问时就生成RSA密钥对,然后后端生成AES密钥保存并使用RSA加密后传输给VUE前端,VUE进行解密,之后聊天信息和敏感信息使用AES进行加密传输。
vue+java 使用 RSA 加解密_第1张图片

使用别人代码不通原因:

  1. String–>byte 使用方式不对
  • 加密
    先String.getbytes(“utf-8”)转bytes 加密完成后使用Base64.encodeBase64String(encryptedData)转String
    -解密
    先 Base64.decodeBase64(base64Stirng) 解密完成后
    new String(decryptByPrivateKey,“u-ft8”);
  1. 前端生成密钥对含有前缀和换行,使用
    publickey=publickey.replace("-----BEGIN PUBLIC KEY-----", “”).
    replace("-----END PUBLIC KEY-----", “”).
    replaceAll("(\r\n|\n|\n\r)","").trim();
    privatekey=privatekey.replace("-----BEGIN RSA PRIVATE KEY-----", “”).
    replace("-----END RSA PRIVATE KEY-----", “”).
    replaceAll("(\r\n|\n|\n\r)","").trim();
    进行处理
  2. java 解密不支持pkcs#1 需要转换成 pkcs#8

代码

1.服务端
包依赖

		<dependency>
			<groupId>org.bouncycastlegroupId>
			<artifactId>bcprov-jdk16artifactId>
			<version>1.46version>
		dependency> 

服务端生成密钥及加解密

package com.langlong.util;


import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * RSA加、解密算法工具类
 */
public class RSAEncryptUtil {
     

    /**
     * 加密算法AES
     */
    private static final String KEY_ALGORITHM = "RSA";

    /**
     * 算法名称/加密模式/数据填充方式
     * 默认:RSA/ECB/PKCS1Padding
     */
    private static final String ALGORITHMS = "RSA/ECB/PKCS1Padding";

    /**
     * Map获取公钥的key
     */
    private static final String PUBLIC_KEY = "publicKey";

    /**
     * Map获取私钥的key
     */
    private static final String PRIVATE_KEY = "privateKey";

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * RSA 位数 如果采用2048 上面最大加密和最大解密则须填写:  245 256
     */
    private static final int INITIALIZE_LENGTH = 1024;

    /**
     * 后端RSA的密钥对(公钥和私钥)Map,由静态代码块赋值
     */
    private static Map<String, Object> genKeyPair = new HashMap<>();

    static {
     
        try {
     
            genKeyPair.putAll(genKeyPair());
        } catch (Exception e) {
     
            e.printStackTrace();
        }
    }

    /**
     * 生成密钥对(公钥和私钥)
     */
    private static Map<String, Object> genKeyPair() throws Exception {
     
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(INITIALIZE_LENGTH);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        //公钥
        keyMap.put(PUBLIC_KEY, publicKey);
        //私钥
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    
    /**
     * 私钥解密
     *
     * @param base64Stirng 前端传过来的BASE64
     * @param privateKey    私钥(BASE64编码)
     */
    public   static String decryptByPrivateKey(String base64Stirng , String privateKey) throws Exception {
     
    
    	byte[] codeBase64 = Base64.decodeBase64(base64Stirng);
    	byte[] decryptByPrivateKey = decryptByPrivateKey(codeBase64, privateKey);
		return new String(decryptByPrivateKey,"u-ft8");
    }
    /**
     * 私钥解密
     *
     * @param encryptedData 已加密数据
     * @param privateKey    私钥(BASE64编码)
     */
    private static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
     
        //base64格式的key字符串转Key对象
      
//        String formatPkcs1ToPkcs8 = RsaPkcsTransformer.formatPkcs1ToPkcs8(privateKey);
//        System.out.println(formatPkcs1ToPkcs8);
//        byte[] keyBytes = Base64.decodeBase64(formatPkcs1ToPkcs8.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", ""));
//       PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
    	//将Pkcs1密钥转换为Pkcs8  java 不支持Pkcs1
    	byte[] formatPkcs1ToPkcs8Bytes = RsaPkcsTransformer.formatPkcs1ToPkcs8Bytes(privateKey);
    	PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(formatPkcs1ToPkcs8Bytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);

        //设置加密、填充方式
        /*
            如需使用更多加密、填充方式,引入
            
                org.bouncycastle
                bcprov-jdk16
                1.46
            
            并改成
             Cipher cipher = Cipher.getInstance(ALGORITHMS);
         */
     
        
        Cipher cipher = Cipher.getInstance(ALGORITHMS );
        cipher.init(Cipher.DECRYPT_MODE, privateK);

        //分段进行解密操作
        return encryptAndDecryptOfSubsection(encryptedData, cipher, MAX_DECRYPT_BLOCK);
    }

    
    
    /**
     * 公钥加密
     * @param data
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey(String data, String publicKey) throws Exception {
     
    	byte[] dataBytes = data.getBytes("utf-8");
    	byte[] encryptedData = encryptByPublicKey(dataBytes, 
    			publicKey);
    	String encodeBase64String = Base64.encodeBase64String(encryptedData);
		return encodeBase64String;
    }
    /**
     * 公钥加密
     *
     * @param data      源数据
     * @param publicKey 公钥(BASE64编码)
     */
    private static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
     
        //base64格式的key字符串转Key对象
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);

        //设置加密、填充方式
        /*
            如需使用更多加密、填充方式,引入
            
                org.bouncycastle
                bcprov-jdk16
                1.46
            
            并改成
            Cipher cipher = Cipher.getInstance(ALGORITHMS ,new BouncyCastleProvider());
                 Cipher cipher = Cipher.getInstance(ALGORITHMS);
         */
   
        Cipher cipher = Cipher.getInstance(ALGORITHMS ,new BouncyCastleProvider());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);

        //分段进行加密操作
        return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK);
    }

    /**
     * 获取私钥
     */
    public static String getPrivateKey() {
     
    	
        Key key = (Key) genKeyPair.get(PRIVATE_KEY);
        return Base64.encodeBase64String(key.getEncoded());
    }

    /**
     * 获取公钥
     */
    public static String getPublicKey() {
     
        Key key = (Key) genKeyPair.get(PUBLIC_KEY);
        return Base64.encodeBase64String(key.getEncoded());
    }

    /**
     * 分段进行加密、解密操作
     */
    private static byte[] encryptAndDecryptOfSubsection(byte[] data, Cipher cipher, int encryptBlock) throws Exception {
     
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
     
            if (inputLen - offSet > encryptBlock) {
     
                cache = cipher.doFinal(data, offSet, encryptBlock);
            } else {
     
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * encryptBlock;
        }
        byte[] toByteArray = out.toByteArray();
        out.close();
        return toByteArray;
    }

服务端的依赖

package com.langlong.util;
import java.io.StringWriter;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;

/**
 * Transform PKCS format
 *     PKCS#8    -> PKCS#1
 * 
 */
public class RsaPkcsTransformer {
     
    private static final String COMMENT_BEGIN_FLAG = "-----";
    private static final String RETURN_FLAG_R = "\r";
    private static final String RETURN_FLAG_N = "\n";
    
    
        
    //format PKCS#8 to PKCS#1
    public static String formatPkcs8ToPkcs1(String rawKey) throws Exception {
     
        String result = null;
        //extract valid key content
      //  String validKey = RsaPemUtil.extractFromPem(rawKey);
            //将BASE64编码的私钥字符串进行解码
            byte[] encodeByte = Base64.decodeBase64(rawKey);
                                    
            //==========
            //pkcs8Bytes contains PKCS#8 DER-encoded key as a byte[]
            PrivateKeyInfo pki = PrivateKeyInfo.getInstance(encodeByte);
            RSAPrivateKeyStructure pkcs1Key = RSAPrivateKeyStructure.getInstance(pki.getPrivateKey());
            byte[] pkcs1Bytes = pkcs1Key.getEncoded();//etc.
            //==========
            
            String type = "RSA PRIVATE KEY";
            result = format2PemString(type, pkcs1Bytes);            
        return result;
    }
    
    //format PKCS#1 to PKCS#8
    public static String formatPkcs1ToPkcs8(String rawKey) throws Exception {
     
        String result = null;
        //extract valid key content
            //将BASE64编码的私钥字符串进行解码
            byte[] encodeByte = Base64.decodeBase64(rawKey);
            
            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.pkcs8ShroudedKeyBag);    //PKCSObjectIdentifiers.pkcs8ShroudedKeyBag        
            ASN1Object asn1Object = ASN1Object.fromByteArray(encodeByte);
            PrivateKeyInfo privKeyInfo = new PrivateKeyInfo(algorithmIdentifier, asn1Object);            
            byte[] pkcs8Bytes = privKeyInfo.getEncoded();
            String type = "PRIVATE KEY";
            result = format2PemString(type, pkcs8Bytes);
            
        return result;
    }
    public static byte[] formatPkcs1ToPkcs8Bytes(String rawKey) throws Exception {
     
        //extract valid key content
            //将BASE64编码的私钥字符串进行解码
            byte[] encodeByte = Base64.decodeBase64(rawKey);
            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.pkcs8ShroudedKeyBag);    //PKCSObjectIdentifiers.pkcs8ShroudedKeyBag        
            ASN1Object asn1Object = ASN1Object.fromByteArray(encodeByte);
            PrivateKeyInfo privKeyInfo = new PrivateKeyInfo(algorithmIdentifier, asn1Object);            
            byte[] pkcs8Bytes = privKeyInfo.getEncoded();
            
        return pkcs8Bytes;
    }
    
    // Write to pem file
    private static String format2PemString(String type, byte[] privateKeyPKCS1) throws Exception {
     
        PemObject pemObject = new PemObject(type, privateKeyPKCS1);
        StringWriter stringWriter = new StringWriter();
        PemWriter pemWriter = new PemWriter(stringWriter);
        pemWriter.writeObject(pemObject);
        pemWriter.close();
        String pemString = stringWriter.toString();
        return pemString;
    }   
}
  1. 客户端密钥生成及加解密
    引入包:
    npm install jsencrypt --dep
import Vue  from 'vue'

import JSEncrypt  from 'jsencrypt'
    //RSA 位数,这里要跟后端对应

    //只能在当页使用,如果需要跨页需要 需要将密钥对保存到缓存
    const  thisKeyPair =  new JSEncrypt({
     default_key_size: 1024});
    //生成密钥对(公钥和私钥)
   export  function genKeyPair() {
     
        let genKeyPair = {
     };
        //获取私钥

        let privateKey = thisKeyPair.getPrivateKey();
        genKeyPair.privateKey= privateKey.replace("-----BEGIN RSA PRIVATE KEY-----", "").
				replace("-----END RSA PRIVATE KEY-----", "").replace("(\\r\\n|\\n|\\n\\r)","").trim();
        //获取公钥
        let publicKey= thisKeyPair.getPublicKey();
        genKeyPair.publicKey=publicKey.replace("-----BEGIN PUBLIC KEY-----", "").
				replace("-----END PUBLIC KEY-----", "").
				replace("(\\r\\n|\\n|\\n\\r)","").trim();

       thisKeyPair.setPublicKey(publicKey);
       thisKeyPair.setPrivateKey(privateKey);
        return genKeyPair;
    }

    //公钥加密
  export function encrypt(plaintext, publicKey) {
     
        if (plaintext instanceof Object) {
     
            //1、JSON.stringify
            plaintext = JSON.stringify(plaintext)
        } 
        publicKey && thisKeyPair.setPublicKey(publicKey);
        return thisKeyPair.encrypt(plaintext);
    }

    //私钥解密
   export function decrypt(ciphertext, privateKey) {
     
        privateKey && thisKeyPair.setPrivateKey(privateKey);
        let decString = thisKeyPair.decrypt(ciphertext);
        if(decString.charAt(0) === "{" || decString.charAt(0) === "[" ){
     
            //JSON.parse
            decString = JSON.parse(decString);
        }
        return decString;
    }

测试代码

  1. 客户端测试
    在钩子函数create()中使用(checkRsa 这个是请求前端函数的api 封装request)
  var a=genKeyPair();
     var  params={
     };
     params.puk=a.publicKey
     params.prk=a.privateKey
      checkRsa(params).then(response=>{
     
       console.log('解密前数据:');
        console.log(response.data.encString);
        console.log('解密数据:');
        console.log(decrypt(response.data.encString));
      }) 
  1. 服务端测试
	@CrossOrigin(origins = "*", maxAge = 3600)
	@RequestMapping(value ="/checkrsa.do")
	@ResponseBody
	public JSONObject checkRSA(HttpServletRequest request,String prk,String puk,String enccString) throws Exception {
     
		
		String enString="这是要测试的数据";
		puk=puk.replace("-----BEGIN PUBLIC KEY-----", "").
				replace("-----END PUBLIC KEY-----", "").
				replaceAll("(\\r\\n|\\n|\\n\\r)","").trim();
		
		prk=prk.replace("-----BEGIN RSA PRIVATE KEY-----", "").
				replace("-----END RSA PRIVATE KEY-----", "").
				replaceAll("(\\r\\n|\\n|\\n\\r)","").trim();
		//后端加密
		String encodeBase64String =   RSAEncryptUtil.encryptByPublicKey(enString, 
				puk);
		//后端解密
		String dcString = RSAEncryptUtil.decryptByPrivateKey(enccString, prk);
		System.out.println(dcString);
		JSONObject  jso= new JSONObject();
		
		System.out.println(encodeBase64String);
		 jso.put("enString", enString);
		 jso.put("puk", puk);
		 jso.put("encString",encodeBase64String);
		return jso;
	}

你可能感兴趣的:(加密解密,java,vue,javascript)