Encrypt/Decrypt message using PGP

Here I am sharing my code when, using PGP, I had to encrypt/sign a JSON request to be the request body, and also to decrypt/verify a response body to see the JSON response.

It’s in Groovy Java. Thank you to Bouncy-GPG for providing the framework on top of Bouncy Castle. It worked well in my project.

How they work is explained in the code comments.

Hopefully this helps.

import java.nio.charset.Charset
import java.security.NoSuchAlgorithmException
import java.security.NoSuchProviderException;
import java.security.Security
import java.security.SignatureException
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.BouncyGPG
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.KeyringConfigCallbacks
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfig
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfigs
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.util.io.Streams


class PayloadService {
    /**
     * On sending
     *
     * To encrypt request payload, to be the request body
     *
     * The PGP works as follow:
     * First, creates a key to encrypt the payload, 
     * then encrypts the key using the recipient's public key.
     * Then encrypts the payload using that key.
     * Then signs the encrypted payload using the sender's signature,
     * so the recipient is sure that this is really from the sender.
     *
     * @param sourceText, i.e. the payload string 
     * @param pubKeyRing, sender's public key ring, containing also the recipient's public key
     * @param privKeyRing, sender's private key ring
     * @param privKeyPwd, sender's private key password
     * @param sender, corresponds with user id in sender's key
     * @param recipient, corresponds with user id in recipient's key
     *
     * @return the encrypted message i.e. PGP message, must be set with UTF-8
     */
    String encryptAndSign(String sourceText, File pubKeyRing, File privKeyRing, String privKeyPwd, String sender, String recipient)
            throws IOException, PGPException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException
    {
        installBCProvider()

        KeyringConfig keyringConfig = KeyringConfigs.withKeyRingsFromFiles(pubKeyRing,
                privKeyRing, KeyringConfigCallbacks.withPassword(privKeyPwd))

        ByteArrayOutputStream cipherStream = new ByteArrayOutputStream()

        OutputStream encryptionStream = BouncyGPG
                .encryptToStream()
                .withConfig(keyringConfig)
                .withStrongAlgorithms()
                .toRecipient(recipient)
                .andSignWith(sender)
                .armorAsciiOutput()
                .andWriteTo(cipherStream)
        encryptionStream.write(sourceText.getBytes())
        encryptionStream.close()

        cipherStream.close()

        byte[] cipherBytes = cipherStream.toByteArray()

        return new String(cipherBytes, Charset.forName("UTF-8"))
    }

    /**
     * On receiving
     *
     * To decrypt response body, to see the response payload
     *
     * The PGP works as follows:
     * Decrypts the key (to open the payload) using recipient's private key.
     * It's possible because the key was encrypted using the recipient's public key.
     * Then, using that key, decrypts the payload.
     * Then, using the sender's signature, verifies if the payload is really
     * from the sender.
     *
     * @param cipherText, the PGP message to be decrypted 
     * @param pubKeyRing, recipient's public key ring, containing also the sender's public key
     * @param privKeyRing, recipient's private key ring
     * @param privKeyPwd, recipient's private key password
     * @param sender, corresponds with user id in sender's key
     *
     * @return the original text, must be set with UTF-8
     */
    String decryptAndVerify(String cipherText, File pubKeyRing, File privKeyRing, String privKeyPwd, String sender)
            throws IOException, PGPException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException
    {
        installBCProvider()

        KeyringConfig keyringConfig = KeyringConfigs.withKeyRingsFromFiles(pubKeyRing,
                privKeyRing, KeyringConfigCallbacks.withPassword(privKeyPwd))

        ByteArrayInputStream cipherStream = new ByteArrayInputStream(cipherText.getBytes());

        InputStream decryptedStream = BouncyGPG
                .decryptAndVerifyStream()
                .withConfig(keyringConfig)
                .andRequireSignatureFromAllKeys(sender)
                .fromEncryptedInputStream(cipherStream)

        byte[] decryptedBytes = Streams.readAll(decryptedStream)

        return new String(decryptedBytes, Charset.forName("UTF-8"))
    }

    /**
     * Call this before all PGP works,
     * as we are using Jens Neuhalfen's library based on Bouncy Castle
     */
    private void installBCProvider() {
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider())
        }
    }
}