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:
parent
ece5897bae
commit
526dc0caac
6 changed files with 453 additions and 10 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()}.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue