Skip to content
Advertisement

Bouncy Castle Java PGP encryption & decryption

I am trying to implement PGP encryption in Java using Bouncy Castle, also using some of the examples provided by them.

But if I try and decrypt the message I just encrypted, it does not work. The keys and the decryption method seem to be ok, because I can encrypt using an external tool (link) and then successfully decrypt it here.

    public static void main(String[] args) throws PGPException, IOException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
    Security.addProvider(new BouncyCastleProvider());

    byte[] msg = ("Test blabla BLA").getBytes();

    byte[] encData = encrypt(msg, publicKey);
    System.out.println(new String(encData));

    System.err.println("DECRYPTED");
    System.out.println(decrypt(IOUtils.toInputStream(new String(encData), StandardCharsets.UTF_8), privateKey, passphrase));

    System.err.println("DECRYPTED external");
    String testEncryptedMsg = "-----BEGIN PGP MESSAGE-----n" +
            "Version: BCPG C# v1.6.1.0n" +
            "n" +
            "hQIMA4p9BgtBOg+BARAAiLdmGZBupsSkrji1qdC/y6KACuaWytcjVg3LWudWRlH8n" +
            "Ye1U0bz+HYc6mLt21RivAZ3p+lPXPzh/h0GVLW/aI2ngJracL+iEA7eam5/H3NQVn" +
            "2o0CR1mjw4xOf0XwDxaLrnivdeuWniXiZyFN7Ud7lmNG+EmQ0d5ZfR7KEnTJNjSen" +
            "FpzwRffBG5lKB4CxF49jBp+Iupdv4A5JzHuDVBkypPMNhS/UEbuPOt5cfmddYVvWn" +
            "gtDM3rX+ePBhg7d/mVkiDT2KM0Cr7GSrdZ8q83fF4sat3Xp8OHMq61GWriNK4gwbn" +
            "ViT/vmEsY0n55TVgN5VagMZJJlEThkqWE2wpEjq4HM8pNxR4PvP5inqP5oipn8Osn" +
            "Nq2a5f77BS7GB9NQ+hnaLSBsywJkZVKqygN81MeHBnNrVXFQ/Hqg24BLwk49bOuHn" +
            "jTUzorudCnjj/p8/WiyR+PuESkQDLKpo1z9KGEE/rKjTqAwH/fBKJ73XpmAWwd4Un" +
            "1hv/EdF+XDZv7XwxeVjSIK2inAjdNz+way1WNY3qouo7G+gjKf1gbL14q2HVPnd/n" +
            "7mfiW3uWCQ7GhXlHtd1E0WeK2296TdBuSGAvLlVxI4xepsiuVw+zC4BxvAkTXB/an" +
            "HyJZhKPksHMRCt8ZxitMfhdvMNGjc4MDhv1MLqKijpagKhgrmDTiJRMb3ng6QLvJn" +
            "L2GICyuS4aIsz6XiCQvZG8ebfb0J4uogbb5HxS2fGFT+Y/VgpCnwhwj6LnLBy1Q3n" +
            "=D/1dn" +
            "-----END PGP MESSAGE-----n";
    System.out.println(decrypt(IOUtils.toInputStream(testEncryptedMsg, StandardCharsets.UTF_8), privateKey, passphrase));
}

I think the problem is on the encryption side, but I cannot figure out what exactly it is. Does anyone have any ideas?

public static byte[] encrypt(byte[] bytes, String publicKey) throws IOException {

    ByteArrayOutputStream encOut = new ByteArrayOutputStream();

    try {
        PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
                new JcePGPDataEncryptorBuilder(PGPEncryptedData.AES_128)
                        .setSecureRandom(new SecureRandom())
                        .setWithIntegrityPacket(true)
                        .setProvider("BC"));

        PGPPublicKey encryptionKey = readPublicKey(publicKey);
        encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey).setProvider("BC"));

        ArmoredOutputStream aOut = new ArmoredOutputStream(encOut);
        OutputStream cOut = encGen.open(aOut, bytes.length);
        // write out the literal data
        PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
        OutputStream pOut = lData.open(aOut, PGPLiteralData.UTF8, PGPLiteralData.CONSOLE, bytes.length, new Date());
        pOut.write(bytes);
        pOut.close();

        // finish the encryption
        cOut.close();
        aOut.close();

    } catch (PGPException e) {
        log.error("Exception encountered while encoding data.", e);
    }
    return encOut.toByteArray();
}

public static String decrypt(InputStream in, String secretKey, String passphrase) throws IOException {
    String content = null;

    try {
        in = PGPUtil.getDecoderStream(in);
        JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
        PGPEncryptedDataList enc;

        Object o = pgpF.nextObject();
        // the first object might be a PGP marker packet.
        if (o instanceof PGPEncryptedDataList) {
            enc = (PGPEncryptedDataList) o;
        } else {
            enc = (PGPEncryptedDataList) pgpF.nextObject();
        }

        // find the secret key
        Iterator<PGPEncryptedData> it = enc.getEncryptedDataObjects();
        PGPPrivateKey sKey = null;
        PGPPublicKeyEncryptedData pbe = null;
        PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(new ByteArrayInputStream(secretKey.getBytes())), new JcaKeyFingerprintCalculator());

        while (sKey == null && it.hasNext()) {
            pbe = (PGPPublicKeyEncryptedData) it.next();
            sKey = findSecretKey(pgpSec, pbe.getKeyID(), passphrase.toCharArray());
        }
        if (sKey == null) {
            log.error("Secret key for message not found.");
            throw new IllegalArgumentException("secret key for message not found.");
        }

        InputStream clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(sKey));
        JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
        Object message = plainFact.nextObject();

        if (message instanceof PGPCompressedData) {
            PGPCompressedData cData = (PGPCompressedData) message;
            JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
            message = pgpFact.nextObject();
        }

        if (message instanceof PGPLiteralData) {
            PGPLiteralData ld = (PGPLiteralData) message;
            content = new String(ld.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
        } else if (message instanceof PGPOnePassSignatureList) {
            throw new PGPException("encrypted message contains a signed message - not literal data.");
        } else {
            throw new PGPException("message is not a simple encrypted file - type unknown.");
        }

        if (pbe.isIntegrityProtected()) {
            if (!pbe.verify()) {
                log.error("message failed integrity check");
            } else {
                log.info("message integrity check passed");
            }
        } else {
            log.warn("no message integrity check");
        }
    } catch (PGPException e) {
        log.error("Exception encountered while decrypting file.", e);
    }
    return content;
}

private static PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) throws PGPException {
    PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
    if (pgpSecKey == null) {
        return null;
    }

    return pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(pass));
}

private static PGPPublicKey readPublicKey(String publicKey) throws IOException, PGPException {
    InputStream input = IOUtils.toInputStream(publicKey, StandardCharsets.UTF_8);

    PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator());

    Iterator<PGPPublicKeyRing> keyRingIter = pgpPub.getKeyRings();
    while (keyRingIter.hasNext()) {
        PGPPublicKeyRing keyRing = keyRingIter.next();

        Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys();
        while (keyIter.hasNext()) {
            PGPPublicKey key = keyIter.next();

            if (key.isEncryptionKey()) {
                return key;
            }
        }
    }

    throw new IllegalArgumentException("Can't find encryption key in key ring.");
}

Advertisement

Answer

Here is the encryption method that eventually worked for me. It helped debugging together with the examples provided by the BCFipsIn100 book.

public byte[] encrypt(byte[] bytes, String publicKey) throws IOException {

    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
        byte[] compressedData = compress(bytes);

        PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
                new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5)
                        .setWithIntegrityPacket(true)
                        .setSecureRandom(new SecureRandom())
                        .setProvider(PROVIDER));

        PGPPublicKey encryptionKey = readPublicKey(publicKey);
        encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey).setProvider(PROVIDER));

        ArmoredOutputStream aOut = new ArmoredOutputStream(byteArrayOutputStream);
        OutputStream cOut = encGen.open(aOut, compressedData.length);
        cOut.write(compressedData);
        cOut.close();
        aOut.close();

        return byteArrayOutputStream.toByteArray();
    } catch (PGPException e) {
        log.error("Exception encountered while encoding data.", e);
    }

    return new byte[0];
}

private static byte[] compress(byte[] clearData) throws IOException {
    try (ByteArrayOutputStream bOut = new ByteArrayOutputStream()) {
        PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
        OutputStream cos = comData.open(bOut);

        PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
        OutputStream pOut = lData.open(cos,
                PGPLiteralData.BINARY,
                PGPLiteralData.CONSOLE,
                clearData.length,
                new Date());

        pOut.write(clearData);
        pOut.close();

        comData.close();

        return bOut.toByteArray();
    }
}

Advertisement