pgpainless/docs/source/pgpainless-core/passphrase.md

90 lines
4.1 KiB
Markdown
Raw Normal View History

2022-07-11 16:11:40 +02:00
# 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 which maps `Passphrases` to (sub-)keys.
2022-07-11 16:11:40 +02:00
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);
```
`SecretKeyRingProtector.unprotectedKeys()` will return an empty passphrase for any key.
It is best used when dealing with unencrypted secret keys.
`SecretKeyRingProtector.unlockAnyKeyWith(passphrase)` will return the same exact passphrase for any given key.
You should use this if you have a single key with a static passphrase.
The last example shows how to instantiate the `CachingSecretKeyRingProtector` with a `SecretKeyPassphraseProvider`
as argument.
As the name suggests, the `CachingSecretKeyRingProtector` caches passphrases it knows about in a map.
That way, you only have to provide the passphrase for a certain key only once, after which it will be remembered.
If you try to unlock a protected secret key for which no passphrase is cached, the `getPassphraseFor()` method of
the `SecretKeyPassphraseProvider` callback will be called to interactively ask for the missing passphrase.
Afterwards, the acquired passphrase will be cached for future use.
:::{note}
While especially the `CachingSecretKeyRingProtector` can handle multiple keys without problems, it is advised
to use individual `SecretKeyRingProtector` objects per key.
The reason for this is, that internally the 64bit key-id is used to resolve `Passphrase` objects and collisions are not
unlikely in this key-space.
Furthermore, multiple OpenPGP keys could contain the same subkey, but with different passphrases set.
If the same `SecretKeyRingProtector` is used for two OpenPGP keys with the same subkey, but different passwords,
the key-id collision will cause the password to be overwritten for one of the keys, which might result in issues.
See `FLO-04-004 WP2` of the [2021 security audit](https://cure53.de/pentest-report_pgpainless.pdf) for more details.
:::
Most `SecretKeyRingProtector` implementations can be instantiated with custom `KeyRingProtectionSettings`.
By default, most implementations use `KeyRingProtectionSettings.secureDefaultSettings()` which corresponds to iterated
and salted S2K using AES256 and SHA256 with an iteration count of 65536.