From df7505eadba46aa6dffd0ccdfa04abcfcbe31028 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 11 Jul 2022 16:11:40 +0200 Subject: [PATCH] Add more documentation --- docs/source/index.rst | 1 + docs/source/pgpainless-core/edit_keys.md | 1 + docs/source/pgpainless-core/generate_keys.md | 101 +++++++++++++++++++ docs/source/pgpainless-core/indepth.rst | 14 +++ docs/source/pgpainless-core/passphrase.md | 60 +++++++++++ docs/source/pgpainless-core/quickstart.md | 25 +---- docs/source/pgpainless-core/userids.md | 33 ++++++ 7 files changed, 213 insertions(+), 22 deletions(-) create mode 100644 docs/source/pgpainless-core/edit_keys.md create mode 100644 docs/source/pgpainless-core/generate_keys.md create mode 100644 docs/source/pgpainless-core/indepth.rst create mode 100644 docs/source/pgpainless-core/passphrase.md create mode 100644 docs/source/pgpainless-core/userids.md diff --git a/docs/source/index.rst b/docs/source/index.rst index cbe82355..362d340d 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -24,3 +24,4 @@ Contents quickstart.md pgpainless-cli/usage.md sop.md + pgpainless-core/indepth.rst \ No newline at end of file diff --git a/docs/source/pgpainless-core/edit_keys.md b/docs/source/pgpainless-core/edit_keys.md new file mode 100644 index 00000000..d7ed93e7 --- /dev/null +++ b/docs/source/pgpainless-core/edit_keys.md @@ -0,0 +1 @@ +# Edit Keys diff --git a/docs/source/pgpainless-core/generate_keys.md b/docs/source/pgpainless-core/generate_keys.md new file mode 100644 index 00000000..2b84ce1e --- /dev/null +++ b/docs/source/pgpainless-core/generate_keys.md @@ -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 ", + Passphrase.fromPassword("sw0rdf1sh")); + +// Simple, EC-based OpenPGP key with combined certification and signing key +// plus encryption subkey +PGPSecretKeyRing secretKey = PGPainless.generateKeyRing() + .simpleEcKeyRing( + "Alice ", + Passphrase.fromPassword("0r4ng3")); + +// Simple, RSA OpenPGP key made of a single RSA key used for all operations +PGPSecretKeyRing secretKey = PGPainless.generateKeyRing() + .simpleRsaKeyRing( + "Alice ", + 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 ") // 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. \ No newline at end of file diff --git a/docs/source/pgpainless-core/indepth.rst b/docs/source/pgpainless-core/indepth.rst new file mode 100644 index 00000000..b1c4beba --- /dev/null +++ b/docs/source/pgpainless-core/indepth.rst @@ -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 diff --git a/docs/source/pgpainless-core/passphrase.md b/docs/source/pgpainless-core/passphrase.md new file mode 100644 index 00000000..27769aad --- /dev/null +++ b/docs/source/pgpainless-core/passphrase.md @@ -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); +``` \ No newline at end of file diff --git a/docs/source/pgpainless-core/quickstart.md b/docs/source/pgpainless-core/quickstart.md index e20a53ec..f2a018ef 100644 --- a/docs/source/pgpainless-core/quickstart.md +++ b/docs/source/pgpainless-core/quickstart.md @@ -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 ", 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 ") - .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 diff --git a/docs/source/pgpainless-core/userids.md b/docs/source/pgpainless-core/userids.md new file mode 100644 index 00000000..ff2db82c --- /dev/null +++ b/docs/source/pgpainless-core/userids.md @@ -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 [Comment] +Firstname Lastname +Firstname Lastname [Comment] + + [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 ", nameAndEmail.toString()): + +UserId onlyEmail = UserId.onlyEmail("john@pgpainless.org"); +assertEquals("", onlyEmail.toString()); + +UserId full = UserId.newBuilder() + .withName("Peter Pattern") + .withEmail("peter@pgpainless.org") + .withComment("Work Address") + .build(); +assertEquals("Peter Pattern [Work Address]", full.toString()); +``` \ No newline at end of file