Add more documentation

This commit is contained in:
Paul Schaub 2022-07-11 16:11:40 +02:00
parent 50d31eb463
commit df7505eadb
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
7 changed files with 213 additions and 22 deletions

View File

@ -24,3 +24,4 @@ Contents
quickstart.md
pgpainless-cli/usage.md
sop.md
pgpainless-core/indepth.rst

View File

@ -0,0 +1 @@
# Edit Keys

View File

@ -0,0 +1,101 @@
# PGPainless In-Depth: Generate Keys
There are two API endpoints for generating OpenPGP keys using `pgpainless-core`:
`PGPainless.generateKeyRing()` presents a selection of pre-configured OpenPGP key archetypes:
```java
// Modern, EC-based OpenPGP key with dedicated primary certification key
// This method is recommended by the authors
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
.modernKeyRing(
"Alice <alice@pgpainless.org>",
Passphrase.fromPassword("sw0rdf1sh"));
// Simple, EC-based OpenPGP key with combined certification and signing key
// plus encryption subkey
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
.simpleEcKeyRing(
"Alice <alice@pgpainless.org>",
Passphrase.fromPassword("0r4ng3"));
// Simple, RSA OpenPGP key made of a single RSA key used for all operations
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
.simpleRsaKeyRing(
"Alice <alice@pgpainless.org>",
RsaLength._4096, Passphrase.fromPassword("m0nk3y")):
```
If you have special requirements on algorithms you can use `PGPainless.buildKeyRing()` instead, which offers more
control over parameters:
```java
// Customized key
// Specification for primary key
KeySpecBuilder primaryKeySpec = KeySpec.getBuilder(
KeyType.RSA(RsaLength._8192), // 8192 bits RSA key
KeyFlag.CERTIFY_OTHER) // used for certification
// optionally override algorithm preferences
.overridePreferredCompressionAlgorithms(CompressionAlgorithm.ZLIB)
.overridePreferredHashAlgorithms(HashAlgorithm.SHA512, HashAlgorithm.SHA384)
.overridePreferredSymmetricKeyAlgorithms(SymmetricKeyAlgorithm.AES256);
// Specification for a signing subkey
KeySpecBuilder signingSubKeySpec = KeySpec.getBuilder(
KeyType.ECDSA(EllipticCurve._P256), // P-256 ECDSA key
KeyFlag.SIGN_DATA); // Used for signing
// Specification for an encryption subkey
KeySpecBuilder encryptionSubKeySpec = KeySpec.getBuilder(
KeyType.ECDH(EllipticCurve._P256),
KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE);
// Build the key itself
PGPSecretKeyRing secretKey = PGPainless.buildKeyRing()
.setPrimaryKey(primaryKeySpec)
.addSubkey(signingSubKeySpec)
.addSubkey(encryptionSubKeySpec)
.addUserId("Juliet <juliet@montague.lit>") // Primary User-ID
.addUserId("xmpp:juliet@capulet.lit") // Additional User-ID
.setPassphrase(Passphrase.fromPassword("romeo_oh_Romeo<3")) // passphrase protection
.build();
```
To specify, which algorithm to use for a single (sub) key, `KeySpec.getBuilder(_)` can be used, passing a `KeyType`,
as well as some `KeyFlag`s as argument.
`KeyType` defines an algorithm and its parameters, e.g. RSA with a certain key size, or ECDH over a certain
elliptic curve.
Currently, PGPainless supports the following `KeyType`s:
* `KeyType.RSA(_)`: Signing, Certification, Encryption
* `KeyType.ECDH(_)`: Encryption
* `KeyType.ECDSA(_)`: Signing, Certification
* `KeyType.EDDSA(_)`: Signing, Certification
* `KeyType.XDH(_)`: Encryption
The `KeyFlag`s are used to specify, how the key will be used later on. A signing key can only be used for signing,
if it carries the `KeyFlag.SIGN_DATA`.
A key can carry multiple key flags.
It is possible to override the default algorithm preferences used by PGPainless with custom preferences.
An algorithm preference list contains algorithms from most to least preferred.
Every OpenPGP key MUST have a primary key. The primary key MUST be capable of certification, so you MUST use an
algorithm that can be used to generate signatures.
The primary key can be set by calling `setPrimaryKey(primaryKeySpec)`.
Furthermore, an OpenPGP key can contain zero or more subkeys.
Those can be set by repeatedly calling `addSubkey(subkeySpec)`.
OpenPGP keys are usually bound to User-IDs like names and/or email addresses.
There can be multiple user-ids bound to a key, in which case the very first User-ID will be marked as primary.
To add a User-ID to the key, call `addUserId(userId)`.
By default, keys do not have an expiration date. This can be changed by setting an expiration date using
`setExpirationDate(date)`.
To enable password protection for the OpenPGP key, you can call `setPassphrase(passphrase)`.
If this method is not called, or if the passed in `Passphrase` is empty, the key will be unprotected.
Finally, calling `build()` will generate a fresh OpenPGP key according to the specifications given.

View File

