OpenPGP Integration (Java and JavaScript): Java PGP encryption

Java PGP encryption

Previously on Codes And Notes:

– … But honey, we don’t have the key to that door!
– Don’t worry mi Amor, I have studied the ancient Art of PGP Key Generation. I have made a pair for you and tie them on a ring.
– A ring? Hay Querida, I love your style! Will you marry me?
Will Pedro and Asunción manage to escape from this dreaded place and live to get married? You will find out on the next episode…

… ahem…

Anyway, after generating PGP key pairs in Java we are now going to see how to use those to encrypt messages. As always, you can find the code on GitHub with tests and all, ready to be grabbed and leveraged for your own projects!

  • Java keys generation
  • Java PGP encryption
  • Front end PGP encryption
  • PGP messages exchange between Java and JavaScript

Java PGP Encryption

Traditionally the Java developer would grab the seminal BouncyCastle library and prepare himself/herself to hack away for days to obtain an encrypted PGP message… And discover that other PGP-based applications could not decrypt the message anyway. Therefore a few valiant developers got to work and created libraries to simplify the process.

Bouncy GPG is one of those libraries. Its author, Jens Neuhalfen, has put a lot of time and effort to come up with a flexible, easy-to-use solution. Frankly, I’ve been unable to find anything better than this. The only thing missing is programmatic PGP generation. But guess what: we have implemented that on the previous episode!

So let’s see how we use Bouncy GPG with our generated keys.

The Bouncy Identity

We start by importing bouncy-pgp into our pom.xml file.

<dependency>
    <groupId>name.neuhalfen.projects.crypto.bouncycastle.openpgp</groupId>
    <artifactId>bouncy-gpg</artifactId>
    <version>${bouncygpg.version}</version>
</dependency>

Bouncy GPG requires us to declare an access to Bintray’s repository:

<repositories>
    <repository>
        <id>bintray</id>
        <name>bintray</name>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
        <url>http://jcenter.bintray.com</url>
    </repository>
</repositories>

For our tests’ purpose, we will define two profiles, two identities: “java” and “avaj”. Each of these identities includes a user ID name, a user ID email, a passphrase and a pair of armored keys (one public and one private). Remember: armored keys use a very recognizable text format that starts with -----BEGIN PGP PUBLIC/PRIVATE KEY BLOCK----- .

The full Identities class can be found on the GitHub repository:

class Identities {

    static final int KEY_SIZE = 2048;

    /****************************************/

    static final String JAVA_USER_ID_NAME = "java";
    static final String JAVA_USER_ID_EMAIL = "java@codesandnotes.be";
    static final String JAVA_PASSPHRASE = "java passphrase";
    static final String JAVA_PRIVATE_KEYS = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
            "Version: BCPG v1.59\n" +
            "\n" +
            ...
            "-----END PGP PRIVATE KEY BLOCK-----\n";
    static final String JAVA_PUBLIC_KEYS = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
            "Version: BCPG v1.59\n" +
            "\n" +
            ...
            "-----END PGP PUBLIC KEY BLOCK-----\n";

    /****************************************/

    static final String AVAJ_USER_ID_NAME = "avaj";
    static final String AVAJ_USER_ID_EMAIL = "avaj@codesandnotes.be";
    static final String AVAJ_PASSPHRASE = "avaj passphrase";
    static final String AVAJ_PRIVATE_KEYS = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
            "Version: BCPG v1.59\n" +
            "\n" +
            ...
            "-----END PGP PRIVATE KEY BLOCK-----\n";
    static final String AVAJ_PUBLIC_KEYS = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
            "Version: BCPG v1.59\n" +
            "\n" +
            ...
            "-----END PGP PUBLIC KEY BLOCK-----\n";
}

The ring of fire

Now that we have all the necessary properties. We can move back to the OpenPGP class and implement our methods to encrypt and decrypt PGP messages. We will also sign those messages to make sure that the sender of the encrypted message is who he says he is.

For “java” to send a message to “avaj”, he needs a key ring that contains his PGP keys as well as”avaj”‘s public key. Bouncy GPG provides all the tools needed to easily build such a ring:

private InMemoryKeyring keyring(String passphrase,
                                ArmoredKeyPair armoredKeyPair,
                                String... recipientsArmoredPublicKey)
        throws IOException, PGPException {
    InMemoryKeyring keyring = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withPassword(passphrase));
    keyring.addSecretKey(armoredKeyPair.privateKey().getBytes(UTF_8));
    keyring.addPublicKey(armoredKeyPair.publicKey().getBytes(UTF_8));
    for (String recipientArmoredPublicKey : recipientsArmoredPublicKey) {
        keyring.addPublicKey(recipientArmoredPublicKey.getBytes(UTF_8));
    }
    return keyring;
}

The KeyrinConfigs class provides static methods to build a ring, whether the keys are provided as files, as a resource, etc. Since we store the keys a String objects, we’re going to build our ring from the String’s bytes and produce an InMemoryKeyring implementation of a ring.
It is then just a matter of adding the sender’s public and private keys (provided, for clarity, as a custom value object named ArmoredKeyPair), and then registering the recipient’s (“avaj”) armored public key.

Encrypting and signing

Good, we have a key ring! We can now proceed to encrypt and sign “java”‘s message to “avaj”. Here’s the full method implementation as found on the repository:

