Categories
Bash Java

Public and private PGP keys with Java BouncyCastle, in PEM format

We explain how to generate public and private PGP keys, in Java, using the BouncyCastle library. Then, we store the keys in PEM base64 files, and add the keys to our GPG keyring. Finally, we test the keys by encrypting and decrypting a text file using the Linux gpg command line interface (CLI).

1) BouncyCastle dependency in pom.xml

Add the following dependency to your pom.xml:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpg-jdk15on</artifactId>
    <version>1.69</version>
</dependency>

2) Required parameters

In your Java code, prepare the following parameters, and replace with your own values:

String workFolder = System.getProperty("user.home") + "/Desktop/pgp/";
String keyUserId = "john@doe.com";
String keyPassPhrase = "secret";
  • The workFolder is a folder on your local machine, where we will generate the PGP keys. We will also generate a dummy text file in this folder, encrypt it and decrypt it using the Linux gpg command line interface.
  • The keyUserId is the User ID that will be associated with the generated PGP keys.
  • The keyPassPhrase is the pass phrase that will be used to decrypt your PGP private key on your machine, every time you want to use it.

3) Build the PGPKeyRingGenerator

import java.io.*;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Date;

import org.bouncycastle.bcpg.*;
import org.bouncycastle.bcpg.sig.*;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.*;
import org.bouncycastle.openpgp.operator.bc.*;

import static org.bouncycastle.bcpg.HashAlgorithmTags.*;
import static org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags.*;
import static org.bouncycastle.bcpg.sig.KeyFlags.*;

static PGPKeyRingGenerator buildKeyRingGenerator(String keyID, char[] keyPassPhraseAr) throws PGPException {
    RSAKeyPairGenerator kpg = new RSAKeyPairGenerator();
    kpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x10001), new SecureRandom(), 2048, 12));

    PGPKeyPair rsakp_sign = new BcPGPKeyPair(PGPPublicKey.RSA_SIGN, kpg.generateKeyPair(), new Date());
    PGPKeyPair rsakp_enc = new BcPGPKeyPair(PGPPublicKey.RSA_ENCRYPT, kpg.generateKeyPair(), new Date());

    PGPSignatureSubpacketGenerator signhashgen = new PGPSignatureSubpacketGenerator();
    signhashgen.setKeyFlags(false, SIGN_DATA | CERTIFY_OTHER);
    signhashgen.setPreferredSymmetricAlgorithms(false, new int[]{AES_256, AES_192, AES_128});
    signhashgen.setPreferredHashAlgorithms(false, new int[]{SHA256, SHA1, SHA384, SHA512, SHA224});
    signhashgen.setFeature(false, Features.FEATURE_MODIFICATION_DETECTION);

    PGPSignatureSubpacketGenerator enchashgen = new PGPSignatureSubpacketGenerator();
    enchashgen.setKeyFlags(false, ENCRYPT_COMMS | ENCRYPT_STORAGE);

    PGPDigestCalculator sha1Calc = new BcPGPDigestCalculatorProvider().get(SHA1);
    PGPDigestCalculator sha256Calc = new BcPGPDigestCalculatorProvider().get(SHA256);

    PGPContentSignerBuilder pcsb = new BcPGPContentSignerBuilder(rsakp_sign.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1);
    PBESecretKeyEncryptor pske = (new BcPBESecretKeyEncryptorBuilder(AES_256, sha256Calc, 0xc0)).build(keyPassPhraseAr);

    PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsakp_sign, keyID, sha1Calc,
signhashgen.generate(), null, pcsb, pske);

    keyRingGen.addSubKey(rsakp_enc, enchashgen.generate(), null);
    return keyRingGen;
}

4) Write the PEM public and private keys to files

static void writePEMKeys(String workFolder, String keyID, String keyPassPhrase) throws IOException, PGPException {
    PGPKeyRingGenerator krgen = buildKeyRingGenerator(keyID, keyPassPhrase.toCharArray());

    PGPPublicKeyRing pkr = krgen.generatePublicKeyRing();
    try (BufferedOutputStream pubout = new BufferedOutputStream(new FileOutputStream(workFolder + "public.key"))) {
        try (ArmoredOutputStream ao = new ArmoredOutputStream(pubout)) {
            pkr.encode(ao);
        }
    }

    PGPSecretKeyRing skr = krgen.generateSecretKeyRing();
    try (BufferedOutputStream secout = new BufferedOutputStream(new FileOutputStream(workFolder + "private.key"))) {
        try (ArmoredOutputStream ao = new ArmoredOutputStream(secout)) {
            skr.encode(ao);
        }
    }
}

(fully working Java code main is available at the bottom of this article).

The generated public key PEM public.key looks like this:

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG v1.69

mQENBGGFj9YDCACUf+oU6iTPPMDFEcPLvIfCsA32IJ3vvdPGIhMjQey+ZTAv/g0K
I94KcyrWV0NKqESDpsoQuiekLNR+tEFRmRvXpyQ/+QHMPCD4OXYqrFBUycL5k360
uX7/DKH6R6EFdTphfnC1FTyApxWumLmo1nEx5koc87e4Hi7DxiVAvC6eRRmTDrQc
KjhuDIYqoIjQL6MEnyQEfFysmc43bHb96h0m161vQEK2OlbX4Ob1dpv0lJwApECB
7CE1Zpoz7MrC3jMpeX4qWsaobq0KTHP2928CNIeW5DgNAeOxifQNH5UTstCjI89K
1+RdUchcoJBEdLdhPIRWhAhs29Nl2Km79Hs9ABEBAAG0DGpvaG5AZG9lLmNvbYkB
LgQTAwIAGAUCYYWP1gIbAwQLCQgHBhUIAgkKCwIeAQAKCRC6JPC9682kXM9QB/45
+WjLasdTR41UTMHZxnsjsE/sAkr3Nj3VfAwepUFHW2FvA42ZNDfMO02//NLfNcST
tbbLNmaCpnO1U8q0V2Qptwp38TwfIXH+TNllGWtHW3CMIRTC2dt3ff3e+8VFUJM5
4KAQa/LvOCcHdC7C6f87ypJvUVwkoizsHTpJPRULjK6orGstCa9hdrds9B1KpDuu
/UifRXrAna/wOBonc7tIE0wPQiGsP/VY8BVsNoIfw+ffotLwjJQpRvnb56z30VFn
2YWstICPGYgyO+/LEWW/sL4ixm7oZONLvZI0RI2JQF7BIpGybHi0cHGBeT6wF9o+
HTwI5Rm3hjV1d0Hyk9AzuQENBGGFj9YCCADRxZCTUztosOARZusDtppl4JFYYsTc
DYN4EJjFzESUldymJ3+V6B0/QCX9DCj/vv4O42zlDikvTlMf78bQaXBF8q6RF0Ti
uvdQkSUIuFA6nfH2p0xd/h97zvD5mSQtM2cIeSDEFYEqrIMdw5WVongarw+H6im1
uUsQWdKsErG17VLoYUM2KUQsXbQT/BSUvDh0AKIisYAtF7YOAFb0HgCQZXQN1jOo
2MF0oGRME4z2+x+HrRGLl4C44KSLEd7BCyghrAEGHsR0CNP7ZNl71/QYjpuvoSAV
ls+l8V8QU9p6INli53qKdRH5XZYB1kMTfFQeaMLApVE+1ASs124Rl0kXABEBAAGJ
AR8EGAMCAAkFAmGFj9cCGwwACgkQuiTwvevNpFwrbAgAgKJ64ukFQdk31vmHdZDo
vTGGivUzFyBBlJBh9B0bNxCmRN+WhwIOe8W8sHQxh+wpWa+mCmzm4VrZsDneU5kF
CejW1sYIR08Cnhighz5jeU0q5c6LFaErW52W3Rm1Nxehtr8cJlNjeImOyUHTTyze
FLnJj2QgkdhlCGehJJdqRJo5oKemDgNZFZfByh/iLIEXukn4kZsT35hfIqZwCnIr
82Z6sjfAspdBEplByQanO7oemphrvrR+/sCsPSVlAvZo2mqwLKxvIzDzkc8JK1pd
C7xF2hCYYQqxvbQcSCox6p7iHee9+tPzq+nZExu19BJJa0AJ8/GfdtiYwwPPgGc9
OQ==
=WcZA
-----END PGP PUBLIC KEY BLOCK-----

The generated private key PEM private.key looks like this:

-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: BCPG v1.69

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
Zz05
=gGxU
-----END PGP PRIVATE KEY BLOCK-----

5) Test the PGP public key with the gpg ENCRYPT command

Open a terminal, and go to the workFolder we defined in step 1). First, let’s import the public key. Run:

gpg --import public.key

Now, let’s generate a dummy text file with some random content. We will use this file to test PGP encryption and decryption. Let’s call this file foo.txt:

echo "hello" > foo.txt

Then, let’s find the ID of the key we will use to encrypt the file, by listing the keys in our GPG keyring:

gpg --list-keys

In the terminal, you should see something like this:

--------------------------------
pub   rsa2048 2021-11-05 [SC]
      EFAB4FA2CF7B8A1B7407532DF438E6F1BA99B396
uid           [ unknown] john@doe.com
sub   rsa2048 2021-11-05 [E

Here, our key ID is the bold value:

EFAB4FA2CF7B8A1B7407532DF438E6F1BA99B396

Finally, let’s encrypt the text file:

gpg --encrypt --trust-model always --recipient EFAB4FA2CF7B8A1B7407532DF438E6F1BA99B396 -o encrypted.gpg foo.txt

In your work folder, a new file called encrypted.gpg has been created. It is the encrypted version of the clear text file foo.txt.

6) Test the PGP private key with the gpg DECRYPT command

Now, let’s decrypt the file we encrypted at previous step: encrypted.gpg. To do this, we need the PGP private key. So let’s import it to our key ring. Open a terminal, and go to the work folder where you have the PGP private key called private.key and the encrypted file called encrypted.gpg (this could be on the same machine as in step 5), or on a different machine). First, let’s import the private key:

gpg --import private.key

You will be prompted for the keyPassPhrase that we setup in step 1).

Then, to decrypt the file in a new clear file called decrypted.txt, simply run:

gpg --decrypt -o decrypted.txt encrypted.gpg

You will be prompted for thekeyPassPhrase one last time. Now you should be able to read the original content of the text file:

$ cat decrypted.txt 
hello

7) Fully working Java code main


That’s it for this tutorial on Java BouncyCastle and PGP keys ! Thanks for reading. Please leave a reply below, if you encounter any problem. We reply within 24h.

Leave a Reply

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