注:文章皆为个人纪录,可用性请以最终结果为准,若有错还请大佬们指出,谢谢!
一、加解密的准备资料
1.1 公钥 (用于加密)
1.2 私钥(用于解密)
1.3 私钥key (用于验证私钥)
导入依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk15on</artifactId>
<version>1.64</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.64</version>
</dependency>
配置准备材料
package com.jxz.owner.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* Pgp 基础信息配置
* User: jiangXueZhi
* Date: 2022/02/17
*/
@Data
@Configuration
public class PaymayaConfig {
/**
* GPG加解密——公钥
*/
public static String GPG_PUBLIC_KEY = "xxx";
/**
* GPG加解密——公钥的文件名
*/
public static String GPG_PUBLIC_KEY_FILENAME = "public-key.gpg";
/**
* GPG加解密——私钥
*/
public static String GPG_PRIVATE_KEY = "xxx";
/**
* GPG加解密——私钥的key
*/
public static String GPG_PRIVATE_KEY_PWD = "xxx";
/**
* GPG加解密——私钥的文件名
*/
public static String GPG_PRIVATE_KEY_FILENAME = "private-key.gpg";
}
二、程序入口
@Test
public void pgpHandle() {
/* 公共参数 */
String path = "D:\\"; // 文件保存的地址(C盘默认无写入权限)
boolean deleteLocalFile = false; // 是否需要删除本地生成的文件
/* 加密方法的参数 */
String clearText = CLEAR_TEXT; // 明文内容
String clearTextFileName = "待加密"; // 明文的文件名
/* 解密方法的参数 */
String cipherText = CIPHER_TEXT; // 密文内容
String cipherTextFileName = "待解密"; // 密文的文件名
boolean encryp = false; //加密:true 解密:false
if (!encryp) { // 加密
String cipherTextResult = iPgpService.encryptFile(clearText, path, clearTextFileName, deleteLocalFile);
System.out.println(cipherTextResult); // 密文
} else { // 解密
String cleartext = iPgpService.decryptFile(cipherText, path, cipherTextFileName, deleteLocalFile);
System.out.println(cleartext); // 明文
}
}
三、加解密接口 IPgpService
package com.jxz.owner.service;
import com.jxz.owner.config.PaymayaConfig;
import com.jxz.owner.utils.FileUtils;
import com.jxz.owner.utils.PgpUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Service;
import java.security.Security;
/**
* @Description: TODO
* @Date: 2022/2/17
* @Author: jiangXueZhi
*/
@Service
public class PgpServiceImpl implements IPgpService {
/**
* 加密
*
* @param clearText 明文
* @param path 生成文件的路径,如 D:\
* @param clearTextFileName 明文文件名
* @param deleteLocalFile 是否删除生成的文件
* @return 密文
*/
@Override
public String encryptFile(String clearText, String path, String clearTextFileName, boolean deleteLocalFile) {
Security.addProvider(new BouncyCastleProvider()); // 此行代码不可省略
String clearPath = FileUtils.writeStrToFile(path + clearTextFileName + ".txt", clearText, true); // 明文文件地址
String cipherPath = clearPath.split("\\.")[0] + "_cipherText.txt"; // 密文文件地址
String publicKeysPath = FileUtils.writeStrToFile(path + PaymayaConfig.GPG_PUBLIC_KEY_FILENAME, PaymayaConfig.GPG_PUBLIC_KEY, false); // 公钥地址
PgpUtils.encryptFile(cipherPath, clearPath, publicKeysPath, true, true);
// 读取密文内容
String cipherText = FileUtils.readStrFromFile(cipherPath);
// 删除文件
FileUtils.deleteFile(publicKeysPath); // 删除公钥文件
if (deleteLocalFile) {
FileUtils.deleteFile(cipherPath); // 删除密文文件
FileUtils.deleteFile(clearPath); // 删除明文文件
}
return cipherText;
}
/**
* 解密
*
* @param cipherText 密文
* @param path 生成文件的路径,如 D:\
* @param cipherTextFileName 密文文件名
* @param deleteLocalFile 是否删除生成的文件
* @return 明文
*/
@Override
public String decryptFile(String cipherText, String path, String cipherTextFileName, boolean deleteLocalFile) {
Security.addProvider(new BouncyCastleProvider()); // 此行代码不可省略
String password = PaymayaConfig.GPG_PRIVATE_KEY_PWD; // 私钥的Key
String cipherPath = FileUtils.writeStrToFile(path + cipherTextFileName + ".txt", cipherText, true); // 密文文件地址
String privateKeysPath = FileUtils.writeStrToFile(path + PaymayaConfig.GPG_PRIVATE_KEY_FILENAME, PaymayaConfig.GPG_PRIVATE_KEY, false); // 私钥地址
String clearPath = cipherPath.split("\\.")[0] + "_clearText.txt"; // 明文文件地址
PgpUtils.decryptFile(cipherPath, privateKeysPath, password.toCharArray(), clearPath);
// 读取明文内容
String cleartext = FileUtils.readStrFromFile(clearPath);
// 删除文件
FileUtils.deleteFile(privateKeysPath); // 删除私钥文件
if (deleteLocalFile) {
FileUtils.deleteFile(cipherPath); // 删除密文文件
FileUtils.deleteFile(clearPath); // 删除明文文件
}
return cleartext;
}
}
四、各种工具类
4.1 PgpUtils
package com.jxz.owner.utils;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.jcajce.*;
import org.bouncycastle.util.io.Streams;
import java.io.*;
import java.security.SecureRandom;
import java.util.Iterator;
/**
* PGP 加解密工具类
*/
public class PgpUtils {
/**
* 加密方法
*
* @param outputFileName 输出得位置
* @param inputFileName 密文位置
* @param encKeyFileName 公钥
* @param armor
* @param withIntegrityCheck
*/
public static void encryptFile(
String outputFileName,
String inputFileName,
String encKeyFileName,
boolean armor,
boolean withIntegrityCheck) {
OutputStream out = null;
try {
out = new BufferedOutputStream(new FileOutputStream(outputFileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
PGPPublicKey encKey = null;
encKey = readPublicKey(encKeyFileName);
encryptFile(out, inputFileName, encKey, armor, withIntegrityCheck);
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 解密方法
*
* @param inputFileName 要解密得文件位置
* @param keyFileName 私钥
* @param passwd 私钥的Key
* @param defaultFileName 解密后的文件输出位置
*/
public static void decryptFile(
String inputFileName,
String keyFileName,
char[] passwd,
String defaultFileName)
{
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(inputFileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
InputStream keyIn = null;
try {
keyIn = new BufferedInputStream(new FileInputStream(keyFileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
decryptFile(in, keyIn, passwd, defaultFileName);
try {
if (keyIn != null) {
keyIn.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* decrypt the passed in message stream
*/
public static void decryptFile(
InputStream in,
InputStream keyIn,
char[] passwd,
String defaultFileName)
{
try {
in = PGPUtil.getDecoderStream(in);
} catch (IOException e) {
e.printStackTrace();
}
try {
JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
PGPEncryptedDataList enc = null;
Object o = null;
try {
o = pgpF.nextObject();
} catch (IOException e) {
e.printStackTrace();
}
//
// the first object might be a PGP marker packet.
//
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
try {
enc = (PGPEncryptedDataList) pgpF.nextObject();
} catch (IOException e) {
e.printStackTrace();
}
}
//
// find the secret key
//
Iterator it = null;
if (enc != null) {
it = enc.getEncryptedDataObjects();
}
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
PGPSecretKeyRingCollection pgpSec = null;
try {
pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(keyIn), new JcaKeyFingerprintCalculator());
} catch (IOException e) {
e.printStackTrace();
}
if (it != null) {
while (sKey == null && it.hasNext()) {
pbe = (PGPPublicKeyEncryptedData) it.next();
if (pgpSec != null) {
sKey = findSecretKey(pgpSec, pbe.getKeyID(), passwd);
}
}
}
if (sKey == null) {
throw new IllegalArgumentException("secret key for message not found.");
}
InputStream clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(sKey));
JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
Object message = null;
try {
message = plainFact.nextObject();
} catch (IOException e) {
e.printStackTrace();
}
if (message instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) message;
JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
try {
message = pgpFact.nextObject();
} catch (IOException e) {
e.printStackTrace();
}
}
if (message instanceof PGPLiteralData) {
PGPLiteralData ld = (PGPLiteralData) message;
String outFileName = ld.getFileName();
if (outFileName.length() == 0) {
outFileName = defaultFileName;
} else {
outFileName = defaultFileName;
}
InputStream unc = ld.getInputStream();
OutputStream fOut = null;
try {
fOut = new BufferedOutputStream(new FileOutputStream(outFileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
Streams.pipeAll(unc, fOut);
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fOut != null) {
fOut.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} else if (message instanceof PGPOnePassSignatureList) {
throw new PGPException("encrypted message contains a signed message - not literal data.");
} else {
throw new PGPException("message is not a simple encrypted file - type unknown.");
}
if (pbe.isIntegrityProtected()) {
try {
if (!pbe.verify()) {
System.err.println("message failed integrity check");
} else {
System.err.println("message integrity check passed");
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.err.println("no message integrity check");
}
} catch (PGPException e) {
System.err.println(e);
if (e.getUnderlyingException() != null) {
e.getUnderlyingException().printStackTrace();
}
}
}
public static void encryptFile(
OutputStream out,
String fileName,
PGPPublicKey encKey,
boolean armor,
boolean withIntegrityCheck)
{
if (armor) {
out = new ArmoredOutputStream(out);
}
try {
byte[] bytes = compressFile(fileName, CompressionAlgorithmTags.ZIP);
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("BC"));
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider("BC"));
OutputStream cOut = null;
try {
cOut = encGen.open(out, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
try {
if (cOut != null) {
cOut.write(bytes);
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (cOut != null) {
cOut.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (armor) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (PGPException e) {
System.err.println(e);
if (e.getUnderlyingException() != null) {
e.getUnderlyingException().printStackTrace();
}
}
}
public static byte[] compressFile(String fileName, int algorithm) {
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(algorithm);
try {
PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY,
new File(fileName));
} catch (IOException e) {
e.printStackTrace();
}
try {
comData.close();
} catch (IOException e) {
e.printStackTrace();
}
return bOut.toByteArray();
}
/**
* Search a secret key ring collection for a secret key corresponding to keyID if it
* exists.
*
* @param pgpSec a secret key ring collection.
* @param keyID keyID we want.
* @param pass passphrase to decrypt secret key with.
* @return the private key.
*/
public static PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) {
PGPSecretKey pgpSecKey = null;
try {
pgpSecKey = pgpSec.getSecretKey(keyID);
} catch (PGPException e) {
e.printStackTrace();
}
if (pgpSecKey == null) {
return null;
}
try {
return pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(pass));
} catch (PGPException e) {
e.printStackTrace();
}
return null;
}
public static PGPPublicKey readPublicKey(String fileName) {
InputStream keyIn = null;
try {
keyIn = new BufferedInputStream(new FileInputStream(fileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
PGPPublicKey pubKey = readPublicKey(keyIn);
try {
if (keyIn != null) {
keyIn.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return pubKey;
}
/**
* A simple routine that opens a key ring file and loads the first available key
* suitable for encryption.
*
* @param input data stream containing the public key data
* @return the first public key found.
*/
public static PGPPublicKey readPublicKey(InputStream input) {
PGPPublicKeyRingCollection pgpPub = null;
try {
pgpPub = new PGPPublicKeyRingCollection(
PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator());
} catch (IOException | PGPException e) {
e.printStackTrace();
}
//
// we just loop through the collection till we find a key suitable for encryption, in the real
// world you would probably want to be a bit smarter about this.
//
Iterator keyRingIter = null;
if (pgpPub != null) {
keyRingIter = pgpPub.getKeyRings();
}
if (keyRingIter != null) {
while (keyRingIter.hasNext()) {
PGPPublicKeyRing keyRing = (PGPPublicKeyRing) keyRingIter.next();
Iterator keyIter = keyRing.getPublicKeys();
while (keyIter.hasNext()) {
PGPPublicKey key = (PGPPublicKey) keyIter.next();
if (key.isEncryptionKey()) {
return key;
}
}
}
}
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
public static PGPSecretKey readSecretKey(String fileName) {
InputStream keyIn = null;
try {
keyIn = new BufferedInputStream(new FileInputStream(fileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
PGPSecretKey secKey = readSecretKey(keyIn);
try {
if (keyIn != null) {
keyIn.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return secKey;
}
/**
* A simple routine that opens a key ring file and loads the first available key
* suitable for signature generation.
*
* @param input stream to read the secret key ring collection from.
* @return a secret key.
*/
public static PGPSecretKey readSecretKey(InputStream input) {
PGPSecretKeyRingCollection pgpSec = null;
try {
pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator());
} catch (IOException | PGPException e) {
e.printStackTrace();
}
//
// we just loop through the collection till we find a key suitable for encryption, in the real
// world you would probably want to be a bit smarter about this.
//
Iterator keyRingIter = null;
if (pgpSec != null) {
keyRingIter = pgpSec.getKeyRings();
}
if (keyRingIter != null) {
while (keyRingIter.hasNext()) {
PGPSecretKeyRing keyRing = (PGPSecretKeyRing) keyRingIter.next();
Iterator keyIter = keyRing.getSecretKeys();
while (keyIter.hasNext()) {
PGPSecretKey key = (PGPSecretKey) keyIter.next();
if (key.isSigningKey()) {
return key;
}
}
}
}
throw new IllegalArgumentException("Can't find signing key in key ring.");
}
}
4.2 FileUtils
package com.jxz.owner.utils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.Objects;
/**
* @Description: 文件相关工具类
*
* @Date: 2021/10/26
* @Author: jiangXueZhi
*/
@Slf4j
public class FileUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUtils.class);
/**
* 获取文件的后缀名
*
* @param appendDot 是否拼接.
* @return
*/
public static String getFileSuffix(String fullFileName, boolean appendDot) {
if (fullFileName == null || fullFileName.indexOf(".") < 0 || fullFileName.length() <= 1) {
return "";
}
return (appendDot ? "." : "") + fullFileName.substring(fullFileName.lastIndexOf(".") + 1);
}
/**
* 往本地文件中写入内容
* 若本地文件不存在,则自动创建,反之则覆盖
*
* @param filePath 本地文件地址
* @param content 写入的内容
* @param uniqueFileName 是否要求文件名唯一
* @return 本地文件地址
*/
public static String writeStrToFile(String filePath, String content, boolean uniqueFileName) {
if (uniqueFileName) {
String[] split = filePath.split("\\.");
filePath = split[0] + "_" + NumberUtils.getRandomNickname(5) + "." + split[1];
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(filePath);
fos.write(content.getBytes());
LOGGER.info("文件写入成功,文件地址:{}", filePath);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return filePath;
}
/**
* 读取文件的内容
*
* @param path 文件地址
* @return 文件内容
*/
public static String readStrFromFile(String path) {
StringBuilder sb = new StringBuilder();
Reader reader = null;
try {
File file = new File(path);
reader = new InputStreamReader(new FileInputStream(file));
int ch;
while ((ch = Objects.requireNonNull(reader).read()) != -1) {
sb.append((char) ch);
}
LOGGER.info("文件读取成功,文件地址:{}", path);
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 删除单个文件
*
* @param path 被删除文件的文件名
* @return 单个文件删除成功返回true,否则返回false
*/
public static boolean deleteFile(String path) {
File file = new File(path);
// 路径为文件且不为空则进行删除
if (file.isFile() && file.exists()) {
LOGGER.info("文件删除成功,文件地址:{}", path);
return file.delete();
}
return false;
}
/**
* 删除目录(文件夹)以及目录下的文件
*
* @param sPath 被删除目录的文件路径
* @return 目录删除成功返回true,否则返回false
*/
public static boolean deleteDirectory(String sPath) {
//如果sPath不以文件分隔符结尾,自动添加文件分隔符
if (!sPath.endsWith(File.separator)) {
sPath = sPath + File.separator;
}
File dirFile = new File(sPath);
//如果dir对应的文件不存在,或者不是一个目录,则退出
if (!dirFile.exists() || !dirFile.isDirectory()) {
return false;
}
boolean flag = true;
//删除文件夹下的所有文件(包括子目录)
File[] files = dirFile.listFiles();
if (files != null) {
for (File file : files) {
//删除子文件
if (file.isFile()) {
flag = deleteFile(file.getAbsolutePath());
if (!flag) break;
} //删除子目录
else {
flag = deleteDirectory(file.getAbsolutePath());
if (!flag) break;
}
}
}
if (!flag) return false;
//删除当前目录
LOGGER.info("目录删除成功,目录地址:{}", sPath);
return dirFile.delete();
}
}
4.3 NumberUtils
package com.jxz.owner.utils;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Description: 数字相关工具类
*
* @Date: 2021/10/26
* @Author: jiangXueZhi
*/
public class NumberUtils {
/**
* 生成随机数字length位数
*
* @param length 生成随机数的长度
* @return 随机数字length位数
*/
public static String getRandomNickname(int length) {
StringBuilder val = new StringBuilder();
Random random = new Random();
for (int i = 0; i < length; i++) {
val.append(random.nextInt(10));
}
return val.toString();
}
/**
* 字符串去重
*
* @param str 字符串
* @return 去重后的字符串
*/
public static String duplicateRemoval(String str) {
StringBuilder sb = new StringBuilder(str);
String rs = sb.reverse().toString().replaceAll("(.)(?=.*\\1)", "");
StringBuilder out = new StringBuilder(rs);
return out.reverse().toString();
}
/**
* 字符串中的所有数字映射为对应的字母
*
* @param str 字符串
* @return 映射后的字符串
*/
public static String strMapping(String str) {
String regEx="[^0-9]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(str);
String trim = m.replaceAll("").trim();
String[] digital = duplicateRemoval(trim).split(""); // 字符串中去重后的所有数字集合
for (String s : digital) {
switch (s) {
case "0":
str = str.replace("0", "Q");
break;
case "1":
str = str.replace("1", "R");
break;
case "2":
str = str.replace("2", "S");
break;
case "3":
str = str.replace("3", "T");
break;
case "4":
str = str.replace("4", "U");
break;
case "5":
str = str.replace("5", "V");
break;
case "6":
str = str.replace("6", "W");
break;
case "7":
str = str.replace("7", "X");
break;
case "8":
str = str.replace("8", "Y");
break;
case "9":
str = str.replace("9", "Z");
break;
default:
}
}
return str;
}
}
五、查看对应的目录文件