Reputation: 63
I have an existing PGP v 1.46 code in maintenance project. now I need to update to 1.56 and it's not working. a lot of classes and methods are changed. I have no idea about PGP.
Here is a sample demo of the exact code.
plugins {
id 'java'
}
group 'com.encryptor.pgp'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
compile fileTree(dir: 'src/main/resources/libs', include: '*.jar')
compile group: 'org.bouncycastle', name: 'bcpg-jdk15on', version: '1.46'
compile group: 'org.bouncycastle', name: 'bcprov-ext-jdk15on', version: '1.46'
compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.46'
}
when i update from 1.46 to 1.56, it gives error.
processor
package com.encryptor.pgp;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class PGPFileProcessor {
private String encryptFile;
private String decryptFile;
private String passphrase;
private String publicKeyFile;
private String privateKeyFile;
private String plainTextFile;
private boolean asciiArmored = false;
private boolean integrityCheck = true;
public PGPFileProcessor() {
}
public void setPublicKeyFile(String publicKeyFile) {
this.publicKeyFile = publicKeyFile;
}
public void setPrivateKeyFile(String privateKeyFile) {
this.privateKeyFile = privateKeyFile;
}
public void setEncryptFile(String encryptFile) {
this.encryptFile = encryptFile;
}
public void setDecryptFile(String decryptFile) {
this.decryptFile = decryptFile;
}
public void setPassphrase(String passphrase) {
this.passphrase = passphrase;
}
public void setPlainTextFile(String plainTextFile) {
this.plainTextFile = plainTextFile;
}
public void setAsciiArmored(boolean asciiArmored) {
this.asciiArmored = asciiArmored;
}
public void setIntegrityCheck(boolean integrityCheck) {
this.integrityCheck = integrityCheck;
}
public boolean encrypt() throws Exception {
FileInputStream keyIn = new FileInputStream(publicKeyFile);
FileOutputStream out = new FileOutputStream(encryptFile, true);
PGPUtil.encryptFile(out, plainTextFile, PGPUtil.readPublicKey(keyIn), asciiArmored, integrityCheck);
out.close();
keyIn.close();
return true;
}
public boolean decrypt() throws Exception {
FileInputStream in = new FileInputStream(encryptFile);
FileInputStream keyIn = new FileInputStream(privateKeyFile);
FileOutputStream out = new FileOutputStream(decryptFile);
PGPUtil.decryptFile(in, out, keyIn, passphrase.toCharArray());
in.close();
out.close();
keyIn.close();
return true;
}
}
Main Class
package com.encryptor.pgp;
public class PGPMain {
public static void main(String[] args) throws Exception {
PGPFileProcessor pgpFileProcessor = new PGPFileProcessor();
pgpFileProcessor.setEncryptFile("enc.txt"); pgpFileProcessor.setDecryptFile("dec.txt");
pgpFileProcessor.setPassphrase("pgpencr");
pgpFileProcessor.setInputFile("plain.txt");
private boolean asciiArmored = false;
pgpFileProcessor.setPublicKeyFile("publickey.key");
pgpFileProcessor.setPrivateKeyFile("pivatekey.key");
pgpFileProcessor.encrypt();*/
pgpFileProcessor.encrypt();
pgpFileProcessor.decrypt();
}
}
Util
package com.encryptor.pgp;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import java.io.*;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Iterator;
public class PGPUtil {
@SuppressWarnings("unchecked")
public static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {
in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);/* 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. */
PGPPublicKey key = null;/* iterate through the key rings. */
Iterator<PGPPublicKeyRing> rIt = pgpPub.getKeyRings();
while (key == null && rIt.hasNext()) {
PGPPublicKeyRing kRing = rIt.next();
Iterator<PGPPublicKey> kIt = kRing.getPublicKeys();
while (key == null && kIt.hasNext()) {
PGPPublicKey k = kIt.next();
if (k.isEncryptionKey()) key = k;
}
}
if (key == null) throw new IllegalArgumentException("Can't find encryption key in key ring.");
return key;
}
private static PGPPrivateKey findSecretKey(InputStream keyIn, long keyID, char[] pass) throws IOException, PGPException, NoSuchProviderException {
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(org.bouncycastle.openpgp.PGPUtil.getDecoderStream(keyIn));
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null) return null;
return pgpSecKey.extractPrivateKey(pass, "BC");
}
@SuppressWarnings("unchecked")
public static void decryptFile(InputStream in, OutputStream out, InputStream keyIn, char[] passwd) throws Exception {
Security.addProvider(new BouncyCastleProvider());
in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);
PGPObjectFactory pgpF = new PGPObjectFactory(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<PGPPublicKeyEncryptedData> it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
while (sKey == null && it.hasNext()) {
pbe = it.next();
sKey = findSecretKey(keyIn, pbe.getKeyID(), passwd);
}
if (sKey == null) throw new IllegalArgumentException("Secret key for message not found.");
InputStream clear = pbe.getDataStream(sKey, "BC");
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object message = plainFact.nextObject();
if (message instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) message;
PGPObjectFactory pgpFact = new PGPObjectFactory(cData.getDataStream());
message = pgpFact.nextObject();
}
if (message instanceof PGPLiteralData) {
PGPLiteralData ld = (PGPLiteralData) message;
InputStream unc = ld.getInputStream();
int ch;
while ((ch = unc.read()) >= 0) out.write(ch);
} 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() && !pbe.verify()) throw new PGPException("Message failed integrity check");
}
public static void encryptFile(OutputStream out, String fileName, PGPPublicKey encKey, boolean armor, boolean withIntegrityCheck) throws IOException, NoSuchProviderException, PGPException {
Security.addProvider(new BouncyCastleProvider());
if (armor) out = new ArmoredOutputStream(out);
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
org.bouncycastle.openpgp.PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY, new File(fileName));
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(), "BC");
cPk.addMethod(encKey);
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes);
cOut.close();
out.close();
}
}
Upvotes: 0
Views: 4867
Reputation: 6414
It looks like as that your "old" PGP encryption methods were taken from a Bouncy Castle example, so I used the renewed Bouncy Castle examples in https://github.com/bcgit/bc-java/tree/master/pg/src/main/java/org/bouncycastle/openpgp/examples for a simple test.
You wrote that the problem shows up when updating Bouncy Castle to version 1.56 - this is outdated as well and the actual version is 1.65 and my example works with this version (and OpenJDK 11.0.6). Take my files as a working basis for your maintenance.
First you need a PGP keypair - for my example I generated one with 'RSAKeyPairGenerator.java -a myidentity mypassphrase' to get the files 'secret.asc' (private key) and 'pub.asc' (public key).
For PGP file encryption you need two more files - 'KeyBasedLargeFileProcessor.java' and 'PGPExampleUtil.java.' In class 'KeyBasedLargeFileProcessor.java' I changed the constructor of the methods decryptFile and encryptFile from 'private' to 'public' to get access from the PGPMain.java.
It's just one line of code for pgp file encryption and another line for pgp file decryption. As the original filename can get stored within the encrypted file I added a "rename-method" to change the original filename 'plain.txt' to 'plain_org.txt'. Be aware that the decryptFile-method will overwrite an existing file without warning or notice and there is no propper exception handling.
The complete set of files including Bouncy Castle library is available here: https://github.com/java-crypto/Stackoverflow/tree/master/PGP_Encryption_after_Update_Not_Working. You need the bcprov-jdk15to18-165.jar and bcpg-jdk15on-165.jar!
PGPMain.java:
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchProviderException;
import java.security.Security;
public class PGPMain {
public static void main(String[] args) throws NoSuchProviderException, IOException, PGPException {
System.out.println("https://stackoverflow.com/questions/61927913/bouncycastle-update-from-1-46-to-1-56-not-working");
Security.addProvider(new BouncyCastleProvider()); // get bouncy castle: https://www.bouncycastle.org/latest_releases.html
System.out.println("\nJava version: " + Runtime.version() + " BouncyCastle Version: " + Security.getProvider("BC"));
// create a keypair with RSAKeyPairGenerator.java
// encryption
KeyBasedLargeFileProcessor.encryptFile("enc.txt", "plain.txt", "pub.asc", false, true);
// rename plaintextfile as it will be overwritten by decryptFile (filename is stored within encrypted file)
File file = new File("plain.txt");
file.renameTo(new File("plain_org.txt"));
// decryption will generate the decrypted file with original filename !
KeyBasedLargeFileProcessor.decryptFile("enc.txt", "secret.asc", "mypassphrase".toCharArray(), "defaultfilename.txt");
// return the original filename, to change this behavior change the code in class KeyBasedLargeFileProcessor lines 142-146
}
}
KeyBasedLargeFileProcessor.java
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Iterator;
import org.bouncycastle.bcpg.ArmoredOutputStream;
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.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
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;
/**
* A simple utility class that encrypts/decrypts public key based
* encryption large files.
* <p>
* To encrypt a file: KeyBasedLargeFileProcessor -e [-a|-ai] fileName publicKeyFile.<br>
* If -a is specified the output file will be "ascii-armored".
* If -i is specified the output file will be have integrity checking added.
* <p>
* To decrypt: KeyBasedLargeFileProcessor -d fileName secretKeyFile passPhrase.
* <p>
* Note 1: this example will silently overwrite files, nor does it pay any attention to
* the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase
* will have been used.
* <p>
* Note 2: this example generates partial packets to encode the file, the output it generates
* will not be readable by older PGP products or products that don't support partial packet
* encoding.
* <p>
* Note 3: if an empty file name has been specified in the literal data object contained in the
* encrypted packet a file with the name filename.out will be generated in the current working directory.
*/
public class KeyBasedLargeFileProcessor
{ // source: https://github.com/bcgit/bc-java/blob/master/pg/src/main/java/org/bouncycastle/openpgp/examples/KeyBasedLargeFileProcessor.java
// changed from private to public
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();
}
/**
* decrypt the passed in message stream
*/
private static void decryptFile(
InputStream in,
InputStream 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(
PGPUtil.getDecoderStream(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);
PGPCompressedData cData = (PGPCompressedData)plainFact.nextObject();
InputStream compressedStream = new BufferedInputStream(cData.getDataStream());
JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(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();
}
}
}
// changed from private to public
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();
}
private 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());
if (args.length == 0)
{
System.err.println("usage: KeyBasedLargeFileProcessor -e|-d [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]");
return;
}
if (args[0].equals("-e"))
{
if (args[1].equals("-a") || args[1].equals("-ai") || args[1].equals("-ia"))
{
encryptFile(args[2] + ".asc", args[2], args[3], true, (args[1].indexOf('i') > 0));
}
else if (args[1].equals("-i"))
{
encryptFile(args[2] + ".bpg", args[2], args[3], false, true);
}
else
{
encryptFile(args[1] + ".bpg", args[1], args[2], false, false);
}
}
else if (args[0].equals("-d"))
{
decryptFile(args[1], args[2], args[3].toCharArray(), new File(args[1]).getName() + ".out");
}
else
{
System.err.println("usage: KeyBasedLargeFileProcessor -d|-e [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]");
}
}
}
Upvotes: 1