1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-12-17 09:41:08 +01:00

Add support for creating cleartext signed messages and add tests

This commit is contained in:
Paul Schaub 2021-09-27 17:10:00 +02:00
parent ece5897bae
commit 526dc0caac
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
6 changed files with 453 additions and 10 deletions

View file

@ -153,6 +153,11 @@ public final class EncryptionStream extends OutputStream {
}
private void prepareLiteralDataProcessing() throws IOException {
if (options.isCleartextSigned()) {
SigningOptions.SigningMethod firstMethod = options.getSigningOptions().getSigningMethods().values().iterator().next();
armorOutputStream.beginClearText(firstMethod.getHashAlgorithm().getAlgorithmId());
return;
}
literalDataGenerator = new PGPLiteralDataGenerator();
literalDataStream = literalDataGenerator.open(outermostStream,
options.getEncoding().getCode(),
@ -214,9 +219,22 @@ public final class EncryptionStream extends OutputStream {
}
// Literal Data
literalDataStream.flush();
literalDataStream.close();
literalDataGenerator.close();
if (literalDataStream != null) {
literalDataStream.flush();
literalDataStream.close();
}
if (literalDataGenerator != null) {
literalDataGenerator.close();
}
if (options.isCleartextSigned()) {
// Add linebreak between body and signatures
// TODO: We should only add this line if required.
// I.e. if the message already ends with \n, don't add another linebreak.
armorOutputStream.write('\r');
armorOutputStream.write('\n');
armorOutputStream.endClearText();
}
try {
writeSignatures();
@ -260,7 +278,8 @@ public final class EncryptionStream extends OutputStream {
PGPSignature signature = signatureGenerator.generate();
if (signingMethod.isDetached()) {
resultBuilder.addDetachedSignature(signingKey, signature);
} else {
}
if (!signingMethod.isDetached() || options.isCleartextSigned()) {
signature.encode(outermostStream);
}
}

View file

@ -31,6 +31,7 @@ public final class ProducerOptions {
private String fileName = "";
private Date modificationDate = PGPLiteralData.NOW;
private StreamEncoding streamEncoding = StreamEncoding.BINARY;
private boolean cleartextSigned = false;
private CompressionAlgorithm compressionAlgorithmOverride = PGPainless.getPolicy().getCompressionAlgorithmPolicy()
.defaultCompressionAlgorithm();
@ -101,6 +102,9 @@ public final class ProducerOptions {
* @return builder
*/
public ProducerOptions setAsciiArmor(boolean asciiArmor) {
if (cleartextSigned && !asciiArmor) {
throw new IllegalArgumentException("Cleartext signing is enabled. Cannot disable ASCII armoring.");
}
this.asciiArmor = asciiArmor;
return this;
}
@ -114,6 +118,28 @@ public final class ProducerOptions {
return asciiArmor;
}
public ProducerOptions setCleartextSigned() {
if (signingOptions == null) {
throw new IllegalArgumentException("Signing Options cannot be null if cleartext signing is enabled.");
}
if (encryptionOptions != null) {
throw new IllegalArgumentException("Cannot encode encrypted message as Cleartext Signed.");
}
for (SigningOptions.SigningMethod method : signingOptions.getSigningMethods().values()) {
if (!method.isDetached()) {
throw new IllegalArgumentException("For cleartext signed message, all signatures must be added as detached signatures.");
}
}
cleartextSigned = true;
asciiArmor = true;
compressionAlgorithmOverride = CompressionAlgorithm.UNCOMPRESSED;
return this;
}
public boolean isCleartextSigned() {
return cleartextSigned;
}
/**
* Set the name of the encrypted file.
* Note: This option cannot be used simultaneously with {@link #setForYourEyesOnly()}.

View file

@ -52,10 +52,12 @@ public final class SigningOptions {
public static final class SigningMethod {
private final PGPSignatureGenerator signatureGenerator;
private final boolean detached;
private final HashAlgorithm hashAlgorithm;
private SigningMethod(PGPSignatureGenerator signatureGenerator, boolean detached) {
private SigningMethod(PGPSignatureGenerator signatureGenerator, boolean detached, HashAlgorithm hashAlgorithm) {
this.signatureGenerator = signatureGenerator;
this.detached = detached;
this.hashAlgorithm = hashAlgorithm;
}
/**
@ -65,8 +67,8 @@ public final class SigningOptions {
* @param signatureGenerator signature generator
* @return inline signing method
*/
public static SigningMethod inlineSignature(PGPSignatureGenerator signatureGenerator) {
return new SigningMethod(signatureGenerator, false);
public static SigningMethod inlineSignature(PGPSignatureGenerator signatureGenerator, HashAlgorithm hashAlgorithm) {
return new SigningMethod(signatureGenerator, false, hashAlgorithm);
}
/**
@ -77,8 +79,8 @@ public final class SigningOptions {
* @param signatureGenerator signature generator
* @return detached signing method
*/
public static SigningMethod detachedSignature(PGPSignatureGenerator signatureGenerator) {
return new SigningMethod(signatureGenerator, true);
public static SigningMethod detachedSignature(PGPSignatureGenerator signatureGenerator, HashAlgorithm hashAlgorithm) {
return new SigningMethod(signatureGenerator, true, hashAlgorithm);
}
public boolean isDetached() {
@ -88,6 +90,10 @@ public final class SigningOptions {
public PGPSignatureGenerator getSignatureGenerator() {
return signatureGenerator;
}
public HashAlgorithm getHashAlgorithm() {
return hashAlgorithm;
}
}
private final Map<SubkeyIdentifier, SigningMethod> signingMethods = new HashMap<>();
@ -266,7 +272,9 @@ public final class SigningOptions {
PGPSecretKey signingSecretKey = secretKey.getSecretKey(signingSubkey.getKeyID());
PGPSignatureGenerator generator = createSignatureGenerator(signingSubkey, hashAlgorithm, signatureType);
generator.setUnhashedSubpackets(unhashedSubpackets(signingSecretKey).generate());
SigningMethod signingMethod = detached ? SigningMethod.detachedSignature(generator) : SigningMethod.inlineSignature(generator);
SigningMethod signingMethod = detached ?
SigningMethod.detachedSignature(generator, hashAlgorithm) :
SigningMethod.inlineSignature(generator, hashAlgorithm);
signingMethods.put(signingKeyIdentifier, signingMethod);
}