public String encryptAndSign(String unencryptedMessage,
                             String senderUserIdEmail,
                             String senderPassphrase,
                             ArmoredKeyPair senderArmoredKeyPair,
                             String receiverUserId,
                             String receiverArmoredPublicKey)
        throws IOException, PGPException, NoSuchAlgorithmException, SignatureException, NoSuchProviderException {

    InMemoryKeyring keyring = keyring(senderPassphrase, senderArmoredKeyPair, receiverArmoredPublicKey);

    ByteArrayOutputStream encryptedOutputStream = new ByteArrayOutputStream();
    try (
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(encryptedOutputStream);
            OutputStream bouncyGPGOutputStream = BouncyGPG.encryptToStream()
                    .withConfig(keyring)
                    .withStrongAlgorithms()
                    .toRecipient(receiverUserId)
                    .andSignWith(senderUserIdEmail)
                    .armorAsciiOutput()
                    .andWriteTo(bufferedOutputStream)
    ) {
        Streams.pipeAll(new ByteArrayInputStream(unencryptedMessage.getBytes()), bouncyGPGOutputStream);
    }

    return encryptedOutputStream.toString(UTF_8.name());
}

The BouncyGPG.encryptToStream() call creates an output stream to which we can feed the data to encrypt. We specify all the required elements through chaining methods on that stream: the key ring, the algorithms to use, the recipient’s and the signer’s user ID emails and the output format we want (in this case, we’ll generate an armored ASCII output).
We also indicate where the encrypted data must be written to: in this example we pipe the data to a BufferedOutputStream feeding a ByteArrayOutputStream, from which we’ll be able to obtain our encrypted bytes.

The data to encrypt must be a stream too, so we build a ByteArrayInputStream of the data’s bytes. We then feed our input stream to Bouncy GPG’s output stream using BouncyCastle's Streams.pipeAll() utility method. In the end, we obtain our resulting stream of bytes in our “encryptedOutputStream” ByteArrayOutputStream.

In the end, we convert the encrypted output stream into an UTF-8 string and… voilà!

Decrypting and verifying the signature

Of course “avaj” needs to decrypt what “java” sent him. Here’s the method to decrypt a PGP message and verify its signature:

public String decryptAndVerify(String encryptedMessage,
                               String receiverPassphrase,
                               ArmoredKeyPair receiverArmoredKeyPair,
                               String senderUserIdEmail,
                               String senderArmoredPublicKey)
        throws IOException, PGPException, NoSuchProviderException {

    InMemoryKeyring keyring = keyring(receiverPassphrase, receiverArmoredKeyPair, senderArmoredPublicKey);

    ByteArrayOutputStream unencryptedOutputStream = new ByteArrayOutputStream();
    try (
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(unencryptedOutputStream);
            InputStream bouncyGPGInputStream = BouncyGPG
                    .decryptAndVerifyStream()
                    .withConfig(keyring)
                    .andRequireSignatureFromAllKeys(senderUserIdEmail)
                    .fromEncryptedInputStream(new ByteArrayInputStream(encryptedMessage.getBytes(UTF_8)))
    ) {
        Streams.pipeAll(bouncyGPGInputStream, bufferedOutputStream);
    }

    return unencryptedOutputStream.toString(UTF_8.name());
}

As you can see, the approach resembles the one used for the encryption method. Bouncy GPG provides the initial stream to decrypt and verify. We specify the required details on that stream: the receiver’s key ring, the sender’s user ID email for signature verification. In the end we obtain a stream of unencrypted bytes, courtesy of some “java.io” plumbing. Those bytes easily become an UTF-8 string of the message sent by “java”.

OpenPGP at work

To see all this at work, just head to OpenPGPTest and run the encryption and decryption test:

@Test
public void encryptSignedMessageAsJavaAndDecryptItAsAvaj() throws IOException, PGPException, NoSuchAlgorithmException, SignatureException, NoSuchProviderException {

    String unencryptedMessage = "Message from java to avaj: you're all backwards!";

    String encryptedMessage = openPgp.encryptAndSign(
            unencryptedMessage,
            JAVA_USER_ID_EMAIL,
            JAVA_PASSPHRASE,
            OpenPGP.ArmoredKeyPair.of(JAVA_PRIVATE_KEYS, JAVA_PUBLIC_KEYS),
            AVAJ_USER_ID_EMAIL,
            AVAJ_PUBLIC_KEYS);

    assertThat(encryptedMessage).isNotEmpty();

    LOGGER.info("java's encrypted message to avaj:\n" + encryptedMessage);

    String messageDecryptedByAvaj = openPgp.decryptAndVerify(
            encryptedMessage,
            AVAJ_PASSPHRASE,
            OpenPGP.ArmoredKeyPair.of(AVAJ_PRIVATE_KEYS, AVAJ_PUBLIC_KEYS),
            JAVA_USER_ID_EMAIL,
            JAVA_PUBLIC_KEYS);

    assertThat(messageDecryptedByAvaj).isEqualTo(unencryptedMessage);
}

Now we can encrypt and decrypt PGP messages in Java. Next time we will see how we can do that in JavaScript, courtesy of OpenPGP.js.

Cheers!

 

Leave A Comment

Please be polite. We appreciate that. Your email address will not be published and required fields are marked

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: