Search code examples
javabouncycastlepublic-keypgp

How do I store and read PGP public keys as strings using Bouncycastle Java?


I have been trying to create, encode, store, retrieve, and decode a Bouncy Castle PGP public key. I get what appears to be the wrong output and an EOFException when I try to read the key back in. The key will be stored in a database as a string.

The original RSA encryption public key is extracted from the key ring as follows:

@SuppressWarnings("unchecked")
public PGPPublicKey getPublicKey() {
    PGPPublicKey pk = null;
    Iterator<PGPPublicKey> it = publicKeyRing.getPublicKeys();
    while (pk == null && it.hasNext()) {
        PGPPublicKey key = it.next();
        if (key.isEncryptionKey()) {
            pk = key;
        }
    }
    return pk;
}

It is encoded, ASCII armored, and stored as a string as follows:

    PGPPublicKey contactPK = realContact.getPublicKey();
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    ArmoredOutputStream armored = new ArmoredOutputStream(out);
    contactPK.encode(armored);
    armored.close();
    publicKey = new String(out.toByteArray(), Charset.forName("US-ASCII"));

This gets me a PGP message block where I would expect a PGP public key block:

-----BEGIN PGP MESSAGE-----\nVersion: BCPG v1.50\n\nuQINBFO8StkCEACQ4vrDnBTDjEvQkGwrAHuJSBZL8tNLxhZ9B74afhObhLVzW6ZB\nT3pk/5XcSPOTvcWd9k1yOKJUabCuF5ixFmMz+niFqUVQTtnl7aqOZ+GrDEzmoYmG\nNQROP0EiA1TWtm2+Ja0FqiJauXytt1sIF/Pr5L47FCjtmZKVoXTP8RVFfGLPB0kT\ndjOz53PaEE3GSValh85w24XIH2/gczURUnjphCX1bRwTFr14SfA9X/rFWqv9SqWQ\nV8OiIWrSiwNd5RLJ9q0B+viDzoxrjmnMJZikxhKiuNVKJCu2ccBdMrbW42iBM2w3\n

... (for brevity)

\n-----END PGP MESSAGE-----

When I try to read the string back in to recreate the public key, I get an EOFException:

// Import the public key.
ByteArrayInputStream in =
        new ByteArrayInputStream(stored.publicKey.getBytes(
                                                    Charset.forName("US-ASCII")));
// Needed to read ASCII armored keys
InputStream decoded = PGPUtil.getDecoderStream(in);
BCPGInputStream bcpgIn = new BCPGInputStream(decoded);
RSAPublicBCPGKey bcpgKey = new RSAPublicBCPGKey(bcpgIn);
PublicKeyPacket pkPacket = new PublicKeyPacket(PublicKeyAlgorithmTags.RSA_ENCRYPT,
                                                new Date(), bcpgKey);
publicKey = new PGPPublicKey(pkPacket, new BcKeyFingerprintCalculator());

The exception occurs when I create the RSAPublicBCPGKey.

I am doing something very wrong, but I can't figure out what it is. Can anyone help?


Solution

  • I ran into this issue, and it turned out that the Public Key Ring was actually the public key itself. When I was running the iterator I was actually getting subkeys (which turns into that message block instead of the public key block). If I'm not explaining that well, check out this explanation.

    Assuming realContact is a public key ring that only contains subkeys, you don't actually need to run it through getPublicKey().