2021-10-07 15:48:52 +02:00
<!--
SPDX-FileCopyrightText: 2021 Paul Schaub < info @ pgpainless . org >
SPDX-License-Identifier: Apache-2.0
-->
2021-05-27 17:11:38 +02:00
# PGPainless - Use OpenPGP Painlessly!
2018-07-24 16:06:45 +02:00
2020-11-29 15:40:17 +01:00
[![Travis (.com) ](https://travis-ci.com/pgpainless/pgpainless.svg?branch=master )](https://travis-ci.com/pgpainless/pgpainless)
2021-06-06 13:33:14 +02:00
[![Maven Central ](https://badgen.net/maven/v/maven-central/org.pgpainless/pgpainless-core )](https://search.maven.org/artifact/org.pgpainless/pgpainless-core)
2018-07-24 16:18:05 +02:00
[![Coverage Status ](https://coveralls.io/repos/github/pgpainless/pgpainless/badge.svg?branch=master )](https://coveralls.io/github/pgpainless/pgpainless?branch=master)
2021-05-27 17:11:38 +02:00
[![Interoperability Test-Suite ](https://badgen.net/badge/Sequoia%20Test%20Suite/%232/green )](https://tests.sequoia-pgp.org/)
2021-06-06 13:33:14 +02:00
[![PGP ](https://img.shields.io/badge/pgp-A027%20DB2F%203E1E%20118A-blue )](https://keyoxide.org/7F9116FEA90A5983936C7CFAA027DB2F3E1E118A)
2021-10-07 16:31:46 +02:00
[![REUSE status ](https://api.reuse.software/badge/github.com/pgpainless/pgpainless )](https://api.reuse.software/info/github.com/pgpainless/pgpainless)
2022-02-11 14:23:19 +01:00
2021-05-27 17:11:38 +02:00
## About
2018-06-27 16:06:50 +02:00
PGPainless aims to make using OpenPGP in Java projects as simple as possible.
It does so by introducing an intuitive Builder structure, which allows easy
2021-10-20 14:33:41 +02:00
setup of encryption/decryption operations, as well as straight forward key generation.
2018-06-27 16:06:50 +02:00
2022-02-16 13:50:45 +01:00
PGPainless is based around the Bouncy Castle java library and can be used on Android down to API level 10.
It can be configured to either use the Java Cryptographic Engine (JCE), or Bouncy Castles lightweight reimplementation.
2018-06-27 16:06:50 +02:00
2022-02-16 13:50:45 +01:00
While signature verification in Bouncy Castle is limited to signature correctness, PGPainless goes much further.
2021-05-27 17:11:38 +02:00
It also checks if signing subkeys are properly bound to their primary key, if keys are expired or revoked, as well as
if keys are allowed to create signatures in the first place.
2018-07-19 19:01:16 +02:00
2021-05-27 17:11:38 +02:00
These rigorous checks make PGPainless stand out from other Java-based OpenPGP libraries and are the reason why
2021-06-10 12:39:11 +02:00
PGPainless currently [*scores second place* on Sequoia-PGPs Interoperability Test-Suite ](https://tests.sequoia-pgp.org ).
2018-07-19 19:01:16 +02:00
2021-06-05 23:00:12 +02:00
> At FlowCrypt we are using PGPainless in our Kotlin code bases on Android and on server side.
2021-10-07 14:15:40 +02:00
> The ergonomics of legacy PGP tooling on Java is not very good, and PGPainless improves it greatly.
2021-06-05 23:00:12 +02:00
> We were so happy with our initial tests and with Paul - the maintainer, that we decided to sponsor further development of this library.
>
> -Tom @ FlowCrypt.com
2021-12-28 01:19:43 +01:00
> Finally, testing irrefutably confirmed that the library removes many associated difficulties with PGP use in its provision of an approachable and uncomplicated API.
> In this regard, Paul Schaub deserves the utmost praise.
>
> -Mario @ Cure53.de
2022-01-12 00:26:52 +01:00
## Get Started
The very easiest way to start using OpenPGP on Java/Kotlin based systems is to use an implementation of [sop-java ](https://github.com/pgpainless/sop-java ).
`sop-java` defines a very stripped down API and is super easy to get started with.
Luckily PGPainless provides an implementation for the `sop-java` interface definitions in the form of [pgpainless-sop ](pgpainless-sop/README.md ).
If you need more flexibility, directly using `pgpainless-core` is the way to go.
2021-05-27 17:11:38 +02:00
## Features
2018-06-27 16:06:50 +02:00
2021-05-27 17:11:38 +02:00
Most of PGPainless' features can be accessed directly from the `PGPainless` class.
If you want to get started, this class is your friend :)
2018-06-27 16:06:50 +02:00
2022-02-11 14:39:11 +01:00
For further details you should check out the [javadoc ](https://javadoc.io/doc/org.pgpainless/pgpainless-core )!
2018-06-27 16:06:50 +02:00
2021-07-01 17:15:33 +02:00
### Handle Keys
Reading keys from ASCII armored strings or from binary files is easy:
```java
String key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n"...
PGPSecretKeyRing secretKey = PGPainless.readKeyRing()
.secretKeyRing(key);
```
Similarly, keys can quickly be exported::
```java
PGPSecretKeyRing secretKey = ...;
String armored = PGPainless.asciiArmor(secretKey);
ByteArrayOutputStream binary = new ByteArrayOutputStream();
secretKey.encode(binary);
```
Extract a public key certificate from a secret key:
```java
PGPSecretKeyRing secretKey = ...;
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
```
2021-05-27 17:11:38 +02:00
### Easily Generate Keys
PGPainless comes with a simple to use `KeyRingBuilder` class that helps you to quickly generate modern OpenPGP keys.
There are some predefined key archetypes, but it is possible to fully customize key generation to your needs.
2018-06-27 16:06:50 +02:00
2018-07-30 17:58:23 +02:00
```java
2021-05-27 17:11:38 +02:00
// RSA key without additional subkeys
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
2018-06-28 15:49:32 +02:00
.simpleRsaKeyRing("Juliet < juliet @ montague . lit > ", RsaLength._4096);
2021-05-27 17:11:38 +02:00
// EdDSA primary key with EdDSA signing- and XDH encryption subkeys
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Romeo < romeo @ montague . lit > ", "I defy you, stars!");
2018-06-27 16:06:50 +02:00
2021-05-27 17:11:38 +02:00
// Customized key
2021-11-02 12:25:52 +01:00
PGPSecretKeyRing keyRing = PGPainless.buildKeyRing()
2021-09-20 12:32:15 +02:00
.setPrimaryKey(KeySpec.getBuilder(
RSA.withLength(RsaLength._8192),
KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER))
.addSubkey(
2021-09-13 19:50:24 +02:00
KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256), KeyFlag.SIGN_DATA)
.overrideCompressionAlgorithms(CompressionAlgorithm.ZLIB)
2021-09-20 12:32:15 +02:00
).addSubkey(
2021-09-13 19:50:24 +02:00
KeySpec.getBuilder(
ECDH.fromCurve(EllipticCurve._P256),
KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
2021-09-20 12:32:15 +02:00
).addUserId("Juliet < juliet @ montague . lit > ")
.addUserId("xmpp:juliet@capulet.lit")
.setPassphrase("romeo_oh_Romeo< 3 " )
2018-06-27 16:06:50 +02:00
.build();
```
2021-05-27 17:11:38 +02:00
### Encrypt and Sign Data
PGPainless makes it easy and painless to encrypt and/or sign data.
Passed in keys are automatically evaluated, so that you don't accidentally encrypt to revoked or expired keys.
PGPainless will furthermore detect which algorithms are supported by recipient keys and will negotiate
algorithms accordingly.
Still it allows you to manually specify which algorithms to use of course.
2018-06-27 16:06:50 +02:00
2018-07-30 17:58:23 +02:00
```java
2021-05-27 17:11:38 +02:00
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign()
.onOutputStream(outputStream)
.withOptions(
ProducerOptions.signAndEncrypt(
new EncryptionOptions()
.addRecipient(aliceKey)
.addRecipient(bobsKey)
// optionally encrypt to a passphrase
.addPassphrase(Passphrase.fromPassword("password123"))
// optionally override symmetric encryption algorithm
.overrideEncryptionAlgorithm(SymmetricKeyAlgorithm.AES_192),
new SigningOptions()
// Sign in-line (using one-pass-signature packet)
.addInlineSignature(secretKeyDecryptor, aliceSecKey, signatureType)
// Sign using a detached signature
.addDetachedSignature(secretKeyDecryptor, aliceSecKey, signatureType)
// optionally override hash algorithm
.overrideHashAlgorithm(HashAlgorithm.SHA256)
).setAsciiArmor(true) // Ascii armor or not
);
2021-07-31 20:40:31 +02:00
Streams.pipeAll(plaintextInputStream, encryptionStream);
2021-05-27 17:11:38 +02:00
encryptionStream.close();
2021-11-02 13:01:43 +01:00
// Information about the encryption (algorithms, detached signatures etc.)
EncryptionResult result = encryptionStream.getResult();
2018-06-27 16:06:50 +02:00
```
2021-05-27 17:11:38 +02:00
### Decrypt and Verify Signatures
2018-06-27 16:06:50 +02:00
2021-07-01 17:15:33 +02:00
Decrypting data and verifying signatures is being done similarly.
2021-05-27 17:11:38 +02:00
PGPainless will not only verify *correctness* of signatures, but also if the signing key was allowed to create the signature.
A key might not be allowed to create signatures if, for example, it expired or was revoked, or was not properly bound to the key ring.
2021-07-01 17:15:33 +02:00
Furthermore, PGPainless will reject signatures made using weak algorithms like SHA-1.
2021-05-27 17:11:38 +02:00
This behaviour can be modified though using the `Policy` class.
2018-06-27 16:06:50 +02:00
2018-07-30 17:58:23 +02:00
```java
2021-05-27 17:11:38 +02:00
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
.onInputStream(encryptedInputStream)
2021-07-01 17:15:33 +02:00
.withOptions(new ConsumerOptions()
.addDecryptionKey(bobSecKeys, secretKeyProtector)
.addVerificationCert(alicePubKeys)
);
2018-06-27 16:06:50 +02:00
2021-07-31 20:40:31 +02:00
Streams.pipeAll(decryptionStream, outputStream);
2021-05-27 17:11:38 +02:00
decryptionStream.close();
2018-06-27 16:06:50 +02:00
2021-05-27 17:11:38 +02:00
// Result contains information like signature status etc.
OpenPgpMetadata metadata = decryptionStream.getResult();
2018-06-27 16:06:50 +02:00
```
2020-08-30 23:06:40 +02:00
*After* the `DecryptionStream` was closed, you can get metadata about the processed data by retrieving the `OpenPgpMetadata` .
2018-06-27 16:06:50 +02:00
Again, this object will contain information about how the message was encrypted, who signed it and so on.
2021-07-01 17:15:33 +02:00
#### Many more examples can be found in the [examples package](pgpainless-core/src/test/java/org/pgpainless/example)!!!
2021-05-27 17:11:38 +02:00
## Include PGPainless in your Project
2018-06-27 16:06:50 +02:00
2021-05-27 17:11:38 +02:00
PGPainless is available on maven central. In order to include it in your project, just add the
maven central repository and add PGPainless as a dependency.
```gradle
repositories {
mavenCentral()
}
dependencies {
2022-03-07 15:02:24 +01:00
implementation 'org.pgpainless:pgpainless-core:1.1.2'
2021-05-27 17:11:38 +02:00
}
```
2020-08-30 23:06:40 +02:00
2018-06-27 16:06:50 +02:00
## About
2021-11-02 13:01:43 +01:00
PGPainless is a by-product of my [Summer of Code 2018 project ](https://blog.jabberhead.tk/summer-of-code-2018/ )
implementing OpenPGP support for the XMPP client library [Smack ](https://github.com/igniterealtime/Smack ).
2021-12-28 13:53:25 +01:00
For that project I was in need of a simple-to-use OpenPGP library.
2018-06-27 16:06:50 +02:00
Originally I was going to use [Bouncy-GPG ](https://github.com/neuhalje/bouncy-gpg ) for my project,
but ultimately I decided to create my own OpenPGP library which better fits my needs.
2021-11-02 13:01:43 +01:00
However, PGPainless was heavily influenced by Bouncy-GPG.
2018-07-18 16:55:48 +02:00
To reach out to the development team, feel free to send a mail: info@pgpainless.org
## Development
PGPainless is developed in - and accepts contributions from - the following places:
* [Github ](https://github.com/pgpainless/pgpainless )
2020-01-10 19:39:02 +01:00
* [Codeberg ](https://codeberg.org/PGPainless/pgpainless )
2018-07-18 16:55:48 +02:00
Please follow the [code of conduct ](CODE_OF_CONDUCT.md ) if you want to be part of the project.
2021-09-13 19:45:49 +02:00
## Acknowledgements
Development on PGPainless is generously sponsored by [FlowCrypt.com ](https://flowcrypt.com ). Thank you very very very much!
Continuous Integration is kindly provided by [Travis-CI.com ](https://travis-ci.com/ ).