引言:
目前CSDN上,大部分都是对于文本的PGP加解密博文,但实际开发中,可能会与渠道对接使用PGP加解密报文,所以这里整理了下,对文本的加解密工具,希望对你有帮助。
一、引入PGP的依赖
<!-- PGP 加解密 依赖-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk15on</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<optional>true</optional>
</dependency>
二、编写工具类
package com.bop.util.pgp;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
public class BCPGPUtils {
public static PGPPublicKey readPublicKey(String fileName) throws IOException, PGPException {
InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName));
PGPPublicKey pubKey = readPublicKey(keyIn);
keyIn.close();
return pubKey;
}
public static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(in),
new JcaKeyFingerprintCalculator());
Iterator<PGPPublicKeyRing> keyRings = pgpPub.getKeyRings();
while (keyRings.hasNext()) {
PGPPublicKeyRing keyRing = keyRings.next();
Iterator<PGPPublicKey> publicKeys = keyRing.getPublicKeys();
while (publicKeys.hasNext()) {
PGPPublicKey key = publicKeys.next();
if (key.isEncryptionKey()) {
return key;
}
}
}
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
public static PGPSecretKey readSecretKey(String fileName) throws IOException, PGPException {
InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName));
PGPSecretKey secKey = readSecretKey(keyIn);
keyIn.close();
return secKey;
}
public static PGPSecretKey readSecretKey(InputStream in) throws IOException, PGPException {
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(in),
new JcaKeyFingerprintCalculator());
Iterator<PGPSecretKeyRing> keyRings = pgpSec.getKeyRings();
while (keyRings.hasNext()) {
PGPSecretKeyRing keyRing = keyRings.next();
Iterator<PGPSecretKey> secretKeys = keyRing.getSecretKeys();
while (secretKeys.hasNext()) {
PGPSecretKey key = secretKeys.next();
if (key.isSigningKey()) {
return key;
}
}
}
throw new IllegalArgumentException("Can't find signing key in key ring.");
}
/**
* Load a secret key ring collection from keyIn and find the private key
* corresponding to keyID if it exists.
*/
public static PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass)
throws PGPException {
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null) {
return null;
}
return pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(pass));
}
public static PGPPrivateKey findPrivateKey(InputStream keyIn, long keyID, char[] pass)
throws IOException, PGPException {
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn),
new JcaKeyFingerprintCalculator());
return findPrivateKey(pgpSec.getSecretKey(keyID), pass);
}
public static PGPPrivateKey findPrivateKey(PGPSecretKey pgpSecKey, char[] pass)
throws PGPException {
if (pgpSecKey == null)
return null;
return pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(pass));
}
}
三、编写加密类
package com.bop.util.pgp;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPOnePassSignature;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
/**
* PGP 加密类
*
* @author neal.armstrong
* @version 1.0
* @since 2021.11.22
*/
public class PgpMemEncryptor {
private final String publicKeyPath;
private final String privateKeyPath;
private InputStream PUBLIC_KEY;
private PGPPublicKey publicKey;
private PGPPrivateKey privateKey;
public PgpMemEncryptor(String publicKeyPath, String privateKeyPath) {
this.publicKeyPath = publicKeyPath;
this.privateKeyPath = privateKeyPath;
}
public PGPPublicKey getPublicKey() {
if (publicKey == null) {
try {
publicKey = BCPGPUtils.readPublicKey(publicKeyPath);
} catch (IOException | PGPException e) {
e.printStackTrace();
}
}
return publicKey;
}
public PGPPrivateKey getPrivateKey(String purhapse) {
if (privateKey == null) {
try {
PGPSecretKey secretKey = BCPGPUtils.readSecretKey(privateKeyPath);
privateKey = secretKey
.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider())
.build(purhapse.toCharArray()));
} catch (PGPException | IOException e) {
e.printStackTrace();
}
}
return privateKey;
}
@SneakyThrows
private InputStream getPublicIs() {
if (PUBLIC_KEY == null) {
PUBLIC_KEY = Files.newInputStream(Paths.get(publicKeyPath));
}
return PUBLIC_KEY;
}
private static void getProvider() {
Provider provider = Security.getProvider("BC");
if (provider == null) {
provider = new BouncyCastleProvider();
Security.addProvider(provider);
}
}
public String encryptFile(String inputFile, boolean withIntegrityCheck) throws IOException, PGPException {
getProvider();
byte[] rawText = inputFile.getBytes();
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
PGPPublicKey pubKey = BCPGPUtils.readPublicKey(getPublicIs());
ArmoredOutputStream outputStream = new ArmoredOutputStream(encOut);
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
OutputStream open = lData.open(comData.open(bOut), PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, rawText.length, PGPLiteralData.NOW);
open.write(rawText);
lData.close();
comData.close();
BcPGPDataEncryptorBuilder builder = new BcPGPDataEncryptorBuilder(PGPEncryptedData.AES_256);
builder.setWithIntegrityPacket(withIntegrityCheck);
builder.setSecureRandom(new SecureRandom());
PGPEncryptedDataGenerator cpk = new PGPEncryptedDataGenerator(builder);
cpk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(pubKey));
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cpk.open(outputStream, bytes.length);
cOut.write(bytes);
cOut.close();
outputStream.close();
return new String(encOut.toByteArray(), StandardCharsets.UTF_8);
}
public String encryptAndSignFile(String inputFile, boolean withIntegrityCheck, String purhapse) {
getProvider();
InputStream input = IOUtils.toInputStream(inputFile);
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
int DEFAULT_BUFFER_SIZE = 16 * 1024;
PGPSecretKey pgpSec = BCPGPUtils.readSecretKey(privateKeyPath);
PGPPrivateKey signingKey = getPrivateKey(purhapse);
PGPPublicKey publicKey = pgpSec.getPublicKey();
String userid = (String) publicKey.getUserIDs().next();
BcPGPDataEncryptorBuilder dataEncryptor = new BcPGPDataEncryptorBuilder(PGPEncryptedData.AES_256);
dataEncryptor.setWithIntegrityPacket(withIntegrityCheck);
dataEncryptor.setSecureRandom(new SecureRandom());
PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(dataEncryptor);
encryptedDataGenerator.addMethod((new BcPublicKeyKeyEncryptionMethodGenerator(getPublicKey())));
OutputStream finalOut = new BufferedOutputStream(new ArmoredOutputStream(output), DEFAULT_BUFFER_SIZE);
OutputStream encOut = encryptedDataGenerator.open(finalOut, new byte[DEFAULT_BUFFER_SIZE]);
PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
BufferedOutputStream compressedOut = new BufferedOutputStream(compressedDataGenerator.open(encOut));
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(
publicKey.getAlgorithm(), HashAlgorithmTags.SHA256));
signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, signingKey);
PGPSignatureSubpacketGenerator pgpSignatureSubpacketGenerator = new PGPSignatureSubpacketGenerator();
pgpSignatureSubpacketGenerator.setSignerUserID(false, userid);
signatureGenerator.setHashedSubpackets(pgpSignatureSubpacketGenerator.generate());
PGPOnePassSignature onePassSignature = signatureGenerator.generateOnePassVersion(false);
onePassSignature.encode(compressedOut);
PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator(true);
OutputStream literalOut = literalDataGenerator.open(compressedOut, PGPLiteralData.BINARY,
PGPLiteralData.CONSOLE, PGPLiteralData.NOW, new byte[1 << 16]);
byte[] buffer = new byte[1 << 16];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
literalOut.write(buffer, 0, bytesRead);
signatureGenerator.update(buffer, 0, bytesRead);
literalOut.flush();
}
// Close Literal data stream and add signature
literalOut.close();
literalDataGenerator.close();
signatureGenerator.generate().encode(compressedOut);
// Close all other streams
compressedOut.close();
compressedDataGenerator.close();
encOut.close();
encryptedDataGenerator.close();
finalOut.close();
input.close();
return new String(output.toByteArray(), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
四、编写解密类
package com.bop.util.pgp;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPOnePassSignature;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
import org.bouncycastle.util.io.Streams;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Provider;
import java.security.Security;
import java.util.Iterator;
/**
* PGP 解密类
*
* @author neal.armstrong
* @version 1.0
* @since 2021.11.22
*/
public class PgpMemDecryptor {
private final String publicKeyPath;
private final String privateKeyPath;
public PgpMemDecryptor(String publicKeyPath, String privateKeyPath) {
this.publicKeyPath = publicKeyPath;
this.privateKeyPath = privateKeyPath;
}
private InputStream PUBLIC_KEY;
private InputStream PRIVATE_KEY;
// int compressionAlgorithm;
// int hashAlgorithm;
// int symmetricKeyAlgorithm;
private static void getProvider() {
Provider provider = Security.getProvider("BC");
if (provider == null) {
provider = new BouncyCastleProvider();
Security.addProvider(provider);
}
}
@SneakyThrows
private InputStream getPublicIs() {
if (PUBLIC_KEY == null) {
PUBLIC_KEY = Files.newInputStream(Paths.get(publicKeyPath));
}
return PUBLIC_KEY;
}
@SneakyThrows
private InputStream getPrivateIs() {
if (PRIVATE_KEY == null) {
PRIVATE_KEY = Files.newInputStream(Paths.get(privateKeyPath));
}
return PRIVATE_KEY;
}
public String decryptAndVerifyFile(String content, String passPhrase) {
getProvider();
InputStream verifyKeyInput = getPrivateIs();
InputStream decryptKeyInput = getPublicIs();
char[] passwd;
try {
passwd = passPhrase.toCharArray();
InputStream input = PGPUtil.getDecoderStream(IOUtils.toInputStream(content));
PGPObjectFactory pgpF = new PGPObjectFactory(input, new BcKeyFingerprintCalculator());
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
Iterator<?> it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(verifyKeyInput),
new BcKeyFingerprintCalculator());
while (sKey == null && it.hasNext()) {
pbe = (PGPPublicKeyEncryptedData) it.next();
sKey = BCPGPUtils.findSecretKey(pgpSec, pbe.getKeyID(), passwd);
}
if (sKey == null) {
throw new IllegalArgumentException("secret key for message not found.");
}
InputStream clear = pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));
// pbe.getSymmetricAlgorithm(new BcPublicKeyDataDecryptorFactory(sKey));
PGPObjectFactory plainFact = new PGPObjectFactory(clear, new BcKeyFingerprintCalculator());
Object message;
PGPOnePassSignatureList onePassSignatureList = null;
PGPSignatureList signatureList = null;
PGPCompressedData compressedData;
message = plainFact.nextObject();
ByteArrayOutputStream actualOutput = new ByteArrayOutputStream();
while (message != null) {
if (message instanceof PGPCompressedData) {
compressedData = (PGPCompressedData) message;
plainFact = new PGPObjectFactory(compressedData.getDataStream(), new BcKeyFingerprintCalculator());
message = plainFact.nextObject();
// this.compressionAlgorithm = compressedData.getAlgorithm();
}
if (message instanceof PGPLiteralData) {
Streams.pipeAll(((PGPLiteralData) message).getInputStream(), actualOutput);
} else if (message instanceof PGPOnePassSignatureList) {
onePassSignatureList = (PGPOnePassSignatureList) message;
} else if (message instanceof PGPSignatureList) {
signatureList = (PGPSignatureList) message;
}
message = plainFact.nextObject();
}
actualOutput.close();
PGPPublicKey publicKey;
byte[] outputBytes = actualOutput.toByteArray();
if (onePassSignatureList == null || signatureList == null) {
throw new PGPException("Poor PGP. Signatures not found.");
} else {
for (int i = 0; i < onePassSignatureList.size(); i++) {
PGPOnePassSignature ops = onePassSignatureList.get(0);
PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(
PGPUtil.getDecoderStream(decryptKeyInput), new BcKeyFingerprintCalculator());
publicKey = pgpRing.getPublicKey(ops.getKeyID());
if (publicKey != null) {
ops.init(new BcPGPContentVerifierBuilderProvider(), publicKey);
ops.update(outputBytes);
PGPSignature signature = signatureList.get(i);
if (ops.verify(signature)) {
// this.hashAlgorithm = ops.getHashAlgorithm();
}
}
}
}
return new String(outputBytes);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String decryptFile(String content, String passphrase)
throws Exception {
getProvider();
InputStream fIn = IOUtils.toInputStream(content);
InputStream in = PGPUtil.getDecoderStream(fIn);
InputStream keyIn = getPrivateIs();
char[] passPhrase = passphrase.toCharArray();
PGPObjectFactory pgpF = new PGPObjectFactory(in, new BcKeyFingerprintCalculator());
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
Iterator<PGPPublicKeyEncryptedData> it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
while (sKey == null && it.hasNext()) {
pbe = it.next();
sKey = BCPGPUtils.findPrivateKey(keyIn, pbe.getKeyID(), passPhrase);
}
InputStream clear = pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));
// this.symmetricKeyAlgorithm = pbe.getSymmetricAlgorithm(new BcPublicKeyDataDecryptorFactory(sKey));
PGPObjectFactory pgpFact = new PGPObjectFactory(clear, new BcKeyFingerprintCalculator());
o = pgpFact.nextObject();
if (o instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) o;
pgpFact = new PGPObjectFactory(cData.getDataStream(), new BcKeyFingerprintCalculator());
o = pgpFact.nextObject();
}
if (o instanceof PGPLiteralData) {
PGPLiteralData ld = (PGPLiteralData) o;
InputStream unc = ld.getInputStream();
return IOUtils.toString(unc);
}
return null;
}
}
五、测试
请选择正确的用法,例如使用加签加密方法时,对应请使用验签解密方法。使用加密方法,则直接使用解密方法即可。
五、结尾
由于时间仓促,并未对代码进行优化,许多流文件,可能遗漏并未进行完美关闭,还请注意使用。