Encrypt/Decrypt message using PGP

As it took me some time to figure it out, here I am sharing my code that, using PGP,  encrypts/signs a JSON request to be the request body, and decrypts/verifies a response body to see the JSON response.

It’s in Groovy Java, using Bouncy-GPG library. It worked well.

How it works is described in the code comments.

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())
        }
    }
}

Leave a Reply

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