Skip to content

semtle/bouncy-gpg

Repository files navigation

Build Status codecov license

Mission Statement

Make using Bouncy Castle with OpenPGP great fun again!

This project gives you the following super-powers

Examples

Encrypting a file

The following snippet encrypts /tmp/plaintext.txt to recipient@exmaple.com and signs with sender@example.com. The encrypted file is written to /tmp/encrypted.gpg.

                final KeyringConfig keyringConfig = KeyringConfigs
                   .withKeyRingsFromFiles(
                         "pubring.gpg",
                        "secring.gpg", 
                        KeyringConfigCallbacks.withPassword("s3cr3t"));


                try (
                        final FileOutputStream fileOutput = new FileOutputStream("/tmp/encrypted.gpg");
                        final BufferedOutputStream bufferedOut = new BufferedOutputStream(fileOutput);

                        final OutputStream outputStream = BouncyGPG
                                .encryptToStream()
                                .withConfig(keyringConfig)
                                .withStrongAlgorithms()
                                .toRecipient("recipient@example.com")
                                .andSignWith("sender@example.com")
                                .binaryOutput()
                                .andWriteTo(bufferedOut);

                        final FileInputStream is = new FileInputStream("/tmp/plaintext.txt")
                ) {
                    Streams.pipeAll(is, outputStream);
                }

Decrypting a file and validating the signature

The following snippet decrypts the file created in the snippet above.

               final KeyringConfig keyringConfig = KeyringConfigs
                   .withKeyRingsFromFiles(
                         "pubring.gpg",
                        "secring.gpg", 
                        KeyringConfigCallbacks.withPassword("s3cr3t"));

                try (
                        final FileInputStream cipherTextStream = new FileInputStream("/tmp/encrypted.gpg");

                        final FileOutputStream fileOutput = new FileOutputStream(destFile);
                        final BufferedOutputStream bufferedOut = new BufferedOutputStream("/tmp/plaintext.txt");

                        final InputStream plaintextStream = BouncyGPG
                               .decryptAndVerifyStream()
                               .withConfig(keyringConfig)
                               .andIgnoreSignatures()
                               .fromEncryptedInputStream(cipherTextStream)

                ) {
                    Streams.pipeAll(plaintextStream, bufferedOut);
                }

Demos

demo_reencrypt.sh

A GPG encrypted ZIP file is decrypted on the fly. The structure of the ZIP is then written to disk. All files are re-encrypted before saving them.

  • demo_reencrypt.sh TARGET -- decrypts an encrypted ZIP file containing three files (total size: 1.2 GB) AND re-encrypts each of the files in the ZIP to the TARGET dir.

The sample shows how e.g. batch jobs can work with large files without leaving plaintext on disk (together with Transparent GPG decryption).

This scheme has some very appealing benefits:

  • Data in transit is always encrypted with public key cryptography. Indispensable when you have to use ftp, comforting when you use https and the next Heartbleed pops up.
  • Data at rest is always encrypted with public key cryptography. When (not if) you get hacked, this can make all the difference between "Move along folks, nothing to see here!" and "I lost confidential customer data to the competition".
  • You still need to protect the private keys, but this is considerable easier than the alternatives.

Consider the following batch job:

  1. The customer sends a large (several GB) GPG encrypted ZIP archive containing a directory structure with several data files
  2. Your pre-processing needs to split up the data for further processing
  3. pre-processing stream-processes the GPG/ZIP archive
    1. The GPG stream is decrypted using the BouncyGPG.decryptAndVerifyStream() InputStream
    2. The ZIP file is processed with ExplodeAndReencrypt
      1. Each file from the archive is processed
      2. And transparently encrypted with GPG and stored for further processing
  4. The processing job transparently reads the files without writing plaintext to the disk.

encrypt.sh

  • encrypt.sh SOURCEFILE DESTFILE -- uses the testing keys to encrypt a file. Useful for performance measurements.

HOWTO

Have a look at the example classes to see how easy it is to use Bouncy Castle PGP.

#1 Register Bouncy Castle Provider

Add bouncy castle as a dependency and then install the provider before in your application.

Add Build Dependency

// in build.gradle add a dependency to bouncy castle and bouncy-gpg
//  ...
dependencies {
    compile 'org.bouncycastle:bcprov-jdk15on:1.56'
    compile 'org.bouncycastle:bcpg-jdk15on:1.56'
    //  ...
    compile 'name.neuhalfen.projects.crypto.bouncycastle.openpgp:bouncy-gpg:2.+'

Install Provider

    // in one of you classed install the BC provider
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }

#2 Important Classes

Class Use when you want to
BouncyGPG Starting point for the convenient fluent en- and decryption API.
KeyringConfigs Create default implementations for GPG keyring access. You can also create your own implementations by implementing KeyringConfig.
KeyringConfigCallbacks Used by KeyringConfigs. Create default implementations to provide secret-key passwords.

|

FAQ

Why should I use this?
For common use cases this project is easier than vanilla Bouncy Castle. It also has a pretty decent unit test coverage. It is free (speech & beer).
Can I just grab a class or two for my project?
Sure! Just grab it and hack away! The code is placed under the WTFPL, you can't get much more permissive than this.
Why is the test coverage so low?
Test coverage for 'non-example' code is >85%. Most of the not tested cases are either trivial OR lines that throw exceptions when the input format is broken.
How can I contribute?
Pullrequests are welcome! Please state in your PR that you put your code under the LICENSE.
I am getting 'org.bouncycastle.openpgp.PGPException: checksum mismatch ..' exceptions
The passphrase to your private key is very likely wrong (or you did not pass a passphrase).
I am getting 'java.security.InvalidKeyException: Illegal key size' / 'java.lang.SecurityException: Unsupported keysize or algorithm parameters'
The unrestricted policy files for the JVM are probably not installed.
Where is 'secring.pgp'?
'secring.gpg' has been removed in gpg 2.1. Use the other methods to read private keys.

Building

The project is a basic gradle build. All the scripts use ./gradlew installDist

The coverage report (incl. running tests) is generated with ./gradlew check.

CAVE

  • Only one keyring per userid ("sender@example.com") supported.
  • Only one signing key per userid supported.

LICENSE

This code is placed under the WTFPL. Don't forget to adhere to the BouncyCastle License (http://bouncycastle.org/license.html).

About

Make using Bouncy Castle with OpenPGP fun again!

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 98.9%
  • Other 1.1%