PGP Encryption/Decryption in Java

A new version of the code (and a new post) has been posted at :
http://sloanseaman.com/wordpress/2012/05/13/revisited-pgp-encryptiondecryption-in-java/
Please note that if you are using Bouncy Castles libraries < 1.47 you should use the code in this post

I have a requirement where I need to decrypt a file that is PGP encrypted for a project I am working on. At the same time I need a unit test for my code so I need to encrypt a file as well.

I started digging around and found that there is really no documentation I could find that covered the whole process. Some postings only covered encryption, some decryption, but none covered both as well as how to make the public/private keys.

So, here you go 🙂

The Downloads/Installs

Before you even start coding we need to discuss Java Security. The JRE by default ships with security for cryptography called Java Cryptography Extension (JCE). The JCE will support PGP but because import/export of cryptography can be sketchy, it only supports weak keys by default (think keys that wouldn’t be too hard to crack). PGP won’t work under this environment so you need to first address this.

JCE Update

Credit where due: This part is taken mainly from this article on jce policy files

Go to the Java download page and go all the way to the bottom and download the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files. The number at the end (currently 6 or 7) is just the JRE version.

Extract the files and copy the two jars to your JRE’s /lib/security directory. This will override two existing jars and will allow you to use strong keys in security.

PGP Tools

Next you need to create you public and private key files. Since Symantec bought PGP in 2010 most of the freeware stuff has disappeared and most links lead to Symantec’s website. I found an old freeware version of PGP (Windows only) at http://www.pgpi.org/products/pgp/versions/freeware/win32/6.5.8/. Please note that newer version don’t work on Windows 7 or are owned by Symantac.

Install the program and then launch it by running PGPKeys.exe. To use it:

  1. Select Keys -> New Key
  2. Click Next
  3. Enter your information
  4. Click Next
  5. Use the Diffie-Hellman/DSS key Pair Type
  6. Click Next
  7. Select your Key Pair Size (I just used 1024 since I’m not that worried about it for now)
  8. Click Next
  9. Leave your Key Expiration as never expires
  10. Click Next
  11. Enter the passphrase (password) you want to use
  12. Click Next
  13. If you entered less than 8 characters you will be warned. Click Next
  14. Key will be generated
  15. Click Next
  16. Click Finish

The files that were generated and that you will need to use in your code can be found by going to Edit -> Options -> Files.
Find these files and copy them to someplace where your code can reach them.

Side note: If you just need a basic public/private key you can generate ones at https://www.igolder.com/pgp/generate-key/

BouncyCastle’s PGP Libraries

I’m using the PGP library from Bouncy Castle. Honestly it’s poorly documented and rather complex but it’s the best and most common one that I could find.

Either download the latest from http://www.bouncycastle.org/latest_releases.html or use maven like so:

    <dependency>
	<groupId>org.bouncycastle</groupId>
	<artifactId>bcpg-jdk16</artifactId>
        <version>1.45</version>
    </dependency>

You are now (finally) ready to actually code something!

The Code

First lets write a general util that will handle any InputStreams (shouldn’t we all be using Readers by now?) and then lets write one that wraps the first one and will handle files for us.

Here is the first util which will handle any type of InputStream:

import java.io.ByteArrayOutputStream;
import java.io.File;
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.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;

/**
 * Taken from org.bouncycastle.openpgp.examples
 *
 * @author seamans
 *
 */
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;
    }

    /**
     * Load a secret key ring collection from keyIn and find the secret key corresponding to
     * keyID if it exists.
     *
     * @param keyIn input stream representing a key ring collection.
     * @param keyID keyID we want.
     * @param pass passphrase to decrypt secret key with.
     * @return
     * @throws IOException
     * @throws PGPException
     * @throws NoSuchProviderException
     */
    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");
    }

    /**
     * decrypt the passed in message stream
     */
    @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()) {
            if (!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();
    }

}

I’m not going to go over it line-by-line because, honestly, I haven’t looked at it that hard so I’m not sure what it’s all doing 😉 Sorry.

Next let’s write a util that allows us to easily work with files (and Spring, if you are into that sort of thing :))

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class PGPFileProcessor {

	private String passphrase;

	private String keyFile;

	private String inputFile;

	private String outputFile;

	private boolean asciiArmored = false;

	private boolean integrityCheck = true;

	public boolean encrypt() throws Exception {
		FileInputStream keyIn = new FileInputStream(keyFile);
        FileOutputStream out = new FileOutputStream(outputFile);
        PGPUtil.encryptFile(out, inputFile, PGPUtil.readPublicKey(keyIn),
        	asciiArmored, integrityCheck);
        out.close();
        keyIn.close();
        return true;
	}

	public boolean decrypt() throws Exception {
		 FileInputStream in = new FileInputStream(inputFile);
         FileInputStream keyIn = new FileInputStream(keyFile);
         FileOutputStream out = new FileOutputStream(outputFile);
         PGPUtil.decryptFile(in, out, keyIn, passphrase.toCharArray());
         in.close();
         out.close();
         keyIn.close();
         return true;
	}

	public boolean isAsciiArmored() {
		return asciiArmored;
	}

	public void setAsciiArmored(boolean asciiArmored) {
		this.asciiArmored = asciiArmored;
	}

	public boolean isIntegrityCheck() {
		return integrityCheck;
	}

	public void setIntegrityCheck(boolean integrityCheck) {
		this.integrityCheck = integrityCheck;
	}

	public String getPassphrase() {
		return passphrase;
	}

	public void setPassphrase(String passphrase) {
		this.passphrase = passphrase;
	}

	public String getKeyFile() {
		return keyFile;
	}

	public void setKeyFile(String keyFile) {
		this.keyFile = keyFile;
	}

	public String getInputFile() {
		return inputFile;
	}

	public void setInputFile(String inputFile) {
		this.inputFile = inputFile;
	}

	public String getOutputFile() {
		return outputFile;
	}

	public void setOutputFile(String outputFile) {
		this.outputFile = outputFile;
	}

}

Remember that if you use the PGPFileProcessor in Spring and intend on changing things programmatically via the get/sets you need to make the scope of the bean “prototype” otherwise you will have issues in a multi-threaded environment.

That should do it! You should be able to encrypt and decrypt files (streams really) using PGP!

Support for signed files

If you wish to support signed files, Mateusz Klos posted an excellent comment below that modifies the above code to support signed files.

