首先请大家一定要搞清楚加密解密概念:
下面这是百度出来的。
如果只是单方面采用非对称性加密算法,其实有两种方式,用于不同用处.
-
第一种是签名,使用私钥加密,公钥解密,用于让所有公钥所有者验证私钥所有者的身份并且用来防止私钥所有者发布的内容被篡改.但是不用来保证内容不被他人获得.
-
第二种是加密,用公钥加密,私钥解密,用于向公钥所有者发布信息,这个信息可能被他人篡改,但是无法被他人获得.
如果甲想给乙发一个安全的保密的数据,那么应该甲乙各自有一个私钥,甲先用乙的公钥加密这段数据,再用自己的私钥加密这段加密后的数据.最后再发给乙,这样确保了内容即不会被读取,也不会被篡改.
折磨了我一个星期,就是因为配合的同事跟我说用公钥解密,所以一定要搞清楚概念。
代码块:
KeyBasedLargeFileProcessor.java
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
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.Iterator;/*
* 加密与解密
* */public class KeyBasedLargeFileProcessor {/** 解密* @Para inputFileName 输入的文件路径* @Para keyFileName 私钥文件路径* @Para passwd 私钥密码* @Para defaultFileName 输出文件路径* */public static void decryptFile(String inputFileName,String keyFileName,char[] passwd,String defaultFileName)throws IOException, NoSuchProviderException {InputStream in = new BufferedInputStream(new FileInputStream(inputFileName));InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName));decryptFile(in, keyIn, passwd, defaultFileName);keyIn.close();in.close();}public static void decryptFile(InputStream in,InputStream keyIn,char[] passwd,String defaultFileName)throws IOException, NoSuchProviderException {in = PGPUtil.getDecoderStream(in);try {//创建PGP对象JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);//PGP加密的数据列表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(PGPUtil.getDecoderStream(keyIn),new JcaKeyFingerprintCalculator());System.out.println("pgpSec=" + pgpSec);while (sKey == null && it.hasNext()) {pbe = (PGPPublicKeyEncryptedData) it.next();System.out.println("pbe=" + pbe);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)));//解密数据的对象流PGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);//压缩解密对象PGPCompressedData cData = (PGPCompressedData) plainFact.nextObject();//获得压缩对象流InputStream compressedStream = new BufferedInputStream(cData.getDataStream());PGPObjectFactory pgpFact = new BcPGPObjectFactory(compressedStream);Object message = pgpFact.nextObject();if (message instanceof PGPLiteralData) {PGPLiteralData ld = (PGPLiteralData) message;String outFileName = ld.getFileName();if (outFileName.length() != 0) {outFileName = defaultFileName;}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();}}}/** 加密* @Para outputFileName 输出文件的路径* @Para inputFileName 输入文件的路径* @Para encKeyFileName 公钥* @Para armor* @Para withIntegrityCheck 完整性检查* */public static void encryptFile(String outputFileName,String inputFileName,String encKeyFileName,boolean armor,boolean withIntegrityCheck)throws IOException, NoSuchProviderException, PGPException {OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName));PGPPublicKey encKey = PGPExampleUtil.readPublicKey(encKeyFileName);encryptFile(out, inputFileName, encKey, armor, withIntegrityCheck);out.close();}public static void encryptFile(OutputStream out,String fileName,PGPPublicKey encKey,boolean armor,boolean withIntegrityCheck)throws IOException, NoSuchProviderException {if (armor) {out = new ArmoredOutputStream(out);}try {PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("BC"));cPk.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider("BC"));OutputStream cOut = cPk.open(out, new byte[1 << 16]);PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);PGPUtil.writeFileToLiteralData(comData.open(cOut), PGPLiteralData.BINARY, new File(fileName), new byte[1 << 16]);comData.close();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 {Security.addProvider(new BouncyCastleProvider());//encryptFile("D://1.PGP.tmp","D://123.txt","D://kkprivate.asc",true,true);decryptFile("D://104110082202643_ODD_02_20191227.PGP", "D://kkprivate.asc", "67216688".toCharArray(), "D://104110082202643_ODD_02_20191227.txt");}
}
PGPExampleUtil.java
package org.kxsj.com.pgp;import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.jcajce.JcaPGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;import java.io.*;
import java.security.GeneralSecurityException;
import java.security.NoSuchProviderException;
import java.util.Iterator;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* @throws PGPException* @throws NoSuchProviderException*/static PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass)throws PGPException{PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);if (pgpSecKey == null){return null;}PGPPrivateKey bc = pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(pass));return bc;}/** 读取公钥* @Para 文件路径* */static PGPPublicKey readPublicKey(String fileName) throws IOException, PGPException{//文件InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName));//字节//ByteArrayInputStream keyIn = new ByteArrayInputStream(fileName.getBytes());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* @return* @throws IOException* @throws PGPException*/static PGPPublicKey readPublicKey(InputStream input) throws IOException, PGPException{// JcaPGPObjectFactory pgpPub = new JcaPGPObjectFactory(PGPUtil.getDecoderStream(input));//PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input),new KeyFingerPrintCalculator());JcaPGPPublicKeyRingCollection jcaPgpPub =new JcaPGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input));//// 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 = jcaPgpPub.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{JcaPGPPublicKeyRingCollection pgpSec = new JcaPGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input));//// 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.");}/*** verify the signature in in against the file fileName.*/private boolean verifySignature(String fileName, byte[] b, InputStream keyIn) throws GeneralSecurityException, IOException, PGPException {//in = PGPUtil.getDecoderStream(in);PGPObjectFactory pgpFact = new JcaPGPObjectFactory(b);PGPSignatureList p3 = null;Object o = pgpFact.nextObject();if (o instanceof PGPCompressedData) {PGPCompressedData c1 = (PGPCompressedData) o;pgpFact = new JcaPGPObjectFactory(c1.getDataStream());p3 = (PGPSignatureList) pgpFact.nextObject();} else {p3 = (PGPSignatureList) o;}JcaPGPPublicKeyRingCollection pgpPubRingCollection = new JcaPGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyIn));InputStream dIn = new BufferedInputStream(new FileInputStream(fileName));PGPSignature sig = p3.get(0);PGPPublicKey key = pgpPubRingCollection.getPublicKey(sig.getKeyID());sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(new BouncyCastleProvider()), key);int ch;while ((ch = dIn.read()) >= 0) {sig.update((byte) ch);}dIn.close();if (sig.verify()) {return true;} else {return false;}}private byte[] createSignature(String fileName, InputStream keyIn, char[] pass, boolean armor) throws GeneralSecurityException, IOException, PGPException {PGPSecretKey pgpSecKey = readSecretKey(keyIn);PGPPrivateKey pgpPrivKey = pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(new BouncyCastleProvider()).build(pass));PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSecKey.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1).setProvider(new BouncyCastleProvider()));sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);ByteArrayOutputStream byteOut = new ByteArrayOutputStream();ArmoredOutputStream aOut = new ArmoredOutputStream(byteOut);BCPGOutputStream bOut = new BCPGOutputStream(byteOut);InputStream fIn = new BufferedInputStream(new FileInputStream(fileName));int ch;while ((ch = fIn.read()) >= 0) {sGen.update((byte) ch);}aOut.endClearText();fIn.close();sGen.generate().encode(bOut);if (armor) {aOut.close();}return byteOut.toByteArray();}}
maven依赖:
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcpg-jdk15on</artifactId><version>1.66</version></dependency><dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.66</version></dependency>
JDK默认Policy只能支持<=128位Key,GPG的密钥从1024-2048,所以必须扩展该Policy。需要下载jce的安全策略文件,去jre路径下C:\Program Files\Java\jdk1.8.0_241\jre\lib\security\policy覆盖。