diff --git a/pgpainless-core/src/test/java/org/pgpainless/example/Encrypt.java b/pgpainless-core/src/test/java/org/pgpainless/example/Encrypt.java new file mode 100644 index 00000000..329e55e6 --- /dev/null +++ b/pgpainless-core/src/test/java/org/pgpainless/example/Encrypt.java @@ -0,0 +1,147 @@ +/* + * Copyright 2021 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pgpainless.example; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.util.io.Streams; +import org.junit.jupiter.api.Test; +import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.DocumentSignatureType; +import org.pgpainless.decryption_verification.DecryptionStream; +import org.pgpainless.decryption_verification.OpenPgpMetadata; +import org.pgpainless.encryption_signing.EncryptionOptions; +import org.pgpainless.encryption_signing.EncryptionStream; +import org.pgpainless.encryption_signing.ProducerOptions; +import org.pgpainless.encryption_signing.SigningOptions; +import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.key.util.KeyRingUtils; +import org.pgpainless.util.Passphrase; + +public class Encrypt { + + /** + * In this example, Alice is sending a signed and encrypted message to Bob. + * She signs the message using her key and then encrypts the message to both bobs certificate and her own. + * + * Bob subsequently decrypts the message using his key and verifies that the message was signed by Alice using + * her certificate. + */ + @Test + public void encryptAndSignMessage() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException { + // Prepare keys + PGPSecretKeyRing keyAlice = PGPainless.generateKeyRing() + .modernKeyRing("alice@pgpainless.org", null); + PGPPublicKeyRing certificateAlice = KeyRingUtils.publicKeyRingFrom(keyAlice); + SecretKeyRingProtector protectorAlice = SecretKeyRingProtector.unprotectedKeys(); + + PGPSecretKeyRing keyBob = PGPainless.generateKeyRing() + .modernKeyRing("bob@pgpainless.org", null); + PGPPublicKeyRing certificateBob = KeyRingUtils.publicKeyRingFrom(keyBob); + SecretKeyRingProtector protectorBob = SecretKeyRingProtector.unprotectedKeys(); + + // plaintext message to encrypt + String message = "Hello, World!\n"; + ByteArrayOutputStream ciphertext = new ByteArrayOutputStream(); + // Encrypt and sign + EncryptionStream encryptor = PGPainless.encryptAndOrSign() + .onOutputStream(ciphertext) + .withOptions(ProducerOptions.signAndEncrypt( + // we want to encrypt communication (affects key selection based on key flags) + EncryptionOptions.encryptCommunications() + .addRecipient(certificateBob) + .addRecipient(certificateAlice), + new SigningOptions() + .addInlineSignature(protectorAlice, keyAlice, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT) + ).setAsciiArmor(true) + ); + + // Pipe data trough and CLOSE the stream (important) + Streams.pipeAll(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)), encryptor); + encryptor.close(); + String encryptedMessage = ciphertext.toString(); + + // Decrypt and verify signatures + DecryptionStream decryptor = PGPainless.decryptAndOrVerify() + .onInputStream(new ByteArrayInputStream(encryptedMessage.getBytes(StandardCharsets.UTF_8))) + .decryptWith(protectorBob, keyBob) + .verifyWith(certificateAlice) + .ignoreMissingPublicKeys() + .build(); + + ByteArrayOutputStream plaintext = new ByteArrayOutputStream(); + + Streams.pipeAll(decryptor, plaintext); + decryptor.close(); + + // Check the metadata to see how the message was encrypted/signed + OpenPgpMetadata metadata = decryptor.getResult(); + assertTrue(metadata.isEncrypted()); + assertTrue(metadata.containsVerifiedSignatureFrom(certificateAlice)); + assertEquals(message, plaintext.toString()); + } + + /** + * This example demonstrates how to encrypt and decrypt a message using a passphrase. + * This method can be combined with public key based encryption and signing. + * + * @throws PGPException + * @throws IOException + */ + @Test + public void encryptUsingPassphrase() throws PGPException, IOException { + String message = "Hello, World!"; + ByteArrayOutputStream ciphertext = new ByteArrayOutputStream(); + // Encrypt + EncryptionStream encryptor = PGPainless.encryptAndOrSign() + .onOutputStream(ciphertext) + .withOptions(ProducerOptions + .encrypt(EncryptionOptions.encryptCommunications() + .addPassphrase(Passphrase.fromPassword("p4ssphr4s3")) + ).setAsciiArmor(true) + ); + + Streams.pipeAll(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)), encryptor); + encryptor.close(); + + String asciiCiphertext = ciphertext.toString(); + + // Decrypt + DecryptionStream decryptor = PGPainless.decryptAndOrVerify() + .onInputStream(new ByteArrayInputStream(asciiCiphertext.getBytes(StandardCharsets.UTF_8))) + .decryptWith(Passphrase.fromPassword("p4ssphr4s3")) + .doNotVerify() + .build(); + + ByteArrayOutputStream plaintext = new ByteArrayOutputStream(); + Streams.pipeAll(decryptor, plaintext); + + decryptor.close(); + + assertEquals(message, plaintext.toString()); + } +}