mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-12-10 14:21:09 +01:00
Fix decryption of signed messages created with PGPainless < 0.2.10
This commit is contained in:
parent
2b2639bde7
commit
963a8170da
3 changed files with 200 additions and 46 deletions
|
|
@ -34,7 +34,6 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
|||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||
import org.bouncycastle.openpgp.PGPUtil;
|
||||
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
|
||||
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
|
||||
|
|
@ -150,13 +149,13 @@ public final class DecryptionStreamFactory {
|
|||
// to allow for detached signature verification.
|
||||
LOGGER.debug("The message appears to not be an OpenPGP message. This is probably data signed with detached signatures?");
|
||||
bufferedIn.reset();
|
||||
inputStream = wrapInVerifySignatureStream(bufferedIn);
|
||||
inputStream = wrapInVerifySignatureStream(bufferedIn, objectFactory);
|
||||
} catch (IOException e) {
|
||||
if (e.getMessage().contains("invalid armor") || e.getMessage().contains("invalid header encountered")) {
|
||||
// We falsely assumed the data to be armored.
|
||||
LOGGER.debug("The message is apparently not armored.");
|
||||
bufferedIn.reset();
|
||||
inputStream = wrapInVerifySignatureStream(bufferedIn);
|
||||
inputStream = wrapInVerifySignatureStream(bufferedIn, objectFactory);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
|
@ -166,10 +165,10 @@ public final class DecryptionStreamFactory {
|
|||
(decoderStream instanceof ArmoredInputStream) ? decoderStream : null);
|
||||
}
|
||||
|
||||
private InputStream wrapInVerifySignatureStream(InputStream bufferedIn) {
|
||||
private InputStream wrapInVerifySignatureStream(InputStream bufferedIn, PGPObjectFactory objectFactory) {
|
||||
return new SignatureInputStream.VerifySignatures(
|
||||
bufferedIn, onePassSignatureChecks,
|
||||
detachedSignatureChecks, options,
|
||||
bufferedIn, objectFactory, onePassSignatureChecks,
|
||||
onePassSignaturesWithMissingCert, detachedSignatureChecks, options,
|
||||
resultBuilder);
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +221,7 @@ public final class DecryptionStreamFactory {
|
|||
throws PGPException, IOException {
|
||||
LOGGER.debug("Depth {}: Encountered PGPOnePassSignatureList of size {}", depth, onePassSignatures.size());
|
||||
initOnePassSignatures(onePassSignatures);
|
||||
return processPGPPackets(objectFactory, ++depth);
|
||||
return processPGPPackets(objectFactory, depth);
|
||||
}
|
||||
|
||||
private InputStream processPGPLiteralData(@Nonnull PGPObjectFactory objectFactory, PGPLiteralData pgpLiteralData, int depth) throws IOException {
|
||||
|
|
@ -238,48 +237,11 @@ public final class DecryptionStreamFactory {
|
|||
return literalDataInputStream;
|
||||
}
|
||||
|
||||
// Parse signatures from message
|
||||
PGPSignatureList signatures = parseSignatures(objectFactory);
|
||||
List<PGPSignature> signatureList = SignatureUtils.toList(signatures);
|
||||
// Set signatures as comparison sigs in OPS checks
|
||||
for (int i = 0; i < onePassSignatureChecks.size(); i++) {
|
||||
int reversedIndex = onePassSignatureChecks.size() - i - 1;
|
||||
onePassSignatureChecks.get(i).setSignature(signatureList.get(reversedIndex));
|
||||
}
|
||||
|
||||
for (PGPSignature signature : signatureList) {
|
||||
if (onePassSignaturesWithMissingCert.containsKey(signature.getKeyID())) {
|
||||
OnePassSignatureCheck check = onePassSignaturesWithMissingCert.remove(signature.getKeyID());
|
||||
check.setSignature(signature);
|
||||
|
||||
resultBuilder.addInvalidInbandSignature(new SignatureVerification(signature, null),
|
||||
new SignatureValidationException("Missing verification certificate " + Long.toHexString(signature.getKeyID())));
|
||||
}
|
||||
}
|
||||
|
||||
return new SignatureInputStream.VerifySignatures(literalDataInputStream,
|
||||
onePassSignatureChecks, detachedSignatureChecks, options, resultBuilder) {
|
||||
return new SignatureInputStream.VerifySignatures(literalDataInputStream, objectFactory,
|
||||
onePassSignatureChecks, onePassSignaturesWithMissingCert, detachedSignatureChecks, options, resultBuilder) {
|
||||
};
|
||||
}
|
||||
|
||||
private PGPSignatureList parseSignatures(PGPObjectFactory objectFactory) throws IOException {
|
||||
PGPSignatureList signatureList = null;
|
||||
Object pgpObject = objectFactory.nextObject();
|
||||
while (pgpObject != null && signatureList == null) {
|
||||
if (pgpObject instanceof PGPSignatureList) {
|
||||
signatureList = (PGPSignatureList) pgpObject;
|
||||
} else {
|
||||
pgpObject = objectFactory.nextObject();
|
||||
}
|
||||
}
|
||||
|
||||
if (signatureList == null || signatureList.isEmpty()) {
|
||||
throw new IOException("Verification failed - No Signatures found");
|
||||
}
|
||||
|
||||
return signatureList;
|
||||
}
|
||||
|
||||
private InputStream decryptSessionKey(@Nonnull PGPEncryptedDataList encryptedDataList)
|
||||
throws PGPException {
|
||||
Iterator<PGPEncryptedData> encryptedDataIterator = encryptedDataList.getEncryptedDataObjects();
|
||||
|
|
|
|||
|
|
@ -10,15 +10,20 @@ import java.io.FilterInputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.exception.SignatureValidationException;
|
||||
import org.pgpainless.policy.Policy;
|
||||
import org.pgpainless.signature.CertificateValidator;
|
||||
import org.pgpainless.signature.DetachedSignatureCheck;
|
||||
import org.pgpainless.signature.OnePassSignatureCheck;
|
||||
import org.pgpainless.signature.SignatureUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
|
@ -32,19 +37,25 @@ public abstract class SignatureInputStream extends FilterInputStream {
|
|||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(VerifySignatures.class);
|
||||
|
||||
private final PGPObjectFactory objectFactory;
|
||||
private final List<OnePassSignatureCheck> opSignatures;
|
||||
private final Map<Long, OnePassSignatureCheck> opSignaturesWithMissingCert;
|
||||
private final List<DetachedSignatureCheck> detachedSignatures;
|
||||
private final ConsumerOptions options;
|
||||
private final OpenPgpMetadata.Builder resultBuilder;
|
||||
|
||||
public VerifySignatures(
|
||||
InputStream literalDataStream,
|
||||
PGPObjectFactory objectFactory,
|
||||
List<OnePassSignatureCheck> opSignatures,
|
||||
Map<Long, OnePassSignatureCheck> onePassSignaturesWithMissingCert,
|
||||
List<DetachedSignatureCheck> detachedSignatures,
|
||||
ConsumerOptions options,
|
||||
OpenPgpMetadata.Builder resultBuilder) {
|
||||
super(literalDataStream);
|
||||
this.objectFactory = objectFactory;
|
||||
this.opSignatures = opSignatures;
|
||||
this.opSignaturesWithMissingCert = onePassSignaturesWithMissingCert;
|
||||
this.detachedSignatures = detachedSignatures;
|
||||
this.options = options;
|
||||
this.resultBuilder = resultBuilder;
|
||||
|
|
@ -71,6 +82,7 @@ public abstract class SignatureInputStream extends FilterInputStream {
|
|||
|
||||
final boolean endOfStream = read == -1;
|
||||
if (endOfStream) {
|
||||
parseAndCombineSignatures();
|
||||
verifyOnePassSignatures();
|
||||
verifyDetachedSignatures();
|
||||
} else {
|
||||
|
|
@ -80,6 +92,51 @@ public abstract class SignatureInputStream extends FilterInputStream {
|
|||
return read;
|
||||
}
|
||||
|
||||
public void parseAndCombineSignatures() throws IOException {
|
||||
// Parse signatures from message
|
||||
PGPSignatureList signatures;
|
||||
try {
|
||||
signatures = parseSignatures(objectFactory);
|
||||
} catch (IOException e) {
|
||||
return;
|
||||
}
|
||||
List<PGPSignature> signatureList = SignatureUtils.toList(signatures);
|
||||
// Set signatures as comparison sigs in OPS checks
|
||||
for (int i = 0; i < opSignatures.size(); i++) {
|
||||
int reversedIndex = opSignatures.size() - i - 1;
|
||||
opSignatures.get(i).setSignature(signatureList.get(reversedIndex));
|
||||
}
|
||||
|
||||
for (PGPSignature signature : signatureList) {
|
||||
if (opSignaturesWithMissingCert.containsKey(signature.getKeyID())) {
|
||||
OnePassSignatureCheck check = opSignaturesWithMissingCert.remove(signature.getKeyID());
|
||||
check.setSignature(signature);
|
||||
|
||||
resultBuilder.addInvalidInbandSignature(new SignatureVerification(signature, null),
|
||||
new SignatureValidationException("Missing verification certificate " + Long.toHexString(signature.getKeyID())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PGPSignatureList parseSignatures(PGPObjectFactory objectFactory) throws IOException {
|
||||
PGPSignatureList signatureList = null;
|
||||
Object pgpObject = objectFactory.nextObject();
|
||||
while (pgpObject != null && signatureList == null) {
|
||||
if (pgpObject instanceof PGPSignatureList) {
|
||||
signatureList = (PGPSignatureList) pgpObject;
|
||||
} else {
|
||||
pgpObject = objectFactory.nextObject();
|
||||
}
|
||||
}
|
||||
|
||||
if (signatureList == null || signatureList.isEmpty()) {
|
||||
throw new IOException("Verification failed - No Signatures found");
|
||||
}
|
||||
|
||||
return signatureList;
|
||||
}
|
||||
|
||||
|
||||
private synchronized void verifyOnePassSignatures() {
|
||||
Policy policy = PGPainless.getPolicy();
|
||||
for (OnePassSignatureCheck opSignature : opSignatures) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue