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.