Skip to content

Commit

Permalink
Added a builder method to sign/encrypt as a text document rather than…
Browse files Browse the repository at this point in the history
… binary data (#60).
  • Loading branch information
bjansen committed May 4, 2021
1 parent 3523ca7 commit dca7a81
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public final class BuildEncryptionOutputStreamAPI {
private String signWith;
private Set<PGPPublicKey> recipients;
private boolean armorOutput;
private boolean textMode;

// Signature

Expand Down Expand Up @@ -99,6 +100,11 @@ OutputStream andWriteTo(OutputStream sinkForEncryptedData)
throws PGPException, SignatureException, NoSuchAlgorithmException, NoSuchProviderException, IOException;
}

public interface BuildWithTextMode extends Build {

Build textMode();
}


public interface WithAlgorithmSuite {

Expand Down Expand Up @@ -222,14 +228,14 @@ interface Armor {
*
* @return next step
*/
Build binaryOutput();
BuildWithTextMode binaryOutput();

/**
* Ascii armor the output, e.g. for usage in text protocols.
*
* @return next step
*/
Build armorAsciiOutput();
BuildWithTextMode armorAsciiOutput();
}
}
}
Expand Down Expand Up @@ -486,21 +492,21 @@ public Armor andDoNotSign() {
public final class ArmorImpl implements Armor {

@Override
public Build binaryOutput() {
public BuildWithTextMode binaryOutput() {
BuildEncryptionOutputStreamAPI.this.armorOutput = false;
LOGGER.trace("binary output");
return new Builder();
return new BuilderWithTextMode();
}

@Override
public Build armorAsciiOutput() {
public BuildWithTextMode armorAsciiOutput() {
BuildEncryptionOutputStreamAPI.this.armorOutput = true;
LOGGER.trace("ascii armor output");
return new Builder();
return new BuilderWithTextMode();
}


public final class Builder implements Build {
public class Builder implements Build {

@Override
public OutputStream andWriteTo(OutputStream sinkForEncryptedData)
Expand All @@ -513,8 +519,19 @@ public OutputStream andWriteTo(OutputStream sinkForEncryptedData)
BuildEncryptionOutputStreamAPI.this.sinkForEncryptedData,
getKeySelectionStrategy(),
BuildEncryptionOutputStreamAPI.this.armorOutput,
BuildEncryptionOutputStreamAPI.this.recipients);
BuildEncryptionOutputStreamAPI.this.recipients,
BuildEncryptionOutputStreamAPI.this.textMode);

}
}

public final class BuilderWithTextMode extends Builder implements BuildWithTextMode {

@Override
public Build textMode() {
BuildEncryptionOutputStreamAPI.this.textMode = true;
LOGGER.trace("text mode");
return this;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ private PGPEncryptingStream(final KeyringConfig config, final PGPAlgorithmSuite
* @param keySelectionStrategy selection strategy
* @param armor armor the file (true) or use binary.
* @param encryptTo encrypt to
* @param textMode simulates GnuPG's {@code --textmode} flag
*
* @return stream where plaintext gets written into
*
Expand All @@ -93,7 +94,8 @@ public static OutputStream create(final KeyringConfig config,
final OutputStream cipherTextSink,
final KeySelectionStrategy keySelectionStrategy,
final boolean armor,
final Set<PGPPublicKey> encryptTo)
final Set<PGPPublicKey> encryptTo,
final boolean textMode)
throws IOException, PGPException, NoSuchAlgorithmException, NoSuchProviderException {

requireNonNull(config, "callback must not be null");
Expand All @@ -109,7 +111,7 @@ public static OutputStream create(final KeyringConfig config,
}

final PGPEncryptingStream encryptingStream = new PGPEncryptingStream(config, algorithmSuite);
encryptingStream.setup(cipherTextSink, signingUid, encryptTo, keySelectionStrategy, armor);
encryptingStream.setup(cipherTextSink, signingUid, encryptTo, keySelectionStrategy, armor, textMode);
return encryptingStream;
}

Expand All @@ -122,6 +124,7 @@ public static OutputStream create(final KeyringConfig config,
* @param pubEncKeys the pub enc keys
* @param keySelectionStrategy key selection strategy (for signatures)
* @param armor if OutputStream should be "armored", that means base64 encoded
* @param textMode simulates GnuPG's {@code --textmode} flag
*
* @throws IOException Signals that an I/O exception has occurred.
* @throws PGPException the pGP exception
Expand All @@ -134,7 +137,8 @@ private void setup(final OutputStream cipherTextSink,
@Nullable final String signingUid,
final Set<PGPPublicKey> pubEncKeys,
final KeySelectionStrategy keySelectionStrategy,
final boolean armor) throws
final boolean armor,
final boolean textMode) throws
IOException, PGPException {
isDoSign = signingUid != null;

Expand Down Expand Up @@ -186,7 +190,7 @@ private void setup(final OutputStream cipherTextSink,
new BcPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(),
algorithmSuite.getHashAlgorithmCode().getAlgorithmId()));

signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);
signatureGenerator.init(textMode ? PGPSignature.CANONICAL_TEXT_DOCUMENT : PGPSignature.BINARY_DOCUMENT, pgpPrivKey);

final Iterator<?> userIDs = pgpSec.getPublicKey().getUserIDs();
if (userIDs.hasNext()) {
Expand All @@ -208,7 +212,7 @@ private void setup(final OutputStream cipherTextSink,

encryptionDataStreamGenerator = new PGPLiteralDataGenerator();
encryptionDataStream = encryptionDataStreamGenerator
.open(compressionStream, PGPLiteralData.BINARY, "", new Date(), new byte[1 << 16]);
.open(compressionStream, textMode ? PGPLiteralData.TEXT : PGPLiteralData.BINARY, "", new Date(), new byte[1 << 16]);
}

@Override
Expand Down Expand Up @@ -268,4 +272,4 @@ public void close() throws IOException {
isClosed = true;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ void encryptAndSign(final InputStream in, OutputStream out, final Set<PGPPublicK

try (final OutputStream encryptionStream = PGPEncryptingStream
.create(config, algorithmSuite, signatureUid, out, keySelectionStrategy, armor,
pubEncKeys)) {
pubEncKeys, false)) {
Streams.pipeAll(in, encryptionStream);
encryptionStream.flush();
}
out.flush();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
Expand Down Expand Up @@ -127,6 +126,41 @@ public void encryptAndSignBinary_thenDecryptAndVerify_yieldsOriginalPlaintext()
assertArrayEquals(expectedPlaintext, decryptedPlaintext);
}

@Test
public void encryptAndSignTextModeBinary_thenDecryptAndVerify_yieldsOriginalPlaintext()
throws IOException, PGPException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException {
final byte[] expectedPlaintext = ExampleMessages.IMPORTANT_QUOTE_TEXT.getBytes("US-ASCII");

ByteArrayOutputStream cipherText = new ByteArrayOutputStream();

final OutputStream encryptionStream = BouncyGPG
.encryptToStream()
.withConfig(Configs.keyringConfigFromFilesForSender())
.withAlgorithms(algorithmSuite)
.toRecipient("[email protected]")
.andSignWith("[email protected]")
.binaryOutput()
.textMode()
.andWriteTo(cipherText);

encryptionStream.write(expectedPlaintext);
encryptionStream.close();
cipherText.close();

ByteArrayInputStream cipherTextAsSource = new ByteArrayInputStream(cipherText.toByteArray());

// Decrypt
final InputStream decryptedPlaintextStream = BouncyGPG
.decryptAndVerifyStream()
.withConfig(Configs.keyringConfigFromResourceForRecipient())
.andRequireSignatureFromAllKeys("[email protected]")
.fromEncryptedInputStream(cipherTextAsSource);

final byte[] decryptedPlaintext = Streams.readAll(decryptedPlaintextStream);

assertArrayEquals(expectedPlaintext, decryptedPlaintext);
}


@Test
public void encryptAndSignBinaryWithSHA256_AES256_thenDecryptAndVerify_yieldsOriginalPlaintext()
Expand Down

0 comments on commit dca7a81

Please sign in to comment.