利用jsencrypt.js 進行前端RSA加密,java後端解密
阿新 • • 發佈:2021-02-03
利用jsencrypt.js 進行前端RSA加密,java後端解密
敏感資訊加密傳輸,可以一定程度上避免重要業務資料和個人資訊洩露。
程式碼測試可用
一、RSA加密演算法
什麼是RSA加密
RSA加密演算法是一種非對稱加密演算法,RSA加密使用了"一對"金鑰.分別是公鑰和私鑰,這個公鑰和私鑰其實就是一組數字!其二進位制位長度可以是1024位或者2048位.長度越長其加密強度越大,目前為止公之於眾的能破解的最大長度為768位金鑰,只要高於768位,相對就比較安全.所以目前為止,這種加密演算法一直被廣泛使用
RSA加密與解密
- 使用公鑰加密的資料,利用私鑰進行解密
- 使用私鑰加密的資料,利用公鑰進行解密
RSA祕鑰生成方式
Windows系統可以使用git命令列工具
- 單擊滑鼠右鍵——git bash here 調出git bash
- 生成私鑰,金鑰長度為1024bit
$ openssl genrsa -out private.pem 1024
- 把pkcs1格式轉為pkcs8格式(java側使用的是pkcs8)
$ openssl pkcs8 -topk8 -inform PEM -in private.pem -outform pem -nocrypt -out pkcs8.pem
PKCS1的檔案頭格式 -----BEGIN RSA PRIVATE KEY-----
PKCS8的檔案頭格式 -----BEGIN PRIVATE KEY-----
- 從私鑰中提取公鑰
$ openssl rsa -in pkcs8.pem -pubout -out public.pem
二、前端使用jsencrypt進行加密
引入js
https://cdn.bootcdn.net/ajax/libs/jsencrypt/2.3.1/jsencrypt.min.js
let passWord = '111111';
//密碼RSA加密
let encrypt = new JSEncrypt();
encrypt.setPublicKey('公鑰');//此處為RSA公鑰,public.pem
passWord = encrypt.encrypt(passWord);//加密後的密碼
三、Java解密
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Map;
import java.util.TreeMap;
public class RSAUtils {
// 加密資料和祕鑰的編碼方式
public static final String UTF_8 = "UTF-8";
// 填充方式
public static final String AES_ALGORITHM = "AES/CFB/PKCS5Padding";
public static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";
public static final String RSA_ALGORITHM_NOPADDING = "RSA";
/**
* Description: 解密接收資料
* @author wh.huang DateTime 2018年11月15日 下午5:06:42
* @param externalPublicKey
* @param selfPrivateKey
* @throws InvalidKeyException
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws UnsupportedEncodingException
* @throws InvalidAlgorithmParameterException
* @throws DecoderException
*/
public static String decryptReceivedData(PublicKey externalPublicKey, PrivateKey selfPrivateKey, String receiveData) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidAlgorithmParameterException, DecoderException {
@SuppressWarnings("unchecked")
Map<String, String> receivedMap = (Map<String, String>) JSON.parse(receiveData);
// receivedMap為請求方通過from urlencoded方式,請求過來的引數列表
String inputSign = receivedMap.get("sign");
// 用請求方提供的公鑰驗籤,能配對sign,說明來源正確
inputSign = decryptRSA(externalPublicKey, inputSign);
// 校驗sign是否一致
String sign = sha256(receivedMap);
if (!sign.equals(inputSign)) {
// sign校驗不通過,說明雙方傳送出的資料和對方收到的資料不一致
System.out.println("input sign: " + inputSign + ", calculated sign: " + sign);
return null;
}
// 解密請求方在傳送請求時,加密data欄位所用的對稱加密金鑰
String key = receivedMap.get("key");
String salt = receivedMap.get("salt");
key = decryptRSA(selfPrivateKey, key);
salt = decryptRSA(selfPrivateKey, salt);
// 解密data資料
String data = decryptAES(key, salt, receivedMap.get("data"));
System.out.println("接收到的data內容:" + data);
return data;
}
/**
* Description: 加密資料組織示例
* @author wh.huang DateTime 2018年11月15日 下午5:20:11
* @param externalPublicKey
* @param selfPrivateKey
* @return 加密後的待發送資料
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws InvalidKeyException
* @throws NoSuchPaddingException
* @throws UnsupportedEncodingException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws InvalidAlgorithmParameterException
*/
public static String encryptSendData(PublicKey externalPublicKey, PrivateKey selfPrivateKey, JSONObject sendData) throws NoSuchAlgorithmException, InvalidKeySpecException,
InvalidKeyException, NoSuchPaddingException, UnsupportedEncodingException, BadPaddingException,
IllegalBlockSizeException, InvalidAlgorithmParameterException {
// 隨機生成對稱加密的金鑰和IV (IV就是加鹽的概念,加密的偏移量)
String aesKeyWithBase64 = genRandomAesSecretKey();
String aesIVWithBase64 = genRandomIV();
// 用接收方提供的公鑰加密key和salt,接收方會用對應的私鑰解密
String key = encryptRSA(externalPublicKey, aesKeyWithBase64);
String salt = encryptRSA(externalPublicKey, aesIVWithBase64);
// 組織業務資料資訊,並用上面生成的對稱加密的金鑰和IV進行加密
System.out.println("傳送的data內容:" + sendData.toJSONString());
String cipherData = encryptAES(aesKeyWithBase64, aesIVWithBase64, sendData.toJSONString());
// 組織請求的key、value對
Map<String, String> requestMap = new TreeMap<String, String>();
requestMap.put("key", key);
requestMap.put("salt", salt);
requestMap.put("data", cipherData);
requestMap.put("source", "由接收方提供"); // 新增來源標識
// 計算sign,並用請求方的私鑰加簽,接收方會用請求方發放的公鑰驗籤
String sign = sha256(requestMap);
requestMap.put("sign", encryptRSA(selfPrivateKey, sign));
// TODO: 以form urlencoded方式呼叫,引數為上面組織出來的requestMap
// 注意:請務必以form urlencoded方式,否則base64轉碼後的個別字元可能會被轉成空格,對方接收後將無法正常處理
JSONObject json = new JSONObject();
json.putAll(requestMap);
return json.toString();
}
/**
* Description: 獲取隨機的對稱加密的金鑰
* @author wh.huang DateTime 2018年11月15日 下午5:25:53
* @return 對稱祕鑰字元
* @throws NoSuchAlgorithmException
* @throws UnsupportedEncodingException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws InvalidKeyException
* @throws NoSuchPaddingException
*/
private static String genRandomAesSecretKey() throws NoSuchAlgorithmException, UnsupportedEncodingException,
IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchPaddingException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
SecretKey secretKey = keyGen.generateKey();
String keyWithBase64 = Base64.encodeBase64(secretKey.getEncoded()).toString();
return keyWithBase64;
}
private static String genRandomIV() {
SecureRandom r = new SecureRandom();
byte[] iv = new byte[16];
r.nextBytes(iv);
String ivParam = Base64.encodeBase64(iv)+"";
return ivParam;
}
/**
* 對稱加密資料
*
* @param keyWithBase64
* @param plainText
* @return
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws UnsupportedEncodingException
* @throws InvalidAlgorithmParameterException
*/
private static String encryptAES(String keyWithBase64, String ivWithBase64, String plainText)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
byte[] keyWithBase64Arry = keyWithBase64.getBytes();
byte[] ivWithBase64Arry = ivWithBase64.getBytes();
SecretKeySpec key = new SecretKeySpec(Base64.decodeBase64(keyWithBase64Arry), "AES");
IvParameterSpec iv = new IvParameterSpec(Base64.decodeBase64(ivWithBase64Arry));
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
return Base64.encodeBase64(cipher.doFinal(plainText.getBytes(UTF_8))).toString();
}
/**
* 對稱解密資料
*
* @param keyWithBase64
* @param cipherText
* @return
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws UnsupportedEncodingException
* @throws InvalidAlgorithmParameterException
*/
private static String decryptAES(String keyWithBase64, String ivWithBase64, String cipherText)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
byte[] keyWithBase64Arry = keyWithBase64.getBytes();
byte[] ivWithBase64Arry = ivWithBase64.getBytes();
byte[] cipherTextArry = cipherText.getBytes();
SecretKeySpec key = new SecretKeySpec(Base64.decodeBase64(keyWithBase64Arry), "AES");
IvParameterSpec iv = new IvParameterSpec(Base64.decodeBase64(ivWithBase64Arry));
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
return new String(cipher.doFinal(Base64.decodeBase64(cipherTextArry)), UTF_8);
}
/**
* 非對稱加密,根據公鑰和原始內容產生加密內容
*
* @param key
* @return
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws UnsupportedEncodingException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws InvalidAlgorithmParameterException
*/
private static String encryptRSA(Key key, String plainText)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException,
BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
return Base64.encodeBase64(cipher.doFinal(plainText.getBytes(UTF_8))).toString();
}
/**
* 根據私鑰和加密內容產生原始內容
* @param key
* @param content
* @return
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws UnsupportedEncodingException
* @throws InvalidAlgorithmParameterException
*/
private static String decryptRSA(Key key, String content) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, DecoderException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] contentArry = content.getBytes();
return new String(cipher.doFinal(Base64.decodeBase64(contentArry)), UTF_8);
}
/**
* 計算sha256值
*
* @param paramMap
* @return 簽名後的所有資料,原始資料+簽名
*/
private static String sha256(Map<String, String> paramMap) {
Map<String, String> params = new TreeMap<String, String>(paramMap);
StringBuilder concatStr = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
if ("sign".equals(entry.getKey())) {
continue;
}
concatStr.append(entry.getKey() + "=" + entry.getValue() + "&");
}
return DigestUtils.md5Hex(concatStr.toString());
}
/**
* 建立RSA的公鑰和私鑰示例 將生成的公鑰和私鑰用Base64編碼後打印出來
* @throws NoSuchAlgorithmException
*/
public static void createKeyPairs() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
System.out.println("公鑰"+Base64.encodeBase64(publicKey.getEncoded()));
System.out.println("私鑰"+Base64.encodeBase64(privateKey.getEncoded()));
}
/**
* Description:預設的RSA解密方法 一般用來解密 引數 小資料
* @author wh.huang DateTime 2018年12月14日 下午3:43:11
* @param privateKeyStr
* @param data
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
public static String decryptRSADefault(String privateKeyStr,String data) {
try{
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM_NOPADDING);
byte[] privateKeyArray = privateKeyStr.getBytes();
byte[] dataArray = data.getBytes();
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyArray));
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM_NOPADDING);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.decodeBase64(dataArray)), UTF_8);
}catch (Exception e){
e.printStackTrace();
}
return "";
}
}
解密
String password = "js側加密密碼"
password = RSAUtils.decryptRSADefault("私鑰",password);//解密