介绍
最近和联通的数据生成系统对接需要使用PGP工具,网上查了资料,调了一整天终于出来了,下面介绍下使用方法以及碰到的一些的小坑
使用方法
依赖jar包
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk15on</artifactId>
<version>1.64</version>
</dependency>
bcprov-jdk15on.jar 常用的加解密jar包,相信大家都很熟悉了,我们通过它来实现PGP加解密及生成公私钥对
生成公私钥对的方法
public class PgpUtil {
/**
* 私有方法,用于生成指定位宽的PGP RSA密钥对
*
* @param rsaWidth_ RSA密钥位宽
* @return 未经私钥加密的PGP密钥对
* @throws Exception IO错误,数值错误等
*/
private static PGPKeyPair generateKeyPair(int rsaWidth_) throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");//获取密钥对生成器实例
kpg.initialize(rsaWidth_);//设定RSA位宽
KeyPair kp = kpg.generateKeyPair();//生成RSA密钥对
return new JcaPGPKeyPair(PGPPublicKey.RSA_GENERAL, kp, new Date());//返回根据日期,密钥对生成的PGP密钥对
}
/**
* 获取PGP密钥<br>
* 密钥是将密钥对的私钥部分用对称的加密方法CAST-128算法加密,再加上公钥部分
*
* @param identity_ 密钥ID也就是key值,可以用来标记密钥属于谁
* @param passPhrase_ 密钥的密码,用来解出私钥
* @param rsaWidth_ RSA位宽
* @return PGP密钥
* @throws Exception IO错误和数值错误等
*/
public static PGPSecretKey getSecretKey(String identity_, String passPhrase_, int rsaWidth_) throws Exception {
char[] passPhrase = passPhrase_.toCharArray(); //将passPharse转换成字符数组
PGPKeyPair keyPair = PgpUtil.generateKeyPair(rsaWidth_); //生成RSA密钥对
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); //使用SHA1作为证书的散列算法
/**
* 用证书等级生成的认证,将公私钥对和PGP ID密码绑定构造PGP密钥(SecretKey)
*
* @param certificationLevel PGP密钥的证书等级
* @param keyPair 需要绑定的公私钥对
* @param id 需要绑定的ID
* @param checksumCalculator 散列值计算器,用于计算私钥密码散列
* @param hashedPcks the hashed packets to be added to the certification.(先不管)
* @param unhashedPcks the unhashed packets to be added to the certification.(也先不管)
* @param certificationSignerBuilder PGP证书的生成器
* @param keyEncryptor 如果需要加密私钥,需要在这里传入私钥加密器
* @throws PGPException 一些PGP错误
*/
return new PGPSecretKey(
PGPSignature.DEFAULT_CERTIFICATION,
keyPair,
identity_,
sha1Calc,
null,
null,
new JcaPGPContentSignerBuilder(keyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1),
//密钥的加密方式
new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.CAST5, sha1Calc).setProvider("BC").build(passPhrase)
);
}
@SuppressWarnings("restriction")
public static void main(String[] args) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
String passPhrase_ = "123456789";
char[] passPhrase = passPhrase_.toCharArray(); //将passPharse转换成字符数组
PGPSecretKey secretKey = PgpUtil.getSecretKey("wathdata", passPhrase_, 2048);
// 这里打印私钥-------------重要
String privateKeyString = new BASE64Encoder().encode(secretKey.getEncoded());
System.out.println(privateKeyString);
PGPPublicKey publicKey = secretKey.getPublicKey();
//FileOutputStream fileOutputStream = new FileOutputStream("c://1.txt");
byte[] encoded = publicKey.getEncoded();
// 这里打印公钥----------------重要
String publicKeyString = new BASE64Encoder().encode(encoded);
System.out.println(publicKeyString);
}
}
也可以使用 gpg4win 软件生成的密钥来获取公钥私钥
下载地址: gpg4win 生成后密钥文件:
文件加解密
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.bouncycastle.util.io.Streams;
import java.io.*;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Base64;
import java.util.Iterator;
public class KeyBasedFileProcessorKey {
/**
* @param inputFileName 要解密的文件名
* @param key 私钥
* @param passwd 私钥解密key
* @param defaultFileName 输出解密的文件
* @throws IOException
* @throws NoSuchProviderException
*/
public static void decryptFile(String inputFileName, String key, char[] passwd, String defaultFileName) throws IOException, NoSuchProviderException {
InputStream in = new BufferedInputStream(new FileInputStream(inputFileName));
byte[] decode = Base64.getDecoder().decode(key);
decryptFile(in, decode, passwd, defaultFileName);
in.close();
}
/**
* decrypt the passed in message stream
*/
private static void decryptFile(InputStream in, byte[] keyIn, char[] passwd, String defaultFileName) throws IOException, NoSuchProviderException {
in = PGPUtil.getDecoderStream(in);
try {
JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
// the first object might be a PGP marker packet.
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
// find the secret key
Iterator it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(keyIn, new JcaKeyFingerprintCalculator());
while (sKey == null && it.hasNext()) {
pbe = (PGPPublicKeyEncryptedData) it.next();
sKey = PGPExampleUtil.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 = plainFact.nextObject();
if (message instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) message;
JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
message = pgpFact.nextObject();
}
if (message instanceof PGPLiteralData) {
PGPLiteralData ld = (PGPLiteralData) message;
String outFileName = ld.getFileName();
if (outFileName.length() == 0) {
outFileName = defaultFileName;
} else {
/**
* modify 20160520
* set fileName
* 不同的系统可能源文件的包含的路径信息不同。
*/
String separator = "";
if (outFileName.contains("/")) {
separator = "/";
} else if (outFileName.contains("\\")) {
separator = "\\";
}
String fileName = outFileName.substring(outFileName.lastIndexOf(File.separator) + 1);
String defseparator = "";
if (defaultFileName.contains("/")) {
defseparator = "/";
} else if (defaultFileName.contains("\\")) {
defseparator = "\\";
}
defaultFileName = defaultFileName.substring(0, defaultFileName.lastIndexOf(defseparator));
outFileName = defaultFileName + File.separator + fileName;
}
InputStream unc = ld.getInputStream();
OutputStream fOut = new BufferedOutputStream(new FileOutputStream(outFileName));
Streams.pipeAll(unc, fOut);
fOut.close();
} 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()) {
if (!pbe.verify()) {
System.err.println("message failed integrity check");
} else {
System.err.println("message integrity check passed");
}
} else {
System.err.println("no message integrity check");
}
} catch (PGPException e) {
System.err.println(e);
if (e.getUnderlyingException() != null) {
e.getUnderlyingException().printStackTrace();
}
}
}
/**
* @param outputFileName 输出的加密文件名 2.pgp
* @param inputFileName 输入的要加密的文件
* @param encryKey 公钥
* @param armor true
* @param withIntegrityCheck true
* @throws IOException
* @throws NoSuchProviderException
* @throws PGPException
*/
public static void encryptFile(String outputFileName, String inputFileName, String encryKey, boolean armor, boolean withIntegrityCheck) throws IOException, NoSuchProviderException, PGPException {
OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName));
Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(encryKey);
PGPPublicKey encKey = PGPExampleUtil.readPublicKey(new ByteArrayInputStream(decode));
encryptFile(out, inputFileName, encKey, armor, withIntegrityCheck);
out.close();
}
private static void encryptFile(OutputStream out, String fileName, PGPPublicKey encKey, boolean armor, boolean withIntegrityCheck) throws IOException, NoSuchProviderException {
if (armor) {
out = new ArmoredOutputStream(out);
}
try {
byte[] bytes = PGPExampleUtil.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 = encGen.open(out, bytes.length);
cOut.write(bytes);
cOut.close();
if (armor) {
out.close();
}
} catch (PGPException e) {
System.err.println(e);
if (e.getUnderlyingException() != null) {
e.getUnderlyingException().printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
//public key
String keyString = "public key";
//private key
String privateKeyString = "private key";
Security.addProvider(new BouncyCastleProvider());
encryptFile("C:\\pgp\\testFiles\\New folder\\test1.txt.pgp", "C:\\pgp\\testFiles\\New folder\\test1.txt", keyString, true, true); // 加密文件
decryptFile("C:\\pgp\\testFiles\\New folder\\test1.txt.pgp", privateKeyString, "password".toCharArray(), "C:\\pgp\\testFiles\\New folder\\test3.txt");// 解密文件
}
}
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchProviderException;
import java.util.Iterator;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
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;
class PGPExampleUtil
{
static byte[] compressFile(String fileName, int algorithm) throws IOException
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(algorithm);
PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY,
new File(fileName));
comData.close();
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.
* @throws PGPException
* @throws NoSuchProviderException
*/
static PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass)
throws PGPException, NoSuchProviderException
{
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null)
{
return null;
}
return pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(pass));
}
static PGPPublicKey readPublicKey(String fileName) throws IOException, PGPException
{
InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName));
PGPPublicKey pubKey = readPublicKey(keyIn);
keyIn.close();
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.
* @throws IOException
* @throws PGPException
*/
static PGPPublicKey readPublicKey(InputStream input) throws IOException, PGPException
{
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator());
//
// 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 = pgpPub.getKeyRings();
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.");
}
static PGPSecretKey readSecretKey(String fileName) throws IOException, PGPException
{
InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName));
PGPSecretKey secKey = readSecretKey(keyIn);
keyIn.close();
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.
* @throws IOException on a problem with using the input stream.
* @throws PGPException if there is an issue parsing the input stream.
*/
static PGPSecretKey readSecretKey(InputStream input) throws IOException, PGPException
{
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator());
//
// 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 = pgpSec.getKeyRings();
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.");
}
}
文件加密解密可以用代码和gpg4win软件互相验证