I didn’t incorporate it into my code because I think he should get full credit for the modifications 🙂

Common Errors

Illegal key size or default parameters

It’s yelling at you because the default JRE security can’t handle PGP.
You didn’t follow the instructions (above) on updating the JCE jars in your JRE’s lib/security directory.

Secret key for message not found

Regenerate you pubring and secring using the PGP tool. Odds are something is wrong with them

The Comments

(Note: Added 4/2/2010)
There had been a lot of great activity in the comments as well as some very kind readers who have posted code snippets to do things that my initial library did not do. I highly suggest you dig through the comments if what you are looking for is not in my original post.

About sseaman

Connect with me on Google+
This entry was posted in Java, Programming and tagged , , , . Bookmark the permalink.

58 Responses to PGP Encryption/Decryption in Java

  1. Jaime Malagon says:

    Hi, how can i prove the code above?

    • sseaman says:

      Jaime, I’m not sure what you mean by ‘prove the code’. If you copy it into a class, run it from your code, that should be it.
      Could you expand on what you mean?

  2. Mateusz Klos says:

    Great post. Thank you for the source code.

    Do you also happen to have a source code of a method for digitally signing the file using PGP private key?
    I have a requirement where I need to digitally sign the file (using my private PGP key) and then encrypt it (using PGP public key). Any help will be appreciated.

    Regards

    Mateusz Klos

    • sseaman says:

      Mateusz,

      Sorry, but I haven’t signed a file before.

      From some quick searching (looking for ‘bouncy castle sign’) I found this article:
      http://jopinblog.wordpress.com/2008/06/23/pgp-single-pass-sign-and-encrypt-with-bouncy-castle/

      From the looks of it, the code you will be most interested in would be:

      // Init signature
      			PgpSecretKeyRingBundle pgpSecBundle = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(keyIn));
      			PgpSecretKey pgpSecKey = pgpSecBundle.GetSecretKey(keyId);
      			if (pgpSecKey == null)
      				throw new ArgumentException(keyId.ToString("X") + " could not be found in specified key ring bundle.", "keyId");
      			PgpPrivateKey pgpPrivKey = pgpSecKey.ExtractPrivateKey(password);
      			PgpSignatureGenerator signatureGenerator = new PgpSignatureGenerator(pgpSecKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
      			signatureGenerator.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);
      			foreach (string userId in pgpSecKey.PublicKey.GetUserIds())
      			{
      				PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
      				spGen.SetSignerUserId(false, userId);
      				signatureGenerator.SetHashedSubpackets(spGen.Generate());
      				// Just the first one!
      				break;
      			}
      			signatureGenerator.GenerateOnePassVersion(false).Encode(compressedOut);
      

      Good luck and if you come up with a nice solution please post it for others here for others to use!

      • Mateusz Klos says:

        I have implemented the single pass PGP Sign-and-Encrypt logic.
        The code is based on your example – I have added a new method called signEncryptFile(). The code is tested and is working fine. Feel free to use/enhance it.

        PGPUtils.java:

        package pgpencryption;
        
        import java.io.*;
        import java.security.*;
        import java.util.*;
        
        import org.bouncycastle.jce.provider.*;
        import org.bouncycastle.openpgp.*;
        import org.bouncycastle.bcpg.*;
        import org.bouncycastle.bcpg.sig.*;
        import org.lockboxlabs.openpgp.tool.util.*;
        
        
        public class PGPUtils {
        
            private static final int   BUFFER_SIZE = 1 &lt;&lt; 16; // should always be power of 2
            private static final int   KEY_FLAGS = 27;
            private static final int[] MASTER_KEY_CERTIFICATION_TYPES = new int[]{PGPSignature.POSITIVE_CERTIFICATION, PGPSignature.CASUAL_CERTIFICATION, PGPSignature.NO_CERTIFICATION, PGPSignature.DEFAULT_CERTIFICATION};
        
            private static boolean hasKeyFlags(PGPPublicKey encKey, int keyUsage) {
                if (encKey.isMasterKey()) {
                    for (int i = 0; i != PGPUtils.MASTER_KEY_CERTIFICATION_TYPES.length; i++) {
                        for (Iterator eIt = encKey.getSignaturesOfType(PGPUtils.MASTER_KEY_CERTIFICATION_TYPES[i]); eIt.hasNext();) {
                            PGPSignature sig = (PGPSignature)eIt.next();
                            if (!isMatchingUsage(sig, keyUsage)) {
                                return false;
                            }
                        }
                    }
                }
                else {
                    for (Iterator eIt = encKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING); eIt.hasNext();) {
                        PGPSignature sig = (PGPSignature)eIt.next();
                        if (!isMatchingUsage(sig, keyUsage)) {
                            return false;
                        }
                    }
                }
                return true;
            }
        
        
            private static boolean isMatchingUsage(PGPSignature sig, int keyUsage) {
                if (sig.hasSubpackets()) {
                    PGPSignatureSubpacketVector sv = sig.getHashedSubPackets();
                    if (sv.hasSubpacket(PGPUtils.KEY_FLAGS)) {
                        if ((sv.getKeyFlags() &amp; keyUsage) == 0) {
                            return false;
                        }
                    }
                }
                return true;
            }
        
        
            @SuppressWarnings(&quot;unchecked&quot;)
            public static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {
        
                PGPPublicKeyRingCollection keyRingCollection = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(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 publicKey = null;
        
                //
                // iterate through the key rings.
                //
                Iterator rIt = keyRingCollection.getKeyRings();
        
                while (publicKey == null &amp;&amp; rIt.hasNext()) {
                    PGPPublicKeyRing kRing = rIt.next();
                    Iterator kIt = kRing.getPublicKeys();
                    while (publicKey == null &amp;&amp; kIt.hasNext()) {
                        PGPPublicKey key = kIt.next();
                        if (key.isEncryptionKey()) {
                            publicKey = key;
                        }
                    }
                }
        
                if (publicKey == null) {
                    throw new IllegalArgumentException("Can't find public key in the key ring.");
                }
                if (!KeyUtil.isForEncryption(publicKey)) {
                    throw new IllegalArgumentException("KeyID " + publicKey.getKeyID() + " not flagged for encryption.");
                }
        
                return publicKey;
            }
        
        
            public static PGPSecretKey readSecretKey(InputStream in) throws IOException, PGPException {
        
                PGPSecretKeyRingCollection keyRingCollection = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(in));
        
                //
                // We just loop through the collection till we find a key suitable for signing.
                // In the real world you would probably want to be a bit smarter about this.
                //
                PGPSecretKey secretKey = null;
        
                Iterator rIt = keyRingCollection.getKeyRings();
                while (secretKey == null &amp;&amp; rIt.hasNext()) {
                    PGPSecretKeyRing keyRing = rIt.next();
                    Iterator kIt = keyRing.getSecretKeys();
                    while (secretKey == null &amp;&amp; kIt.hasNext()) {
                        PGPSecretKey key = kIt.next();
                        if (key.isSigningKey()) {
                            secretKey = key;
                        }
                    }
                }
        
                // Validate secret key
                if (secretKey == null) {
                    throw new IllegalArgumentException("Can't find private key in the key ring.");
                }
                if (!secretKey.isSigningKey()) {
                    throw new IllegalArgumentException("Private key does not allow signing.");
                }
                if (secretKey.getPublicKey().isRevoked()) {
                    throw new IllegalArgumentException("Private key has been revoked.");
                }
                if (!hasKeyFlags(secretKey.getPublicKey(), KeyFlags.SIGN_DATA)) {
                    throw new IllegalArgumentException("Key cannot be used for signing.");
                }
        
                return secretKey;
            }
        
            /**
             * Load a secret key ring collection from keyIn and find the secret key corresponding to
             * keyID if it exists.
             *
             * @param keyIn input stream representing a key ring collection.
             * @param keyID keyID we want.
             * @param pass passphrase to decrypt secret key with.
             * @return
             * @throws IOException
             * @throws PGPException
             * @throws NoSuchProviderException
             */
            private static PGPPrivateKey findSecretKey(InputStream keyIn, long keyID, char[] pass)
            	throws IOException, PGPException, NoSuchProviderException
            {
                PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn));
        
                PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
        
                if (pgpSecKey == null) {
                    return null;
                }
        
                return pgpSecKey.extractPrivateKey(pass, "BC");
            }
        
        
            /**
             * decrypt the passed in message stream
             */
            @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 it = enc.getEncryptedDataObjects();
                PGPPrivateKey sKey = null;
                PGPPublicKeyEncryptedData pbe = null;
        
                while (sKey == null &amp;&amp; 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()) &gt;= 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()) {
                    if (!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);
                PGPUtil.writeFileToLiteralData(
                        comData.open(bOut),
                        PGPLiteralData.BINARY,
                        new File(fileName) );
        
                comData.close();
        
                PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
                    //PGPEncryptedData.CAST5,
                    PGPEncryptedData.TRIPLE_DES,
                    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();
            }
        
        
            public static void signEncryptFile(
                    OutputStream out,
                    String fileName,
                    PGPPublicKey publicKey,
                    PGPSecretKey secretKey,
                    String password,
                    boolean armor,
                    boolean withIntegrityCheck ) throws Exception {
        
                // Initialize Bouncy Castle security provider
                Provider provider = new BouncyCastleProvider();
                Security.addProvider(provider);
        
                if (armor) {
                    out = new ArmoredOutputStream(out);
                }
                
                // Initialize encrypted data generator
                PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(
                        SymmetricKeyAlgorithmTags.TRIPLE_DES,
                        withIntegrityCheck,
                        new SecureRandom(),
                        provider );
                encryptedDataGenerator.addMethod(publicKey);
                OutputStream encryptedOut = encryptedDataGenerator.open(out, new byte[PGPUtils.BUFFER_SIZE]);
        
                // Initialize compressed data generator
                PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
                OutputStream compressedOut = compressedDataGenerator.open(encryptedOut, new byte [PGPUtils.BUFFER_SIZE]);
        
                // Initialize signature generator
                PGPPrivateKey privateKey = secretKey.extractPrivateKey(password.toCharArray(), provider);
                PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
                        secretKey.getPublicKey().getAlgorithm(),
                        HashAlgorithmTags.SHA1,
                        provider );
                signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, privateKey);
        
                boolean firstTime = true;
                Iterator it = secretKey.getPublicKey().getUserIDs();
                while (it.hasNext() &amp;&amp; firstTime) {
                    PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
                    spGen.setSignerUserID(false, (String)it.next());
                    signatureGenerator.setHashedSubpackets(spGen.generate());
                    // Exit the loop after the first iteration
                    firstTime = false;
                }
                signatureGenerator.generateOnePassVersion(false).encode(compressedOut);
        
                // Initialize literal data generator
                PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator();
                OutputStream literalOut = literalDataGenerator.open(
                        compressedOut,
                        PGPLiteralData.BINARY,
                        fileName,
                        new Date(),
                        new byte [PGPUtils.BUFFER_SIZE] );
        
                // Main loop - read the "in" stream, compress, encrypt and write to the "out" stream
                FileInputStream in = new FileInputStream(fileName);
                byte[] buf = new byte[PGPUtils.BUFFER_SIZE];
                int len;
                while ((len = in.read(buf)) &gt; 0) {
                    literalOut.write(buf, 0, len);
                    signatureGenerator.update(buf, 0, len);
                }
        
                in.close();
                literalDataGenerator.close();
                // Generate the signature, compress, encrypt and write to the "out" stream
                signatureGenerator.generate().encode(compressedOut);
                compressedDataGenerator.close();
                encryptedDataGenerator.close();
                if (armor) {
                    out.close();
                }
            }
        
        }
        

        PGPFileProcessor.java:

        package pgpencryption;
        
        import java.io.*;
        import org.bouncycastle.openpgp.*;
        
        public class PGPFileProcessor {
        
            private String passphrase;
            private String publicKeyFileName;
            private String secretKeyFileName;
            private String inputFileName;
            private String outputFileName;
            private boolean asciiArmored = false;
            private boolean integrityCheck = true;
        
            public boolean encrypt() throws Exception {
                FileInputStream keyIn = new FileInputStream(publicKeyFileName);
                FileOutputStream out = new FileOutputStream(outputFileName);
                PGPUtils.encryptFile(out, inputFileName, PGPUtils.readPublicKey(keyIn), asciiArmored, integrityCheck);
                out.close();
                keyIn.close();
                return true;
            }
        
            public boolean signEncrypt() throws Exception {
                FileOutputStream out = new FileOutputStream(outputFileName);
                FileInputStream publicKeyIn = new FileInputStream(publicKeyFileName);
                FileInputStream secretKeyIn = new FileInputStream(secretKeyFileName);
        
                PGPPublicKey publicKey = PGPUtils.readPublicKey(publicKeyIn);
                PGPSecretKey secretKey = PGPUtils.readSecretKey(secretKeyIn);
        
                PGPUtils.signEncryptFile(
                        out,
                        this.getInputFileName(),
                        publicKey,
                        secretKey,
                        this.getPassphrase(),
                        this.isAsciiArmored(),
                        this.isIntegrityCheck() );
        
                out.close();
                publicKeyIn.close();
                secretKeyIn.close();
                
                return true;
            }
        
            public boolean decrypt() throws Exception {
                FileInputStream in = new FileInputStream(inputFileName);
                FileInputStream keyIn = new FileInputStream(publicKeyFileName);
                FileOutputStream out = new FileOutputStream(outputFileName);
                PGPUtils.decryptFile(in, out, keyIn, passphrase.toCharArray());
                in.close();
                out.close();
                keyIn.close();
                return true;
            }
        
            public boolean isAsciiArmored() {
                    return asciiArmored;
            }
        
            public void setAsciiArmored(boolean asciiArmored) {
                    this.asciiArmored = asciiArmored;
            }
        
            public boolean isIntegrityCheck() {
                    return integrityCheck;
            }
        
            public void setIntegrityCheck(boolean integrityCheck) {
                    this.integrityCheck = integrityCheck;
            }
        
            public String getPassphrase() {
                    return passphrase;
            }
        
            public void setPassphrase(String passphrase) {
                    this.passphrase = passphrase;
            }
        
            public String getPublicKeyFileName() {
                    return publicKeyFileName;
            }
        
            public void setPublicKeyFileName(String publicKeyFileName) {
                    this.publicKeyFileName = publicKeyFileName;
            }
        
            public String getSecretKeyFileName() {
                    return secretKeyFileName;
            }
        
            public void setSecretKeyFile(String secretKeyFileName) {
                    this.secretKeyFileName = secretKeyFileName;
            }
        
            public String getInputFileName() {
                    return inputFileName;
            }
        
            public void setInputFileName(String inputFileName) {
                    this.inputFileName = inputFileName;
            }
        
            public String getOutputFileName() {
                    return outputFileName;
            }
        
            public void setOutputFileName(String outputFileName) {
                    this.outputFileName = outputFileName;
            }
        
        }
        

        I hope you will find it useful.

        Mateusz Klos

  3. Khan Hannan says:

    I keep getting the “Secret key for message not found” message. I have a key already made (private/public) and I can’t change or re-make them. The key works with a dotnet component already, but we wanted to use Java. When I started to look at this solution, it seemed great, but I keep getting the same error message.

    I kept the public and private keys in the same file and also tried keeping them in seprate files as solution, but nothing worked.

    Any other suggestion? Anyone? Thanks a bunch.

    • sseaman says:

      Sorry for the delay in responding.

      Have you tried using your own key to generate a file and decrypt it with your java code? Perhaps validating your code with something you know will work might point you to the underlying issue because if it doesn’t work for something you generated then you know you have something else going on.

      Other than that I’m not sure. Did they perhaps sign the file as well?

  4. Sumit says:

    Hi,

    Thanks for the very nice post
    I have seen many post on net but can not understand how ” passphrase” ( an additional parameter in decryption) is generated for decryption.While looking with encryption code I did not get any passphrase key which can be used in decryption.

    Is there any relation between encryption mecahnism and passphrase? I have used “https://www.igolder.com/pgp/generate-key” to generate private and public keys but how this passpharse is generated ,please explain.

    Thanks
    Sumit

  5. Juraj says:

    Hi,
    thank you very much for useful post.

    How about the reverse process of signing the message, i.e. *extracting* the content of signed message.

    I was able to decrypt message with bouncy castle but I am unable to get plain content of signed message.
    I would be very greatful for posting some code that do this part of job.

    PS: I was googling quite extensively and tried following options:
    http://bouncy-castle.1462172.n4.nabble.com/Getting-error-while-decrypting-a-signed-PGP-message-file-td1465973.html
    http://wiki.service-now.com/index.php?title=Sample_Java_BouncyCastle_Algorithm_for_Encryption
    http://boncode.blogspot.com/2012/01/java-implementing-pgp-single-pass-sign.html

    • Juraj says:

      Finally,

      I’ve found one valid solution using org.bouncycastle.openpgp.examples.SignedFileProcessor#verifyFile.

      The only needed modification is to add copying of data InputStream to the output file

      Final result (search for IOUtils.copy):

      private static void verifyFile(
                  InputStream in,
                  InputStream        keyIn,
                  String extractContentFile)
                  throws Exception
          {
              in = PGPUtil.getDecoderStream(in);
      
              PGPObjectFactory pgpFact = new PGPObjectFactory(in);
      
              PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject();
      
              pgpFact = new PGPObjectFactory(c1.getDataStream());
      
              PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject();
      
              PGPOnePassSignature ops = p1.get(0);
      
              PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject();
      
              InputStream                 dIn = p2.getInputStream();
      
              IOUtils.copy(dIn, new FileOutputStream(extractContentFile));
              
              int                         ch;
              PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyIn));
      
              PGPPublicKey key = pgpRing.getPublicKey(ops.getKeyID());
              FileOutputStream out = new FileOutputStream(p2.getFileName());
      
              ops.initVerify(key, "BC");
      
              while ((ch = dIn.read()) &gt;= 0)
              {
                  ops.update((byte)ch);
                  out.write(ch);
              }
      
              out.close();
      
              PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject();
      
              if (ops.verify(p3.get(0)))
              {
                  System.out.println("signature verified.");
              }
              else
              {
                  System.out.println("signature verification failed.");
              }
          }
      
  6. Sumit says:

    Hi,

    I have the below piece of code for sign-in and encrypt.I have few doubt in the parameter passed to the signEncryptFile().Please clarify so that I can test the code.

                PGPPublicKey publicKey- This is the public key field but suppose my public is stored in a file then how can this field will be populated by the file system path?
                PGPSecretKey secretKey -what is this ?and how can I get its value here?
                String password-this must be a password by which we generated public and private key.Correct me if I am wrong?
    
                
    @SuppressWarnings("deprecation")
    	public static void signEncryptFile(
                OutputStream out,
                String fileName,
                PGPPublicKey publicKey,
                PGPSecretKey secretKey,
                String password,
                boolean armor,
                boolean withIntegrityCheck ) throws Exception {
    
            // Initialize Bouncy Castle security provider
            Provider provider = new BouncyCastleProvider();
            Security.addProvider(provider);
    
            if (armor) {
                out = new ArmoredOutputStream(out);
            }
    
            // Initialize encrypted data generator
            PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(
                    SymmetricKeyAlgorithmTags.TRIPLE_DES,
                    withIntegrityCheck,
                    new SecureRandom(),
                    provider );
            encryptedDataGenerator.addMethod(publicKey);
            OutputStream encryptedOut = encryptedDataGenerator.open(out, new byte[PGPSignAndEncrpt.BUFFER_SIZE]);
    
            // Initialize compressed data generator
            PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
            OutputStream compressedOut = compressedDataGenerator.open(encryptedOut, new byte [PGPSignAndEncrpt.BUFFER_SIZE]);
    
            // Initialize signature generator
            PGPPrivateKey privateKey = secretKey.extractPrivateKey(password.toCharArray(), provider);
            PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
                    secretKey.getPublicKey().getAlgorithm(),
                    HashAlgorithmTags.SHA1,
                    provider );
            signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, privateKey);
    
            boolean firstTime = true;
            Iterator it = secretKey.getPublicKey().getUserIDs();
            while (it.hasNext()&amp;&amp; firstTime) {
                PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
                spGen.setSignerUserID(false, (String)it.next());
                signatureGenerator.setHashedSubpackets(spGen.generate());
                // Exit the loop after the first iteration
                firstTime = false;
            }
            signatureGenerator.generateOnePassVersion(false).encode(compressedOut);
    
            // Initialize literal data generator
            PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator();
            OutputStream literalOut = literalDataGenerator.open(
                    compressedOut,
                    PGPLiteralData.BINARY,
                    fileName,
                    new Date(),
                    new byte [PGPSignAndEncrpt.BUFFER_SIZE] );
    
            // Main loop - read the "in" stream, compress, encrypt and write to the "out" stream
            FileInputStream in = new FileInputStream(fileName);
            byte[] buf = new byte[PGPSignAndEncrpt.BUFFER_SIZE];
            int len;
            while ((len = in.read(buf)) &gt; 0) {
                literalOut.write(buf, 0, len);
                signatureGenerator.update(buf, 0, len);
            }
    
            in.close();
            literalDataGenerator.close();
            // Generate the signature, compress, encrypt and write to the "out" stream
            signatureGenerator.generate().encode(compressedOut);
            compressedDataGenerator.close();
            encryptedDataGenerator.close();
            if (armor) {
                out.close();
            }
        }
    

    Thanks
    Sumit

  7. Mahan H. says:

    Thanks for the great example! Exactly what I wanted. Concise and to the point. 🙂

  8. Ali Younas says:

    Awesome work man! Keep it up 🙂

  9. triveni says:

    I am not able to decrypt a file. i getting the below exception .

    java.lang.IllegalArgumentException: secret key for message not found.
    at com.tecnotree.CopyOfPGPDecryption.decryptFile(CopyOfPGPDecryption.java:41)
    at com.tecnotree.CopyOfPGPDecryption.main(CopyOfPGPDecryption.java:19)
    

    I had given a private key(secring.skr) and passphrase key(Password for key) in my program. But still i am getting above exception.
    please help me in resolving this. I have been trying for past 3 days but could not get to its bottom.
    I have used the same code of yours.

    • sseaman says:

      Verify that the private key actually work (use it in another program or attempt to decrypt an encrypted file with it in another program).

      I’m betting that you find that the key has an error or is not valid (your keyId could be invalid as well)

      • triveni says:

        I am able to decrypt file using the pgp 6.5.8 using the passphrase. Also the content is decrypted properly. But I am not sure how to test private key(secring.skr) is invalid or not.

      • triveni says:

        Hi
        I am still not able to decrypt using java.
        I am getting the following exception.

        org.bouncycastle.openpgp.PGPException: error setting asymmetric cipher
        at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder.decryptSessionData(Unknown Source)
        at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder.access$000(Unknown Source)
        at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder$2.recoverSessionData(Unknown Source)
        at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
        at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
        at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
        at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
        at com.tecnotree.PGPDecryption.decryptFile(PGPDecryption.java:52)
        at com.tecnotree.PGPDecryption.main(PGPDecryption.java:23)
        Caused by: java.security.InvalidKeyException: unknown key type passed to ElGamal
        at org.bouncycastle.jcajce.provider.asymmetric.elgamal.CipherSpi.engineInit(Unknown Source)
        at org.bouncycastle.jcajce.provider.asymmetric.elgamal.CipherSpi.engineInit(Unknown Source)
        at javax.crypto.Cipher.init(DashoA12275)
        at javax.crypto.Cipher.init(DashoA12275)
        ... 9 more

        Please find code I have written for decrypting.

        package com.test;

        import java.security.Security;
        import org.bouncycastle.openpgp.*;
        import org.bouncycastle.bcpg.sig.KeyFlags;
        import org.bouncycastle.jce.provider.BouncyCastleProvider;
        import java.io.*;
        import java.util.Iterator;
        import java.security.NoSuchProviderException;

        public class PGPDecryption {

        public static void main(String[] args) {

        try {
        Security.addProvider(new BouncyCastleProvider());
        FileInputStream privKey = new FileInputStream(
        "C:\\Program Files\\Network Associates\\PGPNT\\PGP Keyrings\\secring.skr");
        InputStream file = new BufferedInputStream(new FileInputStream(
        "C:\\testfiles\\BackUp\\MTNUGPO504.inp"));
        decryptFile(file, privKey, "passphrase".toCharArray());
        } catch (Exception ex) {
        ex.printStackTrace();
        }
        }

        @SuppressWarnings({"deprecation", "rawtypes"})
        private static String decryptFile(InputStream in, InputStream keyIn,
        char[] passwd) throws Exception {
        try {
        PGPObjectFactory pgpF = new PGPObjectFactory(
        PGPUtil.getDecoderStream(in));
        Object o = pgpF.nextObject();
        PGPEncryptedDataList enc = o instanceof PGPEncryptedDataList ? (PGPEncryptedDataList) o
        : (PGPEncryptedDataList) pgpF.nextObject();
        Iterator it = enc.getEncryptedDataObjects();
        PGPPrivateKey sKey = null;

        PGPPublicKeyEncryptedData pbe = null;

        while (sKey == null && it.hasNext()) {
        pbe = (PGPPublicKeyEncryptedData) 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();
        }

        File outputfile = new File("decrypttest.out");
        FileWriter writer = new FileWriter(outputfile);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        if (message instanceof PGPLiteralData) {
        PGPLiteralData ld = (PGPLiteralData) message;
        InputStream unc = ld.getInputStream();
        int ch;
        while ((ch = unc.read()) >= 0) {
        baos.write(ch);
        writer.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()) {
        if (!pbe.verify()) {
        System.err.println("message failed integrity check");
        }
        } else {
        System.err.println("no message integrity check");
        }
        return baos.toString();
        } catch (PGPException e) {
        e.printStackTrace();
        }
        return null;
        }

        private static PGPPrivateKey findSecretKey(InputStream keyIn, long keyID,
        char[] pass) throws IOException, PGPException,
        NoSuchProviderException {
        PGPSecretKey pgpSecKey = readSecretKey(keyIn);
        return pgpSecKey != null ? pgpSecKey.extractPrivateKey(pass, "BC") : null;
        }

        public static PGPSecretKey readSecretKey(InputStream in)
        throws IOException, PGPException {

        PGPSecretKeyRingCollection keyRingCollection = new PGPSecretKeyRingCollection(
        PGPUtil.getDecoderStream(in));
        PGPSecretKey secretKey = null;
        Iterator rIt = keyRingCollection.getKeyRings();
        while (secretKey == null && rIt.hasNext()) {
        PGPSecretKeyRing keyRing = (PGPSecretKeyRing) rIt.next();
        Iterator kIt = keyRing.getSecretKeys();
        while (secretKey == null && kIt.hasNext()) {
        PGPSecretKey key = (PGPSecretKey) kIt.next();
        if (key.isSigningKey()) {
        secretKey = key;
        }
        }
        }

        // Validate secret key
        if (secretKey == null) {
        throw new IllegalArgumentException("Can't find private key in the key ring.");
        }
        if (!secretKey.isSigningKey()) {
        throw new IllegalArgumentException("Private key does not allow signing.");
        }
        if (secretKey.getPublicKey().isRevoked()) {
        throw new IllegalArgumentException("Private key has been revoked.");
        }
        if (!hasKeyFlags(secretKey.getPublicKey(), KeyFlags.SIGN_DATA)) {
        throw new IllegalArgumentException("Key cannot be used for signing.");
        }
        return secretKey;
        }

        private static final int[] MASTER_KEY_CERTIFICATION_TYPES = new int[] {
        PGPSignature.POSITIVE_CERTIFICATION,
        PGPSignature.CASUAL_CERTIFICATION, PGPSignature.NO_CERTIFICATION,
        PGPSignature.DEFAULT_CERTIFICATION };

        private static boolean hasKeyFlags(PGPPublicKey encKey, int keyUsage) {
        if (encKey.isMasterKey()) {
        for (int i = 0; i != MASTER_KEY_CERTIFICATION_TYPES.length; i++) {
        for (Iterator eIt = encKey.getSignaturesOfType(MASTER_KEY_CERTIFICATION_TYPES[i]); eIt.hasNext();) {
        PGPSignature sig = (PGPSignature) eIt.next();
        if (!isMatchingUsage(sig, keyUsage)) {
        return false;
        }
        }
        }
        }else {
        for (Iterator eIt = encKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING); eIt.hasNext();) {
        PGPSignature sig = (PGPSignature) eIt.next();
        if (!isMatchingUsage(sig, keyUsage)) {
        return false;
        }
        }
        }
        return true;
        }

        private static final int KEY_FLAGS = 27;

        private static boolean isMatchingUsage(PGPSignature sig, int keyUsage) {
        if (sig.hasSubpackets()) {
        PGPSignatureSubpacketVector sv = sig.getHashedSubPackets();
        if (sv.hasSubpacket(KEY_FLAGS)) {
        if ((sv.getKeyFlags() & keyUsage) == 0) {
        return false;
        }
        }
        }
        return true;
        }
        }

  10. Anand says:

    I need to create a PGP encrypted fie without writing to a temp file.
    Does anyone have suggestions?

  11. chenlei says:

    Hi,
    I used PGP command line 6.5.8 to generate public key(pubring.pkr) and private key(secring.skr). I able to encrypt file with public key, but when try to decrypt file getting following PGPException:

    org.bouncycastle.openpgp.PGPException: cannot recognise cipher
            at org.bouncycastle.openpgp.operator.bc.BcImplProvider.createBlockCipher(Unknown Source)
            at org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder$1.recoverKeyData(Unknown Source)
            at org.bouncycastle.openpgp.PGPSecretKey.extractKeyData(Unknown Source)
            at org.bouncycastle.openpgp.PGPSecretKey.extractPrivateKey(Unknown Source)
            at javaapplication1.PGPUtils.findPrivateKey(PGPUtils.java:192)
    
    • sseaman says:

      This usually means that something is wrong with the key you are using. I would re-generate your keys and see if that fixes things.

      • Herc says:

        Hi,

        I am facing the same issue as Chenlei while extracting the private key:

        org.bouncycastle.openpgp.PGPException: cannot recognise cipher
        at org.bouncycastle.openpgp.operator.bc.BcImplProvider.createBlockCipher(Unknown Source)
        at org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder$1.recoverKeyData(Unknown Source)
        at org.bouncycastle.openpgp.PGPSecretKey.extractKeyData(Unknown Source)
        at org.bouncycastle.openpgp.PGPSecretKey.extractPrivateKey(Unknown Source)
        

        Have you figured out the problem?

  12. Ricky says:

    Does Bouncycastle supports DSS key to encrypt file.

    I have keys both RSA and DSS in my pubring file but for RSA I am able to encrypt file but for DSS key bc readPublicKey() method then –>getPublicKey() method there is a check isEncryptionKey() which returning false for DSS.. n bouncycastle code works with this check true only.

    So, not able to encrypt file using DSS key in pubring.Can I get solution and workaround for this?

  13. srini says:

    I able to encrypt using Java. But while using java decrypt I am getting following error.

    java.lang.IllegalArgumentException: Secret key for message not found.

    But I able encrypt and decrypt using PGPTray {tool} Using PGP 6.5 Version.

    Please help me debug this issue.

    thanks,
    Srini.

    • sseaman says:

      This is a pretty common error and I think it is covered in some of the other comments. There is usually something wrong with the way you are processing the secret key file.

  14. Thulasiram says:

    Hi
    encrypting the file by using globalscape tool and generating publickey and private key and passphrase.
    While I am trying to decrypt the file by using private key and passphrase I am getting the below error.
    I am getting error while reading the key at the below line
    PGPSecretKeyRingCollection collection = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(new FileInputStream(“D:\\Thulasiram\\MFT\\INputs\\AES256\\IPM_AES256.asc”)) );

    error:
    java.io.IOException: unknown object in stream: 21
    at org.bouncycastle.openpgp.PGPObjectFactory.nextObject(Unknown Source)
    at org.bouncycastle.openpgp.PGPSecretKeyRingCollection.(Unknown Source)

    I need urgent help . please help me in this

    • sseaman says:

      The file is correct or of an incorrect format. Make sure the file you are passing in is good.

      • thulasiram says:

        HI,
        I am using the correct file(pgp private key used RSA algorithm). with the global space tool they are successfully able to decrypt the file using the key. But when iam trying to decrypt the file in java iam getting the above error while reading the key. after debugging i got the below doubt.
        Can we decrypt any file if we have pgp private key in java using the above code? I mean do we need use same vendor’s (the tool which is generating keys and encryptinh)jdk to decrypt ? Can i use above code to decrypt any encyted file if i have the privatekey and passphrase. and if i know the algorithm used.

  15. loubna says:

    it is run into an android application?

  16. Dave says:

    I am using the .Net C# solution from Bouncy castle. I was able to create private and public keys in ascii armor format with no issues. A client enrypted a file for us using our public pgp key and sent us the file which has the .asc file ext which seems correct.

    When I call the Decryption method the code throws a casting exception which I noted about 3/4 down in the code block. Any ideas would be appreciated. I understand what a casting error is but I am not sure where to look upstream of the error for why the cast blew up. I am using the code verbatim from the C# zipped solution, but it may be a very simple thing that I am missing.

    The exception is:

    Unable to cast object of type ‘Org.BouncyCastle.Bcpg.OpenPgp.PgpOnePassSignatureList’ to type ‘Org.BouncyCastle.Bcpg.OpenPgp.PgpCompressedData’.

    The code block that I am using from the BC KeyBasedFileProcessor class:

    using System;
    using System.Collections;
    using System.IO;
    using Org.BouncyCastle.Bcpg.OpenPgp;
    using Org.BouncyCastle.Security;
    using Org.BouncyCastle.Utilities.IO;
    
    namespace ConsoleApplication2008
    {
    public class BC
    {
    public void DecryptFile(string inputFileNamePath,
    string keyFileNamePath,
    char[] passwd,
    string OutputFileNamepath)
    {
    using (Stream input = File.OpenRead(inputFileNamePath),
    keyIn = File.OpenRead(keyFileNamePath))
    {
    DecryptFile(input, keyIn, passwd, OutputFileNamepath);
    }
    }
    
    public static void DecryptFile(
    Stream inputStream,
    Stream keyIn,
    char[] passwd,
    string defaultFileName)
    {
    inputStream = PgpUtilities.GetDecoderStream(inputStream);
    
    try
    {
    PgpObjectFactory pgpF = new PgpObjectFactory(inputStream);
    PgpEncryptedDataList enc;
    
    PgpObject o = pgpF.NextPgpObject();
    //
    // the first object might be a PGP marker packet.
    //
    if (o is PgpEncryptedDataList)
    {
    enc = (PgpEncryptedDataList)o;
    }
    else
    {
    enc = (PgpEncryptedDataList)pgpF.NextPgpObject();
    }
    
    //
    // find the secret key
    //
    PgpPrivateKey sKey = null;
    PgpPublicKeyEncryptedData pbe = null;
    PgpSecretKeyRingBundle pgpSec = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(keyIn));
    
    foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects())
    {
    sKey = BCUtility.FindSecretKey(pgpSec, pked.KeyId, passwd);
    
    if (sKey != null)
    {
    pbe = pked;
    break;
    }
    }
    
    if (sKey == null)
    {
    throw new ArgumentException(“secret key for message not found.”);
    }
    
    Stream clear = pbe.GetDataStream(sKey);
    
    PgpObjectFactory plainFact = new PgpObjectFactory(clear);
    <strong>//****Error occurs at the next line where when the cast is made.******</strong>
    PgpCompressedData cData = (PgpCompressedData)plainFact.NextPgpObject();
    
    PgpObjectFactory pgpFact = new PgpObjectFactory(cData.GetDataStream());
    
    PgpObject message = pgpFact.NextPgpObject();
    
    if (message is PgpLiteralData)
    {
    PgpLiteralData ld = (PgpLiteralData)message;
    
    string outFileName = ld.FileName;
    if (outFileName.Length == 0)
    {
    outFileName = defaultFileName;
    }
    
    Stream fOut = File.Create(outFileName);
    Stream unc = ld.GetInputStream();
    Streams.PipeAll(unc, fOut);
    fOut.Close();
    }
    else if (message is 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())
    {
    Console.Error.WriteLine(“message failed integrity check”);
    }
    else
    {
    Console.Error.WriteLine(“message integrity check passed”);
    }
    }
    else
    {
    Console.Error.WriteLine(“no message integrity check”);
    }
    }
    catch (PgpException e)
    {
    Console.Error.WriteLine(e);
    
    Exception underlyingException = e.InnerException;
    if (underlyingException != null)
    {
    Console.Error.WriteLine(underlyingException.Message);
    Console.Error.WriteLine(underlyingException.StackTrace);
    }
    }
    }
    }
    }
    
    • sseaman says:

      Usually this means there is something wrong with the file that is being processed. In this case it appears to be looking for compressed data when instead it is finding something else. I’d first verify that you can encrypt/decrypt correctly with another tool. If you can then I’d start digging into the source code for BC to see exactly what is happening.

  17. Jayesh says:

    I am using your code for decryption which is not encrypted my us. All files works fine however one of the file which large data of around 1GB doesn’t decrypt fully and gives following error
    Unexpected end of ZIP input stream

    Can you please help

  18. Dave says:

    Hello,

    If I use pgptools, I can pick which key to use to encrypt. How do I do the same with the encrypt function you list?

  19. Sudhakar says:

    Hi
    I am new to bouncy castle api. I was able to encrypt the file using your solution. but cannot able to decrypt it.

    i have key file abc.gpgkey , which i have exported to my linux system. it gave me “abc.asc”.
    using that i was able to do encrypt.

    but it does not have any password or paraphrase. so what should i send while invoking decrypt .

    help would be appreciated.

  20. Sumit says:

    Hi ,
    I am new to bouncy castle api. I am able to encrypt the file but I am not getting –BEGIN PGP—- & the —END PGP— markers in the encrypted file. And manually adding these markers to the files the files gets encrypted successfully.

    –really don’t know what I am missing in the code.

    Any help would be appreciated

    Regards,
    Sumit

  21. Ajay says:

    this code give NullPointerException.
    my requirement is
    1)take simple text file to encrypted file using public key.
    2)again decrypt same encrypted file using private key and passphrase.

    please give me jar with all required lib file.

    thanks

  22. Ajay says:

    ok
    but please help me
    i have done some thing its working in eclipse but when i create executable jar its give me below error.
    output :—–

    Encryption jar Error
    —————————————

    Reading public key………
    Public Key found………..
    File Encrypting…………
    Exception in thread “main” java.lang.NoSuchMethodError: org.bouncycastle.openpgp
    .PGPEncryptedDataGenerator.(IZLjava/security/SecureRandom;Ljava/lang/Strin
    g;)V
    at org.pgp.util.KeyBasedFileProcessorUtil.encryptFile(KeyBasedFi
    leProcessorUtil.java:389)
    at org.pgp.encrypt.PGPEncryption.main(PGPEncryptio
    n.java:69)
    —————————————-

    Decryption jar error
    —————————-

    File Decrypting
    secret key…………..
    Exception in thread “main” java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoa
    der.java:58)
    Caused by: java.lang.NoSuchMethodError: org.bouncycastle.openpgp.PGPSecretKey.ex
    tractPrivateKey([CLjava/lang/String;)Lorg/bouncycastle/openpgp/PGPPrivateKey;
    at org.pgp.util.KeyBasedFileProcessorUtil.findSecretKey(KeyBased
    FileProcessorUtil.java:124)
    at org.pgp.util.KeyBasedFileProcessorUtil.decryptFile(KeyBasedFi
    leProcessorUtil.java:172)
    at org.pgp.encrypt.PGPDecryption.main(PGPDecryptio
    n.java:49)
    … 5 more

  23. Ajay says:

    it is possible to encrypt and decrypt file using private key?
    my requirement is public key is share with publicly, they encrypt file using public key and i have private key only for decrypt that file and using same private key encrypt file again because i don’t have public key.

    thanks

  24. Ajay says:

    hi,
    now i am facing new error
    i have added US_export_policy.jar and local_policy.jar in jre and add bcpg and bcprov jar also added. then also i am facing below error

    org.bouncycastle.openpgp.PGPException: Exception starting decryption
    at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
    at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
    at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
    at com.posidex.pgp.util.KeyBasedFileProcessorUtil.decryptFile(KeyBasedFileProcessorUtil.java:182)
    at com.posidex.pgp.encrypt.PosidexPGPDecryption.main(PosidexPGPDecryption.java:49)
    Caused by: java.security.InvalidKeyException: Illegal key size
    at javax.crypto.Cipher.a(DashoA13*..)
    at javax.crypto.Cipher.init(DashoA13*..)
    at javax.crypto.Cipher.init(DashoA13*..)
    … 5 more
    org.bouncycastle.openpgp.PGPException: Exception starting decryption
    java.security.InvalidKeyException: Illegal key size
    at javax.crypto.Cipher.a(DashoA13*..)
    at javax.crypto.Cipher.init(DashoA13*..)
    at javax.crypto.Cipher.init(DashoA13*..)
    at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
    at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
    at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
    at com.posidex.pgp.util.KeyBasedFileProcessorUtil.decryptFile(KeyBasedFileProcessorUtil.java:182)
    at com.posidex.pgp.encrypt.PosidexPGPDecryption.main(PosidexPGPDecryption.java:49)
    Decrypted File created with name of E:/Encyption/final/out.txt

  25. Ajay says:

    Please help me,

    i am using GPG4Win for encrypt the file and then your code for decrypt file its give previous post error.

    suppose i use your code for encrypt file and then use your decryption code its able to decrypt file and GPG4win also able to decrypt the file.

    please help me i want to decrypt the file those encrypted by GPG4win

    both using same algorithm or different. please give me some guide line.

  26. Abdul Rasheed says:

    Hi, I read all the comments and tried the google. I need to have my own implementation of PGP for some project, can you please tell me where I can find the actual algorithm or how to start my own code for it……..

  27. INDRA CHATTERJEE says:

    I have been trying to implement your code to encrypt and decrypt a message string.
    Encrypt module works fine but i am getting errors in decrypt module. Public and private keys are getting extracted from respective files, key pair created at: https://pgpkeygen.com/

    Please help in resolving the error in decrypt module :
    InputStream clear = pbe.getDataStream(pgpPrivateKey, “BC”);
    PGPObjectFactory plainFact = new PGPObjectFactory(clear);
    Object message = plainFact.nextObject();

    I encounter an exception in above code:
    java.io.IOException: invalid header encountered
    at org.bouncycastle.bcpg.BCPGInputStream.readPacket(Unknown Source)
    at org.bouncycastle.openpgp.PGPCompressedData.(Unknown Source)
    at org.bouncycastle.openpgp.PGPObjectFactory.nextObject(Unknown Source)
    at com.entrust.aws.PGPChipher.PGPSecurityProvider.decryptMessage(PGPSecurityProvider.java:248)

    In order to overcome this exception, i added code line:
    clear = PGPUtil.getDecoderStream(clear);
    But then, Object message = plainFact.nextObject(); message is always NULL.

    Please help in resolving this error.

Leave a Reply

Your email address will not be published. Required fields are marked *