vue+java 使用 RSA 加解密
大部分代码使用以下大神连接,参考连接但前后端不相通,进行了近一步封装。
最近了解vue,感觉功能很强大。加上有个朋友成考毕业设计是“简易聊天工具”,涉及到加密传输,设想使用RSA+AES混合加密,具体就是客户端在第一访问时就生成RSA密钥对,然后后端生成AES密钥保存并使用RSA加密后传输给VUE前端,VUE进行解密,之后聊天信息和敏感信息使用AES进行加密传输。
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;
}
}
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;
}
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));
})
@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;
}