mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-26 06:12:06 +01:00
Add more documentation
This commit is contained in:
parent
50d31eb463
commit
df7505eadb
7 changed files with 213 additions and 22 deletions
|
@ -24,3 +24,4 @@ Contents
|
||||||
quickstart.md
|
quickstart.md
|
||||||
pgpainless-cli/usage.md
|
pgpainless-cli/usage.md
|
||||||
sop.md
|
sop.md
|
||||||
|
pgpainless-core/indepth.rst
|
1
docs/source/pgpainless-core/edit_keys.md
Normal file
1
docs/source/pgpainless-core/edit_keys.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Edit Keys
|
101
docs/source/pgpainless-core/generate_keys.md
Normal file
101
docs/source/pgpainless-core/generate_keys.md
Normal 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.
|
14
docs/source/pgpainless-core/indepth.rst
Normal file
14
docs/source/pgpainless-core/indepth.rst
Normal 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
|
60
docs/source/pgpainless-core/passphrase.md
Normal file
60
docs/source/pgpainless-core/passphrase.md
Normal 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);
|
||||||
|
```
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
The `pgpainless-core` module contains the bulk of the actual OpenPGP implementation.
|
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}
|
:::{note}
|
||||||
This chapter is work in progress.
|
This chapter is work in progress.
|
||||||
:::
|
:::
|
||||||
|
@ -65,7 +67,7 @@ byte[] binary = secretKey.getEncoded();
|
||||||
```
|
```
|
||||||
|
|
||||||
### Generate a Key
|
### 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.
|
There are some predefined key archetypes, but it is possible to fully customize the key generation to fit your needs.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
|
@ -78,27 +80,6 @@ PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||||
.simpleRsaKeyRing("Juliet <juliet@montague.lit>", RsaLength._4096);
|
.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.
|
As you can see, it is possible to generate all kinds of different keys.
|
||||||
|
|
||||||
### Extract a Certificate
|
### Extract a Certificate
|
||||||
|
|
33
docs/source/pgpainless-core/userids.md
Normal file
33
docs/source/pgpainless-core/userids.md
Normal 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());
|
||||||
|
```
|
Loading…
Reference in a new issue