KeyRings

Justin Ludwig edited this page Jul 11, 2016 · 4 revisions

Key Rings

A Ring in JPGPJ is a collection of keys -- essentially equivalent to a GnuPG "keyring" (ie what you see when you run the gpg --list-keys command).

A Key in JPGPJ is a collection of subkeys. A key is like an identity (like a person, or persona, or non-human actor, etc), and different subkeys are used by the same identity for different cryptographic purposes (like a hammer is used to hammer nails, a screwdriver is used to tighten screws, etc).

A Subkey in JPGPJ is a public-key pair. It may consist of only the public part of the pair, or it may include both the public and private parts. Each subkey is designated for a specific cryptographic purpose (or purposes), typically either certification (ie signing other keys), encryption, or signing (ie signing messages). The passphrase for a subkey must be provided in order to use its private part (the private part is needed for signing and decryption).

When a key includes only the public part of its public-key pairs, we'll call it a "public key" (this is the version of the key you distribute publicly to everyone). When a key includes both the public and the private part of its public-key pairs, we'll call it a "secret key" (this is the version of the key you keep protected in a secure location).

JPGPJ does not support creating, modifying, or certifying keys (although the underlying Bouncy Castle implementation does). JPGPJ only supports using keys for encrypting, decrypting, signing, and verifying messages.

Importing From GnuPG

See CreatingKeys for how to create a key with GnuPG.

A simple public key file

If you have a key in your default GnuPG keyring for a user Alice <alice@example.com>, you can export the public parts with this command:

gpg --export alice > /path/to/alice-pub.gpg

You can load the alice-pub.gpg file as an individual Key in JPGPJ with the following code:

Key alice = new Key(new File("/path/to/alice-pub.gpg"));

An ASCII-Armored key file

You can also export the key in ASCII Armor format (base64-encoded plain text, instead of binary), with this command:

gpg --armor --export alice > /path/to/alice-pub.asc

JPGPJ can load ASCII-Armored keys the same way (using the underlying Bouncy Castle implementation, it can automatically detect the format, so the extension of the file doesn't matter -- it could be alice-pub.asc or alice-pub.gpg or alice-pub.txt or just alice-pub, etc):

Key alice = new Key(new File("/path/to/alice-pub.asc"));

An input stream

You can also load a Key from a raw input stream, like if you downloaded it from the web:

Key alice;
InputStream in = new URL("https://example.com/alice.gpg").connect();
try {
    alice = new Key(in);
} finally {
    in.close();
}

An ASCII-Armored string

Or load a Key from an ASCII-Armored string, like if you embedded it in some code:

String aliceArmored = "" +
"-----BEGIN PGP PUBLIC KEY BLOCK-----" +
"" +
"mQENBFbbwVEBCADx5BwVVL7lrU73U0iYNQkv9dxOAHDEnbitL5FqEjmEk3sClM1H" +
// ...
"=AVaJ" +
"-----END PGP PUBLIC KEY BLOCK-----";
Key alice = new Key(aliceArmored);

A Ring constructor

You can also load a key into a JPGPJ Ring, either via a Ring constructor:

Ring ring = new Ring(new File("/path/to/alice-pub.asc"));

The Ring.load() method

Or loading it into an existing Ring:

Ring ring = new Ring();
ring.load(new File("/path/to/alice-pub.asc"));

A simple secret key file

If you have a key in your default GnuPG keyring for a user Bob <bob@example.com>, you can export both the public and private parts with this command:

gpg --export-secret-keys bob > /path/to/bob.gpg

You can load the bob.gpg file as an individual Key in JPGPJ with the following code:

Key alice = new Key(new File("/path/to/bob.gpg"));

All the previous examples also work the same for secret keys (ie the combined public/private parts of a key).

Multiple keys in the same file

You can export multiple keys from GnuPG into the same file. For example, to export both Alice and Bob's public keys from above, you'd use this command:

gpg --armor --export alice bob > /path/to/alice-bob-pub.asc

Then you could import both keys in JPGPJ with the following code:

Ring ring = new Ring(new File("/path/to/alice-bob-pub.asc"));

Multiple keys in multiple files

You can also import keys from multiple files into the same JPGPJ Ring. Like if you export the public keys for Alice and Bob into one file, and the secret key for Carol into another file:

gpg --armor --export alice bob > /path/to/alice-bob-pub.asc
gpg --armor --export-secret-keys carol > /path/to/carol.asc

You could import them all into JPGPJ with the following code:

Ring ring = new Ring(
    new File("/path/to/alice-bob-pub.asc"),
    new File("/path/to/carol.asc")
);

Or:

Ring ring = new Ring();
ring.load(new File("/path/to/alice-bob-pub.asc"));
ring.load(new File("/path/to/carol.asc"));

Or:

Ring ring = new Ring(new File("/path/to/alice-bob-pub.asc"));
ring.load(new File("/path/to/carol.asc"));

Or:

Ring ring = new Ring(new File("/path/to/alice-bob-pub.asc"));
ring.getKeys().add(new Key(new File("/path/to/carol.asc")));

Or:

Ring aliceAndBob = new Ring(new File("/path/to/alice-bob-pub.asc"));
Ring ring = new Ring();
ring.getKeys().addAll(aliceAndBob.getKeys().findAll("alice"));
ring.getKeys().addAll(aliceAndBob.getKeys().findAll("bob"));
ring.load(new File("/path/to/carol.asc"));

Setting Passphrases

To use a secret key to sign a message when encrypting it, or to decrypt a message, you must supply its passphrase. The secret key material will be decrypted on the fly with the passphrase as its needed (so if it's never used, it will never be decrypted). However, once decrypted, the secret key material will be sitting around in memory until it gets garbage-collected by the JVM and overwritten by something else (so, potentially it may exist in memory for a very long time). Similarly, the passphrase you've supplied will sit in memory until the key associated with it is garbage-collected by the JVM and overwritten by something else. Neither the decrypted key nor the passphrase will be written to disk -- unless they're sent to swap by the OS.

You can set individual passphrases on individual subkeys like this:

Key alice = new Key(new File("/path/to/alice.asc"));
for (Subkey subkey : alice.getSubkeys())
    if (subkey.matches("A123B456"))
        subkey.setPassphrase("password123");
    else if (subkey.matches("C789DEF0"))
        subkey.setPassphrase("correct horse battery staple");

Or if all subkeys of a key use the same passphrase (which is the usual case), you can set them all at once via the Key object, like this:

Key alice = new Key(new File("/path/to/alice.asc"));
alice.setPassphrase("password123");

Or when loading the Key like this:

Key alice = new Key(new File("/path/to/alice.asc"), "password123");

Setting Usage Flags

When loading a key, JPGPJ will read each subkey's usage flags and apply them to the forSigning, forVerification, forEncryption, and forDecryption fields of the Subkey object. Signing subkeys that include both the public and private parts of the key pair will be flagged as both forSigning and forVerification; whereas signing subkeys that include only the public part will be flagged as forVerification only. Similarly, encryption subkeys that include both the public and private parts will be flagged as both forEncryption and forDecryption; whereas encryption subkeys that include only the public part will be flagged as forEncryption only.

When using a key for signing that has multiple signing subkeys flagged as forSigning, JPGPJ will automatically select the last subkey. This matches typical GnuPG usage of users who have a subkey flagged for both certification and signing (often considered the "master" subkey, with the private part stored offline), as well as an additional signing subkey (used for day-to-day message signing). However, after loading a key, you can programmatically adjust which subkeys to use for signing by manipulating the forSigning flag of its subkeys with the isForSigning() and setForSigning() methods of a Subkey. JPGPJ doesn't persist any changes you make to keys or subkeys.

You can also turn off the usage of a subkey for signing, verification, encryption, or decryption completely (for example if you load a key into a keyring for the exclusive purpose of using it to encrypt a message but not sign it, or vice versa) by manipulating these forSigning, forVerification, forEncryption, and forDecryption flags via their getters and setters (ie isForSigning(), setForSigning(), etc).

Prevent a key from being used for encryption

For example, say you want to encrypt a message for Alice and Bob, and sign it as Carol -- but not encrypt it for Carol. If you load Alice and Bob's public keys, and Carol's secret key, into a ring like this:

Ring ring = new Ring(
    new Key(new File("/path/to/alice-bob-pub.asc")),
    new Key(new File("path/to/carol.asc"), "hunter2")
);

You can turn off the forEncryption usage of Carol's key like this:

for (Key key: ring.findAll("carol"))
    for (Subkey subkey: key.getSubkeys())
        subkey.setForEncryption(false);

Then when you use this ring to encrypt a message, it will be encrypted for just Alice and Bob, not Carol (but still will be signed by Carol):

new Encryptor(ring).encrypt(
    new File("path/to/plaintext.txt"),
    new File("path/to/ciphertext.txt.gpg")
);

Prevent a key from being used for verification

As another example, say you had the secret keys for Alice and Bob, and wanted to use them to decrypt a message, but not trust Alice or Bob's signing of the message (instead, require that Carol had signed the message). If you loaded Alice and Bob's secret keys, and Carol's public key, into a ring like this:

Ring ring = new Ring(
    new Key(new File("/path/to/alice.asc"), "password123"),
    new Key(new File("/path/to/bob.asc"), "b0bru1z!"),
    new Key(new File("path/to/carol-pub.asc"))
);

You can turn off the forVerification usage of Alice and Bob's keys like this:

for (Key key: ring.getKeys())
    if (!key.matches("alice") || !key.matches("bob"))
        for (Subkey subkey: key.getSubkeys())
            subkey.setForVerification(false);

Then when you use this ring to decrypt a message, it will be able to decrypt a message encrypted for either Alice or Bob, but will verify that it was signed only by Carol:

new Decryptor(ring).decrypt(
    new File("path/to/ciphertext.txt.gpg"),
    new File("path/to/plaintext.txt")
);

Supported Key Types

Bouncy Castle, and therefore JPGPJ, supports the public-key algorithms specified in RFC 4880:

  • RSA
  • Elgamal
  • DSA
  • ECDH
  • ECDSA