@ -0,0 +1,14 @@
In-Depth Guide to pgpainless-core
=================================
This is an in-depth introduction to OpenPGP using PGPainless.
If you are looking for a quickstart introduction instead, check out [](quickstart.md).
Contents
--------
.. toctree::
generate_keys.md
edit_keys.md
userids.md
passphrase.md

View File

@ -0,0 +1,60 @@
# Passwords
In Java based applications, passing passwords as `String` objects has the
[disadvantage](https://stackoverflow.com/a/8881376/11150851) that you have to rely on garbage collection to clean up
once they are no longer used.
For that reason, `char[]` is the preferred method for dealing with passwords.
Once a password is no longer used, the character array can simply be overwritten to remove the sensitive data from
memory.
## Passphrase
PGPainless uses a wrapper class `Passphrase`, which takes care for the wiping of unused passwords:
```java
Passphrase passphrase = new Passphrase(new char[] {'h', 'e', 'l', 'l', 'o'});
assertTrue(passphrase.isValid());
assertArrayEquals(new char[] {'h', 'e', 'l', 'l', 'o'}, passphrase.getChars()):
// Once we are done, we can clean the data
passphrase.clear();
assertFalse(passphrase.isValid());
assertNull(passphrase.getChars());
```
Furthermore, `Passphrase` can also wrap empty passphrases, which increases null-safety of the API:
```java
Passphrase empty = Passphrase.emptyPassphrase();
assertTrue(empty.isValid());
assertTrue(empty.isEmpty());
assertNull(empty.getChars());
empty.clear();
assertFalse(empty.isValid());
```
## SecretKeyRingProtector
There are certain operations that require you to provide the passphrase for a key.
Examples are decryption of messages, or creating signatures / certifications.
The primary way of telling PGPainless, which password to use for a certain key is the `SecretKeyRingProtector`
interface.
There are multiple implementations of this interface, which may or may not suite your needs:
```java
// If your key is not password protected, this implementation is for you:
SecretKeyRingProtector unprotected = SecretKeyRingProtector
.unprotectedKeys();
// If you use a single passphrase for all (sub-) keys, take this:
SecretKeyRingProtector singlePassphrase = SecretKeyRingProtector
.unlockAnyKeyWith(passphrase);
// If you want to be flexible, use this:
CachingSecretKeyRingProtector flexible = SecretKeyRingProtector
.defaultSecretKeyRingProtector(passphraseCallback);
```

View File

@ -2,6 +2,8 @@
The `pgpainless-core` module contains the bulk of the actual OpenPGP implementation.
This is a quickstart guide. For more in-depth exploration of the API, checkout [](indepth.md).
:::{note}
This chapter is work in progress.
:::
@ -65,7 +67,7 @@ byte[] binary = secretKey.getEncoded();
```
### Generate a Key
PGPainless comes with a simple to use `KeyRingBuilder` class that helps you to quickly generate modern OpenPGP keys.
PGPainless comes with a method to quickly generate modern OpenPGP keys.
There are some predefined key archetypes, but it is possible to fully customize the key generation to fit your needs.
```java
@ -78,27 +80,6 @@ PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.simpleRsaKeyRing("Juliet <juliet@montague.lit>", RsaLength._4096);
```
To generate a customized key, use `PGPainless.buildKeyRing()` instead:
```java
// Customized key
PGPSecretKeyRing keyRing = PGPainless.buildKeyRing()
.setPrimaryKey(KeySpec.getBuilder(
RSA.withLength(RsaLength._8192),
KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER)
.overrideCompressionAlgorithms(CompressionAlgorithm.ZLIB)
).addSubkey(
KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256), KeyFlag.SIGN_DATA)
).addSubkey(
KeySpec.getBuilder(
ECDH.fromCurve(EllipticCurve._P256),
KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
).addUserId("Juliet <juliet@montague.lit>")
.addUserId("xmpp:juliet@capulet.lit")
.setPassphrase(Passphrase.fromPassword("romeo_oh_Romeo<3"))
.build();
```
As you can see, it is possible to generate all kinds of different keys.
### Extract a Certificate

View File

@ -0,0 +1,33 @@
# User-IDs
User-IDs are identities that users go by. A User-ID might be a name, an email address or both.
User-IDs can also contain both and even have a comment.
In general, the format of a User-ID is not fixed, so it can contain arbitrary strings.
However, it is agreed upon to use the
Below is a selection of possible User-IDs:
```
Firstname Lastname <email@address.tld> [Comment]
Firstname Lastname
Firstname Lastname [Comment]
<email@address.tld>
<email@address.tld> [Comment]
```
PGPainless comes with a builder class `UserId`, which can be used to safely construct User-IDs:
```java
UserId nameAndEMail = UserId.nameAndEmail("Jane Doe", "jane@pgpainless.org");
assertEquals("Jane Doe <jane@pgpainless.org>", nameAndEmail.toString()):
UserId onlyEmail = UserId.onlyEmail("john@pgpainless.org");
assertEquals("<john@pgpainless.org>", onlyEmail.toString());
UserId full = UserId.newBuilder()
.withName("Peter Pattern")
.withEmail("peter@pgpainless.org")
.withComment("Work Address")
.build();
assertEquals("Peter Pattern <peter@pgpainless.org> [Work Address]", full.toString());
```