Quickstart guide: Add sections on encrypting, signing, decryption, verification

This commit is contained in:
Paul Schaub 2022-08-29 13:18:02 +02:00
parent bc24c4626a
commit 4efe8fb468
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
1 changed files with 176 additions and 3 deletions

View File

@ -132,6 +132,22 @@ armorOut.close(); // important!
The output stream will now contain the ASCII armored representation of the binary data.
If the data you want to wrap in ASCII armor is non-OpenPGP data (e.g. the String "Hello World!"),
you need to use the following code:
```java
InputStream inputStream = ...;
OutputStream output = ...;
EncryptionStream armorStream = PGPainless.encryptAndOrSign()
.onOutputStream(output)
.withOptions(ProducerOptions.noEncryptionNoSigning()
.setAsciiArmor(true));
Streams.pipeAll(inputStream, armorStream);
armorStream.close();
```
To remove ASCII armor, you can make use of BouncyCastle's `ArmoredInputStream` as follows:
```java
@ -146,10 +162,167 @@ armorIn.close();
The output stream will now contain the binary OpenPGP data.
### Encrypt and/or Sign a Message
TODO
Encrypting and signing messages is done using the same API in PGPainless.
The type of action depends on the configuration of the `ProducerOptions` class, which in term accepts
`SigningOptions` and `EncryptionOptions` objects:
```java
// Encrypt only
ProducerOptions options = ProducerOptions.encrypt(encryptionOptions);
// Sign only
ProducerOptions options = ProducerOptions.sign(signingOptions);
// Sign and encrypt
ProducerOptions options = ProducerOptions.signAndEncrypt(signingOptions, encryptionOptions);
```
The `ProducerOptions` object can then be passed into the `encryptAndOrSign()` API:
```java
InputStream plaintext = ...; // The data that shall be encrypted and/or signed
OutputStream ciphertext = ...; // Destination for the ciphertext
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign()
.onOutputStream(ciphertext)
.withOptions(options); // pass in the options object
Streams.pipeAll(plaintext, encryptionStream); // pipe the data through
encryptionStream.close(); // important! Close the stream to finish encryption/signing
EncryptionResult result = encryptionStream.getResult(); // metadata
```
The `ciphertext` output stream now contains the encrypted and/or signed data.
Now lets take a look at the configuration of the `SigningOptions` object and how to instruct PGPainless to add a simple
signature to the message:
```java
PGPSecretKeyRing signingKey = ...; // Key used for signing
SecretKeyRingProtector protector = ...; // Protector to unlock the signing key
SigningOptions signOptions = SigningOptions.get()
.addSignature(protector, signingKey);
```
This will add an inline signature to the message.
It is possible to add multiple signatures from different keys by repeating the `addSignature()` method call.
If instead of an inline signature, you want to create a detached signature instead (e.g. because you do not want
to alter the data you are signing), you can add the signature as follows:
```java
signOptions.addDetachedSignature(protector, signingKey);
```
Passing in the `SigningOptions` object like this will result in the signature not being added to the message itself.
Instead, the signature can later be acquired from the `EncryptionResult` object via `EncryptionResult.getDetachedSignatures()`.
That way, it can be distributed independent of the message.
The `EncryptionOptions` object can be configured in a similar way:
```java
PGPPublicKey certificate = ...;
EncryptionOptions encOptions = EncryptionOptions.get()
.addRecipient(certificate);
```
Once again, it is possible to add multiple recipients by repeating the `addRecipient()` method call.
You can also encrypt a message to a password like this:
```java
encOptions.addPassphrase(Passphrase.fromPassword("sw0rdf1sh"));
```
Both methods can be used in combination to create a message which can be decrypted with either a recipients secret key
or the passphrase.
### Decrypt and/or Verify a Message
TODO
Decryption and verification of a message is both done using the same API.
Whether a message was actually signed / encrypted can be determined after the message has been processed by checking
the `OpenPgpMetadata` object which can be obtained from the `DecryptionStream`.
To configure the decryption / verification process, the `ConsumerOptions` object is used:
```java
PGPPublicKeyRing verificationCert = ...; // optional, signers certificate for signature verification
PGPSecretKeyRing decryptionKey = ...; // optional, decryption key
ConsumerOptions options = ConsumerOptions.get()
.addVerificationCert(verificationCert) // add a verification cert for signature verification
.addDecryptionKey(decryptionKey); // add a secret key for message decryption
```
Both verification certificates and decryption keys are optional.
If you know the message is signed, but not encrypted you can omit providing a decryption key.
Same goes for if you know that the message is encrypted, but not signed.
In this case you can omit the verification certificate.
On the other hand, providing these parameters does not hurt.
PGPainless will ignore unused keys / certificates, so if you provide a decryption key and the message is not encrypted,
nothing bad will happen.
It is possible to provide multiple verification certs and decryption keys. PGPainless will pick suitable ones on the fly.
If the message is signed with key `0xAAAA` and you provide certificates `0xAAAA` and `0xBBBB`, it will verify
with cert `0xAAAA` and ignore `0xBBBB`.
To do the actual decryption / verification of the message, do the following:
```java
InputStream ciphertext = ...; // encrypted and/or signed message
OutputStream plaintext = ...; // destination for the plaintext
ConsumerOptions options = ...; // see above
DecryptionStream consumerStream = PGPainless.decryptAndOrVerify()
.onInputStream(ciphertext)
.withOptions(options);
Streams.pipeAll(consumerStream, plaintext);
consumerStream.close(); // important!
// The result will contain metadata of the message
OpenPgpMetadata result = consumerStream.getResult();
```
After the message has been processed, you can consult the `OpenPgpMetadata` object to determine the nature of the message:
```java
boolean wasEncrypted = result.isEncrypted();
SubkeyIdentifier decryptionKey = result.getDecryptionKey();
Map<SubkeyIdentifier, PGPSignature> validSignatures = result.getVerifiedSignatures();
boolean wasSignedByCert = result.containsVerifiedSignatureFrom(certificate);
// For files:
String fileName = result.getFileName();
Date modificationData = result.getModificationDate();
```
### Verify a Signature
TODO
In some cases, detached signatures are distributed alongside the message.
This is the case for example with Debians `Release` and `Release.gpg` files.
Here, `Release` is the plaintext message, which is unaltered by the signing process while `Release.gpg` contains
the detached OpenPGP signature.
To verify a detached signature, you need to call the PGPainless API like this:
```java
InputStream plaintext = ...; // e.g. new FileInputStream(releaseFile);
InputStream detachedSignature = ...; // e.g. new FileInputStream(releaseGpgFile);
PGPPublicKeyRing certificate = ...; // e.g. debians public signing key
ConsumerOptions options = ConsumerOptions.get()
.addVerificationCert(certificate) // provide certificate for verification
.addVerificationOfDetachedSignatures(detachedSignature) // provide detached signature
DecryptionStream verificationStream = PGPainless.decryptAndOrVerify()
.onInputStream(plaintext)
.withOptions(options);
Streams.drain(verificationStream); // push all the data through the stream
verificationStream.close(); // finish verification
OpenPgpMetadata result = verificationStream.getResult(); // get metadata of signed message
assertTrue(result.containsVerifiedSignatureFrom(certificate)); // check if message was in fact signed
```