Add new ProducerOption setComment() for Ascii armored EncryptionStreams. (#254)

* Add new ProducerOption setComment() for Ascii armored EncryptionStreams.
This commit is contained in:
feri 2022-02-24 00:51:16 +01:00 committed by GitHub
parent 1753cef10e
commit 928fa12b51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 106 additions and 0 deletions

View File

@ -23,6 +23,7 @@ import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.SubkeyIdentifier;
import org.pgpainless.util.ArmorUtils;
import org.pgpainless.util.ArmoredOutputStreamFactory;
import org.pgpainless.util.StreamGeneratorWrapper;
import org.slf4j.Logger;
@ -74,6 +75,9 @@ public final class EncryptionStream extends OutputStream {
LOGGER.debug("Wrap encryption output in ASCII armor");
armorOutputStream = ArmoredOutputStreamFactory.get(outermostStream);
if (options.hasComment()) {
ArmorUtils.addCommentHeader(armorOutputStream, options.getComment());
}
outermostStream = armorOutputStream;
}

View File

@ -25,6 +25,7 @@ public final class ProducerOptions {
private CompressionAlgorithm compressionAlgorithmOverride = PGPainless.getPolicy().getCompressionAlgorithmPolicy()
.defaultCompressionAlgorithm();
private boolean asciiArmor = true;
private String comment = null;
private ProducerOptions(EncryptionOptions encryptionOptions, SigningOptions signingOptions) {
this.encryptionOptions = encryptionOptions;
@ -107,6 +108,40 @@ public final class ProducerOptions {
return asciiArmor;
}
/**
* set the comment for header in ascii armored output.
* The default value is null, which means no comment header is added.
* Multiline comments are possible using '\\n'.
*
* @param comment comment header text
* @return builder
*/
public ProducerOptions setComment(String comment) {
if (!asciiArmor) {
throw new IllegalArgumentException("Comment can only be set when ASCII armoring is enabled.");
}
this.comment = comment;
return this;
}
/**
* Return comment set for header in ascii armored output.
*
* @return comment
*/
public String getComment() {
return comment;
}
/**
* Return whether a comment was set (!= null).
*
* @return comment
*/
public boolean hasComment() {
return comment != null;
}
public ProducerOptions setCleartextSigned() {
if (signingOptions == null) {
throw new IllegalArgumentException("Signing Options cannot be null if cleartext signing is enabled.");

View File

@ -129,4 +129,71 @@ public class Encrypt {
assertEquals(message, plaintext.toString());
}
/**
* In this example, Alice is sending a signed and encrypted message to Bob.
* She encrypts the message to both bobs certificate and her own.
* A comment header with the text "This comment was added using options." is added
* using the fluent ProducerOption syntax.
*
* Bob subsequently decrypts the message using his key.
*/
@Test
public void encryptWithCommentHeader() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
// Prepare keys
PGPSecretKeyRing keyAlice = PGPainless.generateKeyRing()
.modernKeyRing("alice@pgpainless.org", null);
PGPPublicKeyRing certificateAlice = KeyRingUtils.publicKeyRingFrom(keyAlice);
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";
String comment = "This comment was added using options.";
ByteArrayOutputStream ciphertext = new ByteArrayOutputStream();
// Encrypt and sign
EncryptionStream encryptor = PGPainless.encryptAndOrSign()
.onOutputStream(ciphertext)
.withOptions(ProducerOptions.encrypt(
// we want to encrypt communication (affects key selection based on key flags)
EncryptionOptions.encryptCommunications()
.addRecipient(certificateBob)
.addRecipient(certificateAlice)
).setAsciiArmor(true)
.setComment(comment)
);
// Pipe data trough and CLOSE the stream (important)
Streams.pipeAll(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)), encryptor);
encryptor.close();
String encryptedMessage = ciphertext.toString();
// check that comment header was added after "BEGIN PGP" and "Version:"
assertEquals(encryptedMessage.split("\n")[2].trim(), "Comment: " + comment);
// also test, that decryption still works...
// Decrypt and verify signatures
DecryptionStream decryptor = PGPainless.decryptAndOrVerify()
.onInputStream(new ByteArrayInputStream(encryptedMessage.getBytes(StandardCharsets.UTF_8)))
.withOptions(new ConsumerOptions()
.addDecryptionKey(keyBob, protectorBob)
.addVerificationCert(certificateAlice)
);
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());
assertEquals(message, plaintext.toString());
}
}