From 4efe8fb468cafb1ba478d1d8bdb332657d968565 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 29 Aug 2022 13:18:02 +0200 Subject: [PATCH] Quickstart guide: Add sections on encrypting, signing, decryption, verification --- docs/source/pgpainless-core/quickstart.md | 179 +++++++++++++++++++++- 1 file changed, 176 insertions(+), 3 deletions(-) diff --git a/docs/source/pgpainless-core/quickstart.md b/docs/source/pgpainless-core/quickstart.md index a965799a..371a5f6f 100644 --- a/docs/source/pgpainless-core/quickstart.md +++ b/docs/source/pgpainless-core/quickstart.md @@ -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 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 +``` \ No newline at end of file