RSA加密算法是一种非对称加密算法。非对称加密与对称加密相比其安全性更好,但对应的加密和解密花费时间长、速度慢,只适合对少量数据进行加密。使用RSA一般需要产生公钥和私钥,当采用公钥加密时,使用私钥解密;采用私钥加密时,使用公钥解密。本文采用公钥给别人使用,别人用公钥加密,我们用私钥解密。
package com.caxs.warn.common.utils;
import com.caxs.warn.common.exception.BusinessException;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.HashMap;
import java.util.Map;
/**
* @Author: TheBigBlue
* @Description:
* @Date: 2019/9/23
*/
public class RSAUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(RSAUtils.class);
/**
* 加解密算法关键字
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* 公钥关键字
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 私钥关键字
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* 默认编码
*/
public static final String CHARSET = "UTF-8";
/**
* @Author: TheBigBlue
* @Description: 解密
* @Date: 2019/9/23
* @Param str: 加密的base64串
* @Param privateKey: Base64私钥串
* @Return: 明文
**/
public static String decrypt(String str, String privateKey) throws Exception {
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes(CHARSET));
//base64编码的私钥
byte[] keyBytes = Base64.decodeBase64(privateKey.getBytes(CHARSET));
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
//RSA解密
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, priKey);
return new String(cipher.doFinal(inputByte), CHARSET);
}
/**
* @Author: TheBigBlue
* @Description: 加密
* @Date: 2019/9/23
* @Param data: 需要加密的数据
* @Param publicKey: Base64公钥串
* @Return:
**/
public static String encrypt(String data, String publicKey) throws Exception {
// 取得公钥
Key key = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey.getBytes(CHARSET))));
// 对数据加密
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
return new String(Base64.encodeBase64(cipher.doFinal(data.getBytes(CHARSET))), CHARSET);
}
/**
* 取得私钥。
*/
public static String getPrivateKeyStr(Map<String, Object> keyMap) {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return Base64.encodeBase64String(key.getEncoded());
}
/**
* 取得公钥。
*/
public static String getPublicKeyStr(Map<String, Object> keyMap) {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return Base64.encodeBase64String(key.getEncoded());
}
/**
* 初始化密钥。
*/
public static Map<String, Object> initKey() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 公钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 私钥
Map<String, Object> keyMap = new HashMap<>(5);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* @Author: TheBigBlue
* @Description: 生成公私钥,并保存至指定目录下
* @Date: 2019/9/23
* @Param filepath: 保存路径
* @Return:
**/
public static void getKeyAndSave(String savePath) {
try {
Map<String, Object> keyMap = initKey();
LOGGER.info("密钥保存路径为:{}", savePath);
FileUtil.writeFile(getPrivateKeyStr(keyMap), savePath + "/private.store");
FileUtil.writeFile(getPublicKeyStr(keyMap), savePath + "/public.store");
} catch (Exception e) {
LOGGER.error("生成密钥失败", e);
throw new BusinessException("生成密钥失败", e);
}
}
}
package com.caxs.warn.secret;
import com.caxs.warn.common.utils.FileUtil;
import com.caxs.warn.common.utils.RSAUtils;
import org.junit.Test;
/**
* @Author: TheBigBlue
* @Description:
* @Date: 2019/9/23
*/
public class SecretTest {
/**
* @Author: TheBigBlue
* @Description: 生成密钥并保存
* @Date: 2019/9/23
* @Return:
**/
@Test
public void testCreate() {
RSAUtils.getKeyAndSave("D:\\Download\\secret");
}
/**
* @Author: TheBigBlue
* @Description: 测试加密后保存
* @Date: 2019/9/23
* @Return:
**/
@Test
public void testEncrypt() {
try {
String data = FileUtil.readFile("D:\\Download\\secret\\script.sql");
System.out.println("需要加密的内容:" + data);
String publicKey = FileUtil.readFile("D:\\Download\\secret\\public.store");
String encryptStr = RSAUtils.encrypt(data, publicKey);
FileUtil.writeFile(encryptStr, "D:\\Download\\secret\\secret-data");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Author: TheBigBlue
* @Description: 测试读取后解密
* @Date: 2019/9/23
* @Return:
**/
@Test
public void testDecrypt() {
try {
String data = FileUtil.readFile("D:\\Download\\secret\\secret-data");
String privateKey = FileUtil.readFile("D:\\Download\\secret\\private.store");
String decryptStr = RSAUtils.decrypt(data, privateKey);
System.out.println("解密后的内容:" + decryptStr);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Author: TheBigBlue
* @Description: 测试加/解密
* @Date: 2019/9/23
* @Return:
**/
@Test
public void testEncryptAndDecrypt() {
try {
String data = FileUtil.readFile("D:\\Download\\secret\\script.sql");
System.out.println("需要加密的内容:" + data);
String publicKey = FileUtil.readFile("D:\\Download\\secret\\public.store");
String privateKey = FileUtil.readFile("D:\\Download\\secret\\private.store");
String encryptStr = RSAUtils.encrypt(data, publicKey);
String decryptStr = RSAUtils.decrypt(encryptStr, privateKey);
System.out.println("解密后的内容:" + decryptStr);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.caxs.warn.common.utils;
import com.caxs.warn.common.exception.BusinessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* @ClassName FileUtil
* @Description 操作文件工具
* @Author baojin
* @Date 2019/9/18 14:39
* @Version: v1.0 文件初始创建
**/
public class FileUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUtil.class);
private FileUtil() {
}
/**
* @return java.lang.String
* @Description 读取文件
* @Param filePath 路径
* @Author baojin
* @date 2019/9/18 14:41
* @Throw
**/
public static String readFile(String filePath) {
String string;
StringBuilder data = new StringBuilder();
try (
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)
) {
while ((string = bufferedReader.readLine()) != null) {
//每行用分号分割
data.append(string);
}
LOGGER.info("文件读取成功,文件名称为:{}", filePath);
return data.toString();
} catch (Exception e) {
LOGGER.error("文件读取失败,文件名称为:" + filePath, e);
throw new BusinessException("文件读取失败", e);
}
}
}
非对称加密存在加密数据限制问题(即使用非对称密钥加密数据时,一次加密的数据长度是(密钥长度/8-11)本程序中,密钥长度是1024( keyPairGen.initialize(1024);程序中这里指定了),那么加密内容应该不得超过1024/8-11=117,解密要求密文最大长度为1024/8=128字节,如果需要加密的数据长度大于117,则会报错:Data must not be longer than 117 bytes
/**
* 最大加密字节数,超出最大字节数需要分组加密
*/
private static int MAX_ENCRYPT_BLOCK = 117;
/**
* 最大解密字节数,超出最大字节数需要分组解密
*/
private static int MAX_DECRYPT_BLOCK = 128;
/**
* @Author: TheBigBlue
* @Description: 解密
* @Date: 2019/9/23
* @Param str: 加密的base64串
* @Param privateKey: Base64私钥串
* @Return: 明文
**/
public static String decrypt(String inputData, String privateKey) throws Exception {
//64位解码加密后的字符串
byte[] inputBytes = Base64.decodeBase64(inputData.getBytes(CHARSET));
//base64编码的私钥
byte[] keyBytes = Base64.decodeBase64(privateKey.getBytes(CHARSET));
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
//RSA解密
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, priKey);
byte[] resultBytes = doFinalBySegment(inputBytes, cipher, MAX_DECRYPT_BLOCK);
return new String(resultBytes, CHARSET);
}
/**
* @Author: TheBigBlue
* @Description: 加密
* @Date: 2019/9/23
* @Param data: 需要加密的数据
* @Param publicKey: Base64公钥串
* @Return:
**/
public static String encrypt(String inputData, String publicKey) throws Exception {
// 取得公钥
Key key = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey.getBytes(CHARSET))));
// 对数据加密
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] resultBytes = doFinalBySegment(inputData.getBytes(CHARSET), cipher, MAX_ENCRYPT_BLOCK);
return new String(Base64.encodeBase64(resultBytes), CHARSET);
}
/**
* @Author: TheBigBlue
* @Description: 分段加解密
* @Date: 2019/9/24
* @Return:
**/
private static byte[] doFinalBySegment(byte[] inputBytes, Cipher cipher, int maxLength) throws Exception {
int inputLenth = inputBytes.length;
LOGGER.info("数据超出最大字节数进行分段加解密:maxLength={}, inputLength={}", maxLength, inputLenth);
// 标识
int offSet = 0;
byte[] cache;
byte[] resultBytes = {};
while (inputLenth - offSet > 0) {
//超出最大字节数分组加密
if (inputLenth - offSet > maxLength) {
cache = cipher.doFinal(inputBytes, offSet, maxLength);
offSet += maxLength;
} else {
//直接加密
cache = cipher.doFinal(inputBytes, offSet, inputLenth - offSet);
offSet = inputLenth;
}
resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
}
return resultBytes;
}