justinludwig / jpgpj

Java Pretty Good Privacy Jig
MIT License
74 stars 20 forks source link

Load GPG2 secret keys from private-keys-v1.d #24

Open justinludwig opened 4 years ago

justinludwig commented 4 years ago

JPGPJ currently cannot the load secret keys stored in a .gnupg directory, if the .gnupg directory was created/upgraded by GPG2. GPG2 stores key information for new keyrings in the .gnupgp/pubring.kbx file, which JPGPJ can load (see #21); but it stores the secret key material for those keys in the .gnupg/private-keys-v1.d directory, with a .key file for each secret subkey (which JPGPJ currently can't load).

It would be nice if you could just point JPGPJ at a directory and have it load up all available keys from it (whether they stored in new GPG2 .kbx and .key files, or traditional OpenPGP .gpg/.asc files), like:

Ring ring = new Ring(new File("path/to/.gnupg"));
KeyForSigning signingKey = new KeyForSigning(ring.findAll("alice").get(0), "password123");
KeyForEncryption encryptionKey = new KeyForEncryption(ring.findAll("bob").get(0));
new Encryptor(signingKey, encryptionKey).encrypt(
    new File("plain.txt"),
    new File("cipher.txt.gpg")
);

or:

Ring ring = new Ring(new File("path/to/.gnupg"));
ring.findAll("bob").get(0).setPassphrase("b0bru1z!");
new Decryptor(ring).decrypt(new File("cipher.txt.gpg"), new File("plain.txt"));

Also, it would be nice if you could call out specific .kbx and .key files to load into a JPGPJ ring:

Ring ring = new Ring(
    new File("path/to/pubring.kbx"),
    new File("path/to/1234ABCD.key"),
    new File("path/to/5678EF90.key"),
    new File("path/to/ABCD1234.key"),
    new File("path/to/EF905789.key")
);

Or load the .key file for just a specific subkey:

Ring ring = new Ring(new File("path/to/pubring.kbx"));
Key alice = ring.findAll("0x1234ABCD").get(0);
Subkey signingKey = alice.findAll("0x1234ABCD").get(0);
signingKey.loadSecretKey(new File("path/to/alice-signing.key"));
signingKey.setPassphrase("password123");

The format of GPG2 .key files is described here:

https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=agent/keyformat.txt;hb=HEAD

And Bouncy Castle looks like it has support for loading the key material from those files here:

https://github.com/bcgit/bc-java/blob/master/pg/src/main/java/org/bouncycastle/gpg/SExprParser.java

The files in the .gnupg/private-keys-v1.d directory are identified by their GPG2 keygrip. It doesn't look like Bouncy Castle has any tools for calculating keygrips. The best reference for this seems to be the libgcrypt implementation, with the _gcry_pk_get_kegrip function in this file:

https://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=blob;f=cipher/pubkey.c;hb=HEAD

And test cases for different key types in this file:

https://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=blob;f=tests/keygrip.c;hb=HEAD