import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
public class RSAEncrypt {
public static void main(String[] args) throws Exception {
String point = "xxx";
//生成公钥和私钥
Map keyMap = new HashMap<>();//genKeyPair();
keyMap.put("publicKey", "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOUDwJVY1JMY4oSHM1+VKeYZ5T2LjQ4wvENnt0TlRoOYDrUen4Nm3GbVKiGTot76gu7xYL1X9PQvDnYLpUVu0mA2oLrXWZj2ByTW83Ehdc5Y9aLXtNzmm4e6PXtuEtXk2sqUZz+XtBBAUMAne4J9G9DAZVPLFxUUJyzVb9cnK6NQIDAQAB");
keyMap.put("privateKey", "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAM5QPAlVjUkxjihIczX5Up5hnlPYuNDjC8Q2e3ROVGg5gOtR6fg2bcZtUqIZOi3vqC7vFgvVf09C8OdgulRW7SYDagutdZmPYHJNbzcSF1zlj1ote03Oabh7o9e24S1eTaypRnP5e0EEBQwCd7gn0b0MBlU8sXFRQnLNVv1ycro1AgMBAAECgYAKkJlCcRsXEG6TKYKc1POiIKWW7ZYpPDcyCQgxYIF6BNfRNRSiHUdpzddZbalJCOi33o5mdLxcNrVXY+CmyPzDyeyNWWX8UcL2Wud8vRlWU7kQ+YcCVyS/nqRLBpHb0QgW7bqzb7fRpnmqhfj+A9hzRaoKxsZ8EWQfvN5UcdmQgQJBAPyTXzHcicK6gsgaXVo8awXsKxT6/bVAq7+FO/F4ckflS3oyABFNnqVRTC3nuQsU6nq3fu1kDwa03NcAa5zZaykCQQDRHEw4N0pnGKdecKTjBlD95B9WI0KCMWHpSOTIZJUIEvKXANX5BFaGHY01BNxDmwVcuecHgG2XH/WyIzVeJEQtAkEA8WM/NX4aQvrRhsB7u4PGnPBq9DA0TQeznOSOt2ZvgfrIOc6TdfYCyuh5r92oYcjpl8LLEcHxAm3UKb8DGfJIkQJAAjyYQB2vSQ0FdUglK1x870pKX4R/CJ94maMy90XEJlL1j1Ht9/zo5ARa509G/94fn49JflYMVgp8eUxRHNGsfQJBAKm39ZUaFyuDSpRINHZNHfldasmy9hLyXdTb3sLFj/bPaq0MyORAZPqq6XCu+nnIhVKVyADbXb+8T5kn70lzIbg=");
String publicKey = keyMap.get("publicKey");
String privateKey = keyMap.get("privateKey");
System.out.println(publicKey);
System.out.println(privateKey);
//加密字符串
String message = "这是亲前端请求的入参";
System.out.println("-------------------------------------------------------");
System.out.println("前端请求的原数据 :" + message);
System.out.println("-------------------------------------------------------");
String messageEn = publicKeyEncrypt(message, publicKey, point);
System.out.println("前端请求的加密 :" + messageEn);
System.out.println("-------------------------------------------------------");
String messageDe = privateKeyDecrypt(messageEn, privateKey,point);
System.out.println("后端解密出来的数据 :" + messageDe);
System.out.println("-------------------------------------------------------");
//模拟前端数据展示处理
//私钥加密,公钥解密
String s = privateKeyEncrypt(messageDe, privateKey,point);
System.out.println("-------------------------------------------------------");
System.out.println("后端返回的加密数据 :"+s);
System.out.println("-------------------------------------------------------");
String s1 = publicKeyDecrypt(s, publicKey,point);
System.out.println("前端解密出来显示的数据 :"+s1);
System.out.println("-------------------------------------------------------");
}
/**
* 随机生成密钥对
*
* @param point
* @throws NoSuchAlgorithmException
*/
public static Map genKeyPair(String point) throws NoSuchAlgorithmException {
log.info("{}|开始生成公私钥", point);
// KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// 初始化密钥对生成器,密钥大小为96-1024位
keyPairGen.initialize(1024, new SecureRandom());
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥
String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
// 得到私钥字符串
String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
// 将公钥和私钥保存到Map
Map map = new HashMap<>();
map.put("publicKey", publicKeyString);
map.put("privateKey", privateKeyString);
log.info("{}|生成的公私钥|map:{}", point, map);
return map;
}
/**
* RSA公钥加密
*
* @param str 加密字符串
* @param publicKey 公钥
* @return 密文
* 问题场景:
* Cipher提供加解密API,其中RSA非对称加密解密内容长度是有限制的,加密长度不超过117Byte,解密长度不超过128Byte,
* 报错如下:javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes。
* @throws Exception 加密过程中的异常信息
*/
public static String publicKeyEncrypt(String str, String publicKey, String point) throws Exception {
log.info("{}|RSA公钥加密前的数据|str:{}|publicKey:{}", point, str, publicKey);
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").
generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
log.info("{}|公钥加密后的数据|outStr:{}", point, outStr);
return outStr;
}
/**
* RSA公钥加密
*
* @param str 加密字符串
* @param publicKey 公钥
* @return 当入参超过117个字节时,会分段进行加密
* 问题场景:
* @throws Exception 加密过程中的异常信息
*/
public static List publicKeyEncryptBigStr(String str, String publicKey, String point) throws Exception {
List resultList = new ArrayList<>();
log.info("{}|RSA公钥加密前的数据|str:{}|publicKey:{}", point, str, publicKey);
// 分割后的入参
List listStr = split(str);
for (String s : listStr) {
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").
generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr = Base64.encodeBase64String(cipher.doFinal(s.getBytes("UTF-8")));
resultList.add(outStr);
log.info("{}|公钥加密后的数据|outStr:{}",point,outStr);
}
return resultList;
}
/**
* RSA私钥解密
*
* @param str 加密字符串
* @param privateKey 私钥
* @param point
* @return 铭文
* @throws Exception 解密过程中的异常信息
*/
public static String privateKeyDecrypt(String str, String privateKey, String point) throws Exception {
log.info("{}|RSA私钥解密前的数据|str:{}|privateKey:{}", point, str, privateKey);
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA")
.generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte));
log.info("{}|RSA私钥解密后的数据|outStr:{}", point, outStr);
return outStr;
}
/**
* RSA私钥加密
*
* @param str
* @param privateKey
* @return
* @throws Exception
*/
public static String privateKeyEncrypt(String str, String privateKey, String point) throws Exception {
log.info("{}|RSA私钥加密前的数据|str:{}|publicKey:{}", point, str, privateKey);
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(privateKey);
PrivateKey priKey = KeyFactory.getInstance("RSA").
generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, priKey);
String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes()));
log.info("{}|RSA私钥加密后的数据|outStr:{}", point, outStr);
return outStr;
}
/**
* RSA公钥解密
*
* @param str
* @param publicKey
* @return
* @throws Exception
*/
public static String publicKeyDecrypt(String str, String publicKey, String point) throws Exception {
log.info("{}|RSA公钥解密前的数据|str:{}|publicKey:{}", point, str, publicKey);
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(publicKey);
PublicKey pubKey = KeyFactory.getInstance("RSA")
.generatePublic(new X509EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pubKey);
String outStr = new String(cipher.doFinal(inputByte));
log.info("{}|RSA公钥解密后的数据|outStr:{}", point, outStr);
return outStr;
}
public static List split(String originalString) {
int chunkSize = 30;
List chunks = splitString(originalString, chunkSize);
return chunks;
}
public static List splitString(String original, int chunkSize) {
List chunks = new ArrayList<>();
for (int i = 0; i < original.length(); i += chunkSize) {
int endIndex = Math.min(i + chunkSize, original.length());
chunks.add(original.substring(i, endIndex));
}
return chunks;
}
}
关于中文乱码分析
应用场景 :后端返回数据时,对数据进行RSA加密。
问题1: 后端返回数据过长,RSA无法加密过长的数据
抛出此异常 :Data must not be longer than 117 bytes。
解决方案(1)(坑) : (此方案返回数据包含中文时,会有概率乱码)
把返回原始数据转为 byte数组 byte[] inputArray = input.getBytes()
然后把byte数组切割成长度 为117(每次加密117Byte) ,(加密长度不超过117Byte,解密长度不超过128Byte),返回前端数据格式为 List
,此时前端拿到数据解密,发现部分中文乱码 ,若返回数据不包含中文,则正常显示。(同样的也不能先Base64)
解决方案 (2):(正确解决)
把返回的数据提前切割成长度为35为的List
,(为什么切割为35 ?,1个英文字符1占一个字节,一个中文,或一个中文符号占3个字节 假设这35个字符都是中文,则 35*3 = 102,若设置过大,加密会有异常 ) 然后循环加密,返回前端List ,前端循环解密,而后正常显示
思考? 为什么方案1会有部分乱码?
因为一个中文字符为3个字节,会被切开,所以正好切到中文的时候,所以就造成了部分中文乱码,就是因为中文被切断造成的,所以要对原始数据切割,这样就不会把中文截断,就不会造成中文乱码的现象