-
首先了解下,什么是堆成加密,什么是非對稱加密?
對稱加密:加密與解密的密鑰是相同的,加解密速度很快,比如AES
非對稱加密:加密與解密的秘鑰是不同的,速度較慢,比如RSA
-
先看代碼(先會用在研究)
相關依賴:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.58</version>
</dependency>
1,RSA工具類:
package cn.wangtao.utils; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; /** * @ClassName RSAUtils * @Auth 桃子 * @Date 2019-6-25 15:15 * @Version 1.0 * @Description **/ public class RSAUtils { private static final String RSA = "RSA"; // 加密方式
private static final Logger logger= LoggerFactory.getLogger(RSAUtils.class); //獲取密鑰 public static KeyPair getKey() throws Exception { try { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA, new BouncyCastleProvider()); keyPairGenerator.initialize(2048); // 初始化密鑰長度 KeyPair keyPair = keyPairGenerator.generateKeyPair();// 生成密鑰對 return keyPair; } catch (Exception e) { logger.error("獲取RSA秘鑰對異常",e); throw new Exception("獲取RSA秘鑰對異常",e); } } //利用公鑰進行加密 public static String encryptStr(RSAPublicKey publicKey, String str) throws Exception { try { Cipher cipher = Cipher.getInstance(RSA, new BouncyCastleProvider()); cipher.init(Cipher.ENCRYPT_MODE, publicKey); //加密 byte[] bytes = getBytes(str.getBytes(), cipher); //2進行轉換成16進制 String result = CommonUtils.parseByte2HexStr(bytes); return result; } catch (Exception e) { logger.error("使用RSA公鑰進行加密異常",e); throw new Exception("使用RSA公鑰進行加密異常",e); } } //利用私鑰進行解密 public static String decryptStr(RSAPrivateKey privateKey, String str) throws Exception { try { Cipher cipher = Cipher.getInstance(RSA, new BouncyCastleProvider()); cipher.init(Cipher.DECRYPT_MODE, privateKey); // 用密鑰初始化此Cipher對象 //16進制轉換成2進制 byte[] bytes = CommonUtils.parseHexStr2Byte(str); //解密 byte[] bs = getBytes(bytes, cipher); String content=new String(bs,"utf-8"); return content; } catch (Exception e) { logger.error("使用RSA私鑰進行解密異常",e); throw new Exception("使用RSA私鑰進行解密異常",e); } } //通過cipher獲取字節數組 public static byte[] getBytes(byte[] bytes,Cipher cipher) throws Exception { int blockSize = cipher.getBlockSize(); // 返回塊的大小 int j = 0; ByteArrayOutputStream baos = new ByteArrayOutputStream(); while (bytes.length - j * blockSize > 0) { // 將二進制數據分塊寫入ByteArrayOutputStream中 if(bytes.length-j*blockSize>blockSize){ baos.write(cipher.doFinal(bytes, j * blockSize, blockSize)); }else{ baos.write(cipher.doFinal(bytes, j * blockSize,bytes.length-j*blockSize)); } j++; } baos.close(); byte[] byteArray = baos.toByteArray(); return byteArray; } //保存秘鑰對到文件 public void saveRSAKey(String fileName) throws Exception { FileOutputStream fos=null; ObjectOutputStream oos=null; try { KeyPair keyPair = getKey(); fos=new FileOutputStream(fileName); oos=new ObjectOutputStream(fos); //對象序列號 oos.writeObject(keyPair); } catch (Exception e) { logger.error("RSA秘鑰對保存到文件異常[{}]",fileName,e); throw new Exception("RSA秘鑰對保存到文件異常",e); }finally { if(oos!=null){ try { oos.close(); } catch (IOException e1) { e1.printStackTrace(); } } if(fos!=null){ try { fos.close(); } catch (IOException e1) { e1.printStackTrace(); } } } } }
/** * @ClassName RSAUtils * @Version 1.0 * @Description RSA工具類 **/ public class RSAUtils { private static final String RSA = "RSA"; public static final String MD5WITHRSA="MD5withRSA"; public static final String SHA1WITHRSA="SHA1withRSA"; private static Logger logger= LoggerFactory.getLogger(RSAUtils.class); /** * @desc 讀取秘鑰文本內容 * @createTime 2019年7月2日 下午5:20:38 * @param keyFile 秘鑰文件 * @return 秘鑰文本內容 * @throws Exception */ private static String initKeyByFile(File keyFile) throws Exception { if(keyFile.exists() && keyFile.isFile()) { BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new FileReader(keyFile)); StringBuilder stringBuilder = new StringBuilder(); String line = null; while ((line = bufferedReader.readLine()) != null) { if (line.length() == 0 || line.charAt(0) == '-') { continue; } stringBuilder.append(line).append(System.getProperty("line.separator")); } return stringBuilder.toString(); }catch (Exception e) { logger.error("讀取秘鑰文本內容異常",e); throw new Exception("讀取秘鑰文本內容異常",e); }finally { CommonUtils.closeReaderandWriter(bufferedReader, null); } } return null; } /** * @desc 獲取公鑰 * @author Liuweian * @createTime 2019年7月2日 下午5:19:34 * @param pubKeyFileName 公鑰文件地址 * @return PublicKey * @throws Exception */ public static PublicKey getPublicKeyByFile(File pubKeyFile) throws Exception { String keyContent = initKeyByFile(pubKeyFile); byte[] keyByte = Base64.decode(keyContent); KeyFactory kf = KeyFactory.getInstance(RSA); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyByte); return kf.generatePublic(keySpec); } /** * @desc 獲取私鑰 * @createTime 2019年7月2日 下午5:19:16 * @param priKeyFileName 私鑰文件地址 * @return PrivateKey * @throws Exception */ public static PrivateKey getPrivateKeyByFile(File priKeyFile) throws Exception { String keyContent = initKeyByFile(priKeyFile); byte[] keyByte = Base64.decode(keyContent); KeyFactory kf = KeyFactory.getInstance(RSA); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyByte); return kf.generatePrivate(keySpec); } /** * @desc 使用RSA中的私鑰對數據簽名 * @createTime 2019年7月2日 下午5:24:30 * @param privateKey 秘鑰對中的私鑰 * @param data 待加簽字節數組 * @param signType 加簽類型 * @return 加簽后的簽名 * @throws Exception */ public static String sign(byte[] data, PrivateKey privateKey, String signType) throws Exception { Signature signature = Signature.getInstance(signType); signature.initSign(privateKey); signature.update(data); byte[] signByte = signature.sign(); //Base64加密 return new String(Base64.encode(signByte)); } /** * @desc 使用RSA中的公鑰對簽名驗簽 * @createTime 2019年7月2日 下午5:24:30 * @param data 待驗簽字節數組 * @param sign 簽名 * @param publicKey 秘鑰對中的公鑰 * @param signType 加簽類型 * @return 驗簽是否成功 * @throws Exception */ public static boolean verify(byte[] data, byte[] sign, PublicKey publicKey, String signType) { try { Signature signature = Signature.getInstance(signType); signature.initVerify(publicKey); signature.update(data); //Base64解密 byte[] keyByte = Base64.decode(sign); return signature.verify(keyByte); } catch (Exception e) { logger.error("驗簽出現異常", e); return false; } } /** * @desc 使用RSA中的私鑰對數據簽名 加簽方式 MD5withRSA * @createTime 2019年7月2日 下午5:24:30 * @param privateKey 秘鑰對中的私鑰 * @param data 待加簽字節數組 * @return 加簽后的簽名 * @throws Exception */ public static String signMD5withRSA(byte[] data, PrivateKey privateKey) throws Exception { return sign(data, privateKey, MD5WITHRSA); } /** * @desc 使用RSA中的公鑰對簽名驗簽 驗簽方式 MD5withRSA * @createTime 2019年7月2日 下午5:24:30 * @param sign 簽名 * @param publicKey 秘鑰對中的公鑰 * @param signType 加簽類型 * @return 驗簽是否成功,失敗則異常拋出 * @throws Exception */ public static void verifyMD5withRSA(byte[] data, byte[] sign, PublicKey publicKey) throws Exception { if(!verify(data, sign, publicKey, MD5WITHRSA)) { throw new Exception("驗簽失敗"); } } /** * @desc 通過cipher獲取字節數組 分塊 * @createTime 2019年7月2日 下午5:21:33 * @param data 待加密的字節數組 * @param cipher * @return * @throws Exception */ public static byte[] getBytes(byte[] data, Cipher cipher) throws Exception { int blockSize = cipher.getBlockSize(); // 返回塊的大小 int j = 0; ByteArrayOutputStream baos = new ByteArrayOutputStream(); while (data.length - j * blockSize > 0) { // 將二進制數據分塊寫入ByteArrayOutputStream中 if(data.length - j * blockSize > blockSize) { baos.write(cipher.doFinal(data, j * blockSize, blockSize)); }else{ baos.write(cipher.doFinal(data, j * blockSize, data.length - j * blockSize)); } j++; } baos.close(); byte[] byteArray = baos.toByteArray(); return byteArray; } /** * @desc 利用公鑰進行 加密 * @createTime 2019年7月2日 下午5:24:30 * @param key 密鑰對的一個 * @param data 待加密字節數組 * @return 密文 * @throws Exception */ public static String encrypt(Key key, byte[] data) throws Exception { try { Cipher cipher = Cipher.getInstance(RSA, new BouncyCastleProvider()); cipher.init(Cipher.ENCRYPT_MODE, key); //加密 byte[] bytes = getBytes(data, cipher); //2進行轉換成16進制 String result = CommonUtils.parseByte2HexStr(bytes); return new String(Base64.encode(result.getBytes(CommonUtils.CODE_TYPE))); } catch (Exception e) { logger.error("使用RSA公鑰進行加密異常",e); throw new Exception("使用RSA公鑰進行加密異常",e); } } /** * @desc 利用私鑰進行 解密 * @createTime 2019年7月2日 下午5:23:10 * @param key 密鑰對的一個 * @param data 待解密的字節數組 * @return 明文 * @throws Exception */ public static String decrypt(Key key, byte[] data) throws Exception { try { Cipher cipher = Cipher.getInstance(RSA, new BouncyCastleProvider()); cipher.init(Cipher.DECRYPT_MODE, key); // 用密鑰初始化此Cipher對象 //16進制轉換成2進制 byte[] bytes = CommonUtils.parseHexStr2Byte(Base64.decode(data)); //解密 byte[] bs = getBytes(bytes, cipher); String content=new String(bs,CommonUtils.CODE_TYPE); return content; } catch (Exception e) { logger.error("使用RSA私鑰進行解密異常",e); throw new Exception("使用RSA私鑰進行解密異常",e); } } }
2,CommonUtils通用工具類:
package cn.wangtao.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
/**
* @ClassName CommonUtils
* @Auth 桃子
* @Date 2019-6-27 12:51
* @Version 1.0
* @Description
**/
public class CommonUtils {
private static final Logger logger= LoggerFactory.getLogger(CommonUtils.class);
//編碼方式
public static final String CODE_TYPE = "UTF-8";
//字符補全
private static final String[] consult = new String[]{"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G"};
//關流
public static void closeReaderandWriter(Reader reader, Writer writer){
if(writer!=null){
try {
writer.close();
} catch (IOException e) {
logger.error("關閉輸出流失敗",e);
}
}
if(reader!=null){
try {
reader.close();
} catch (IOException e) {
logger.error("關閉輸出流失敗",e);
}
}
}
//將16進制轉換為二進制
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length()/2];
for (int i = 0;i< hexStr.length()/2; i++) {
int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
//將二進制轉換成16進制
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
//補全字符
public static String completionCodeFor16Bytes(String str) throws Exception {
try{
int num = str.getBytes(CODE_TYPE).length;
int index = num%16;
//進行加密內容補全操作, 加密內容應該為 16字節的倍數, 當不足16*n字節是進行補全, 差一位時 補全16+1位
//補全字符 以 $ 開始,$后一位代表$后補全字符位數,之后全部以0進行補全;
if(index != 0){
StringBuffer sbBuffer = new StringBuffer(str);
if(16-index == 1){
sbBuffer.append("$" + consult[16-1] + addStr(16-1-1));
}else{
sbBuffer.append("$" + consult[16-index-1] + addStr(16-index-1-1));
}
str = sbBuffer.toString();
}
return str;
}catch (Exception e){
logger.error("使用AES加密前補全字符異常",e);
throw new Exception("使用AES加密前補全字符異常",e);
}
}
//追加字符
public static String addStr(int num){
StringBuffer sbBuffer = new StringBuffer("");
for (int i = 0; i < num; i++) {
sbBuffer.append("0");
}
return sbBuffer.toString();
}
//還原字符(進行字符判斷)
public static String resumeCodeOf16Bytes(String str) throws Exception{
int indexOf = str.lastIndexOf("$");
if(indexOf == -1){
return str;
}
String trim = str.substring(indexOf+1,indexOf+2).trim();
int num = 0;
for (int i = 0; i < consult.length; i++) {
if(trim.equals(consult[i])){
num = i;
}
}
if(num == 0){
return str;
}
return str.substring(0,indexOf).trim();
}
}
3,AESUtils通用工具類:
package cn.wangtao.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.interfaces.RSAPrivateKey;
import java.util.Map;
/**
* @ClassName AESUtils
* @Auth 桃子
* @Date 2019-6-27 12:05
* @Version 1.0
* @Description
**/
public class AESUtils {
private static final Logger logger= LoggerFactory.getLogger(AESUtils.class);
//填充類型
public static final String AES_TYPE = "AES/ECB/PKCS5Padding";
private static final String AES = "AES"; // 加密方式
public static final String DES_TYPE = "DES/ECB/PKCS5Padding";
private static final String DES = "DES"; // 加密方式
private final String defaultDesKey="11112222";//8位
//對字符串加密
public static String encryptStr(String content,String aesKey) throws Exception {
try {
SecretKeySpec key = new SecretKeySpec(aesKey.getBytes(),AES );
Cipher cipher = Cipher.getInstance(AES_TYPE);
cipher.init(Cipher.ENCRYPT_MODE, key);
//字符補全
String content16Str = CommonUtils.completionCodeFor16Bytes(content);
byte[] encryptedData = cipher.doFinal(content16Str.getBytes(CommonUtils.CODE_TYPE));
//2進制轉換成16進制
String hexStr = CommonUtils.parseByte2HexStr(encryptedData);
return hexStr;
} catch (Exception e) {
logger.error("使用AES對字符串加密異常",e);
throw new Exception("使用AES對字符串加密異常",e);
}
}
//對字符串解密
public static String decryptStr(String content,String aesKey) throws Exception {
try {
//16進制轉換成2進制
byte[] bytes = CommonUtils.parseHexStr2Byte(content);
SecretKeySpec key = new SecretKeySpec(
aesKey.getBytes(), AES);
Cipher cipher = Cipher.getInstance(AES_TYPE);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedData = cipher.doFinal(bytes);
String result=new String(decryptedData, CommonUtils.CODE_TYPE);
//還原字符
String orgResult = CommonUtils.resumeCodeOf16Bytes(result);
return orgResult;
} catch (Exception e) {
logger.error("使用AES對字符串解密異常",e);
throw new Exception("使用AES對字符串解密異常",e);
}
}
//對文件加密
public static File encryptFile(File orgFile, File encryptFile, Map<String,Object> context) throws Exception {
logger.info("使用AES對文件加密開始,源文件地址[{}]加密后文件地址[{}]",orgFile.getPath(),encryptFile.getPath());
BufferedReader br=null;
BufferedWriter bw=null;
try{
//獲取AESKEY ,如果沒有為默認
String aesKey = (String) context.get(Dirt.AES_KEY);
br=new BufferedReader(new FileReader(orgFile));
bw=(BufferedWriter)context.get(Dirt.BUFFEREDWRITER);
if(null==bw){
bw=new BufferedWriter(new FileWriter(encryptFile));
}
String len=null;
while (null!=(len=br.readLine())){
String encrypt= encryptStr(len,aesKey);
bw.write(encrypt);
bw.newLine();
bw.flush();
}
logger.info("使用AES對文件加密結束,源文件地址[{}]加密后文件地址[{}]",orgFile.getPath(),encryptFile.getPath());
return encryptFile;
}catch (Exception e){
logger.error("使用AES對文件加密異常,源文件地址[{}]加密后文件地址[{}]",orgFile.getPath(),encryptFile.getPath(),e);
throw new Exception("使用AES對文件加密異常",e);
}finally {
CommonUtils.closeReaderandWriter(br,bw);
}
}
//對文本解密,返回解密文件后的文件
public static File decryptFile(File decryptfile, File encryptFile,Map<String,Object> context) throws Exception {
logger.info("使用AES對文件解密開始,源加密文件地址[{}]解密后文件地址[{}]",encryptFile.getPath(),decryptfile.getPath());
BufferedReader br=null;
BufferedWriter bw=null;
try{
if(decryptfile.exists()){
decryptfile.delete();
}
//邊讀邊加密邊寫
br=new BufferedReader(new FileReader(encryptFile));
bw=new BufferedWriter(new FileWriter(decryptfile));
String len=null;
String aesKey=null;
//判斷是否加密
RSAPrivateKey privateKey= (RSAPrivateKey) context.get(Dirt.RSAPRIVATEKEY);
if(null!=privateKey){
StringBuffer sb=new StringBuffer();
while ((len=br.readLine())!=null){
sb.append(len);
if(len.equals("\n")||len.equals("")||len.equals("\r\n")||len.equals("\r")){
aesKey=RSAUtils.decryptStr(privateKey,sb.toString());
break;
}
}
}
if(null==aesKey){
aesKey=(String) context.get(Dirt.AES_KEY);
}
logger.info("aesKey[{}]",aesKey);
if(aesKey!=null){
while ((len=br.readLine())!=null){
String decrypt= decryptStr(len,aesKey);
bw.write(decrypt);
bw.flush();
bw.newLine();
}
}
logger.info("使用AES對文件解密結束,源加密文件地址[{}]解密后文件地址[{}]",encryptFile.getPath(),decryptfile.getPath());
return decryptfile;
}catch (Exception e){
logger.error("使用AES對文件解密異常,源加密文件地址[{}]解密后文件地址[{}]",encryptFile.getPath(),decryptfile.getPath(),e);
throw new Exception("使用AES對文件解密異常",e);
}finally {
CommonUtils.closeReaderandWriter(br,bw);
}
}
}
/** * @ClassName AESUtils * @Version 1.0 * @Description AES工具類 **/ public class AESUtils { private static final Logger logger = LoggerFactory.getLogger(AESUtils.class); /** 加密方式 */ public static final String AES = "AES"; public static final String AES_Type = "AES/ECB/PKCS5Padding"; /** * @desc 隨機生成AES加密秘鑰 * @createTime 2019年7月2日 下午5:36:00 * @return 隨機的AES的秘鑰串 16位 * @throws Exception */ public static String getAesKey() { Random random = new Random(); String result = ""; for(int i = 0; i< 16; i++){ String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num"; // 輸出字母還是數字 if( "char".equalsIgnoreCase(charOrNum) ) { // 輸出是大寫字母還是小寫字母 // int temp = random.nextInt(2) % 2 == 0 ? 65 : 97; result += (char)(random.nextInt(26) + 65); } else { result += String.valueOf(random.nextInt(10)); } } return result; } /** * @desc AES加密操作 * @createTime 2019年7月2日 下午5:30:33 * @param data 待加密字節數組 * @param aesKey 秘鑰串 * @return 密文 * @throws Exception */ public static String encrypt(byte[] data, String aesKey) throws Exception { try { SecretKeySpec key = new SecretKeySpec(aesKey.getBytes(CommonUtils.CODE_TYPE), AES); Cipher cipher = Cipher.getInstance(AES); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encryptedData = cipher.doFinal(data); //2進制轉換成16進制並返回密文 String result = CommonUtils.parseByte2HexStr(encryptedData); return new String(Base64.encode(result.getBytes(CommonUtils.CODE_TYPE))); } catch (Exception e) { logger.error("使用AES對字符串加密異常", e); throw new Exception("使用AES對字符串加密異常", e); } } /** * @desc AES解密操作 * @createTime 2019年7月2日 下午5:31:37 * @param data 待解密字節數組 * @param aesKey 秘鑰串 * @return 明文 * @throws Exception */ public static String decrypt(byte[] data, String aesKey) throws Exception { try { // 16進制轉換成2進制 byte[] bytes = CommonUtils.parseHexStr2Byte(Base64.decode(data)); SecretKeySpec key = new SecretKeySpec(aesKey.getBytes(CommonUtils.CODE_TYPE), AES); Cipher cipher = Cipher.getInstance(AES); cipher.init(Cipher.DECRYPT_MODE, key); //執行解密 byte[] decryptedData = cipher.doFinal(bytes); //還原字符並返回 return new String(decryptedData, CommonUtils.CODE_TYPE); } catch (Exception e) { logger.error("使用AES對字符串解密異常", e); throw new Exception("使用AES對字符串解密異常", e); } } }
4,Dirt常量
package cn.wangtao.utils;
import java.security.interfaces.RSAPublicKey;
/**
* @ClassName Dirt
* @Auth 桃子
* @Date 2019-6-27 14:20
* @Version 1.0
* @Description
**/
public class Dirt {
public static final String UPLOADFILEURL="uploadFileUrl";
public static final String AES_KEY="aesKey";
public static final String RSAPUBLICKEY="rsaPublicKey";
public static final String RSAPRIVATEKEY="rsaPrivateKey";
public final static String RETURNCODE="returnCode";
public final static String RETURNMSG="returnMsg";
public final static String FILENAME="fileName";
public final static String ORGFILENAME="orgFileName";
public final static String ENCRYPTFILE="encryptFile";
public static final String BUFFEREDWRITER="bufferedWriter"; //是為了在原始文件中進行補充加密
//返回碼
public final static String SUCCESSCODE="000000";
public final static String FAILEDCODE="999999";
//加密文件所放的目錄
public final static String BASELOCALDIR="XXX"; //基本目錄路徑
public final static String ENCRYPTLOCALDIR="encrypt"; //加密文件目錄
}
-
AES的介紹
詳情請參考:https://blog.csdn.net/qq_28205153/article/details/55798628
-
RSA的介紹
詳情請參考:https://www.cnblogs.com/jiftle/p/7903762.html