mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-26 21:07:58 +01:00
Move signature verification to dedicated streams
This commit is contained in:
parent
ba0e5eb3fe
commit
90a00e0541
12 changed files with 374 additions and 325 deletions
|
@ -15,21 +15,12 @@
|
|||
*/
|
||||
package org.pgpainless.decryption_verification;
|
||||
|
||||
import static org.pgpainless.signature.SignatureValidator.signatureWasCreatedInBounds;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.util.io.Streams;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.exception.SignatureValidationException;
|
||||
import org.pgpainless.signature.CertificateValidator;
|
||||
import org.pgpainless.signature.DetachedSignature;
|
||||
import org.pgpainless.util.IntegrityProtectedInputStream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Decryption Stream that handles updating and verification of detached signatures,
|
||||
|
@ -37,10 +28,7 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
public class DecryptionStream extends InputStream {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DecryptionStream.class);
|
||||
|
||||
private final InputStream inputStream;
|
||||
private final ConsumerOptions options;
|
||||
private final OpenPgpMetadata.Builder resultBuilder;
|
||||
private boolean isClosed = false;
|
||||
private final IntegrityProtectedInputStream integrityProtectedInputStream;
|
||||
|
@ -50,17 +38,15 @@ public class DecryptionStream extends InputStream {
|
|||
* Create an input stream that handles decryption and - if necessary - integrity protection verification.
|
||||
*
|
||||
* @param wrapped underlying input stream
|
||||
* @param options options for consuming, eg. decryption key...
|
||||
* @param resultBuilder builder for decryption metadata like algorithms, recipients etc.
|
||||
* @param integrityProtectedInputStream in case of data encrypted using SEIP packet close this stream to check integrity
|
||||
* @param armorStream armor stream to verify CRC checksums
|
||||
*/
|
||||
DecryptionStream(@Nonnull InputStream wrapped, @Nonnull ConsumerOptions options,
|
||||
DecryptionStream(@Nonnull InputStream wrapped,
|
||||
@Nonnull OpenPgpMetadata.Builder resultBuilder,
|
||||
IntegrityProtectedInputStream integrityProtectedInputStream,
|
||||
InputStream armorStream) {
|
||||
this.inputStream = wrapped;
|
||||
this.options = options;
|
||||
this.resultBuilder = resultBuilder;
|
||||
this.integrityProtectedInputStream = integrityProtectedInputStream;
|
||||
this.armorStream = armorStream;
|
||||
|
@ -69,58 +55,27 @@ public class DecryptionStream extends InputStream {
|
|||
@Override
|
||||
public int read() throws IOException {
|
||||
int r = inputStream.read();
|
||||
maybeUpdateDetachedSignatures(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@Nonnull byte[] bytes, int offset, int length) throws IOException {
|
||||
int read = inputStream.read(bytes, offset, length);
|
||||
if (read != -1) {
|
||||
maybeUpdateDetachedSignatures(bytes, offset, read);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
private void maybeUpdateDetachedSignatures(byte[] bytes, int offset, int length) {
|
||||
for (DetachedSignature s : resultBuilder.getDetachedSignatures()) {
|
||||
s.getSignature().update(bytes, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeUpdateDetachedSignatures(int rByte) {
|
||||
for (DetachedSignature s : resultBuilder.getDetachedSignatures()) {
|
||||
if (rByte != -1) {
|
||||
s.getSignature().update((byte) rByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (armorStream != null) {
|
||||
Streams.drain(armorStream);
|
||||
}
|
||||
inputStream.close();
|
||||
maybeVerifyDetachedSignatures();
|
||||
if (integrityProtectedInputStream != null) {
|
||||
integrityProtectedInputStream.close();
|
||||
}
|
||||
this.isClosed = true;
|
||||
}
|
||||
|
||||
private void maybeVerifyDetachedSignatures() {
|
||||
for (DetachedSignature s : resultBuilder.getDetachedSignatures()) {
|
||||
try {
|
||||
signatureWasCreatedInBounds(options.getVerifyNotBefore(), options.getVerifyNotAfter()).verify(s.getSignature());
|
||||
boolean verified = CertificateValidator.validateCertificateAndVerifyInitializedSignature(s.getSignature(), (PGPPublicKeyRing) s.getSigningKeyRing(), PGPainless.getPolicy());
|
||||
s.setVerified(verified);
|
||||
} catch (SignatureValidationException e) {
|
||||
LOGGER.warn("Could not verify signature of key {}", s.getSigningKeyIdentifier(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the result of the decryption.
|
||||
* The result contains metadata about the decryption, such as signatures, used keys and algorithms, as well as information
|
||||
|
|
|
@ -19,10 +19,9 @@ import java.io.BufferedInputStream;
|
|||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
|
@ -43,6 +42,7 @@ 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;
|
||||
|
@ -59,12 +59,11 @@ import org.pgpainless.exception.MissingLiteralDataException;
|
|||
import org.pgpainless.exception.UnacceptableAlgorithmException;
|
||||
import org.pgpainless.exception.WrongConsumingMethodException;
|
||||
import org.pgpainless.implementation.ImplementationFactory;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.SubkeyIdentifier;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||
import org.pgpainless.signature.DetachedSignature;
|
||||
import org.pgpainless.signature.OnePassSignature;
|
||||
import org.pgpainless.signature.OnePassSignatureCheck;
|
||||
import org.pgpainless.signature.SignatureUtils;
|
||||
import org.pgpainless.util.CRCingArmoredInputStreamWrapper;
|
||||
import org.pgpainless.util.IntegrityProtectedInputStream;
|
||||
|
@ -78,15 +77,17 @@ public final class DecryptionStreamFactory {
|
|||
private static final int MAX_RECURSION_DEPTH = 16;
|
||||
|
||||
private final ConsumerOptions options;
|
||||
|
||||
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
||||
private final List<OnePassSignatureCheck> onePassSignatureChecks = new ArrayList<>();
|
||||
private final List<DetachedSignature> detachedSignatureChecks = new ArrayList<>();
|
||||
|
||||
private static final PGPContentVerifierBuilderProvider verifierBuilderProvider =
|
||||
ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider();
|
||||
private static final KeyFingerPrintCalculator keyFingerprintCalculator =
|
||||
ImplementationFactory.getInstance().getKeyFingerprintCalculator();
|
||||
private final Map<OpenPgpV4Fingerprint, OnePassSignature> verifiableOnePassSignatures = new HashMap<>();
|
||||
private IntegrityProtectedInputStream integrityProtectedEncryptedInputStream;
|
||||
|
||||
|
||||
public static DecryptionStream create(@Nonnull InputStream inputStream,
|
||||
@Nonnull ConsumerOptions options)
|
||||
throws PGPException, IOException {
|
||||
|
@ -109,9 +110,9 @@ public final class DecryptionStreamFactory {
|
|||
PGPPublicKey signingKey = signingKeyRing.getPublicKey(issuerKeyId);
|
||||
SubkeyIdentifier signingKeyIdentifier = new SubkeyIdentifier(signingKeyRing, signingKey.getKeyID());
|
||||
try {
|
||||
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signingKey);
|
||||
resultBuilder.addDetachedSignature(
|
||||
new DetachedSignature(signature, signingKeyRing, signingKeyIdentifier));
|
||||
signature.init(verifierBuilderProvider, signingKey);
|
||||
DetachedSignature detachedSignature = new DetachedSignature(signature, signingKeyRing, signingKeyIdentifier);
|
||||
detachedSignatureChecks.add(detachedSignature);
|
||||
} catch (PGPException e) {
|
||||
LOGGER.warn("Cannot verify detached signature made by {}. Reason: {}", signingKeyIdentifier, e.getMessage(), e);
|
||||
}
|
||||
|
@ -142,29 +143,35 @@ public final class DecryptionStreamFactory {
|
|||
inputStream = processPGPPackets(objectFactory, 1);
|
||||
} catch (EOFException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (MissingLiteralDataException e) {
|
||||
} catch (MissingLiteralDataException e) {
|
||||
// Not an OpenPGP message.
|
||||
// Reset the buffered stream to parse the message as arbitrary binary data
|
||||
// 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 = bufferedIn;
|
||||
inputStream = wrapInVerifySignatureStream(bufferedIn);
|
||||
} catch (IOException e) {
|
||||
if (e.getMessage().contains("invalid armor")) {
|
||||
// We falsely assumed the data to be armored.
|
||||
LOGGER.debug("The message is apparently not armored.");
|
||||
bufferedIn.reset();
|
||||
inputStream = bufferedIn;
|
||||
inputStream = wrapInVerifySignatureStream(bufferedIn);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return new DecryptionStream(inputStream, options, resultBuilder, integrityProtectedEncryptedInputStream,
|
||||
return new DecryptionStream(inputStream, resultBuilder, integrityProtectedEncryptedInputStream,
|
||||
(decoderStream instanceof ArmoredInputStream) ? decoderStream : null);
|
||||
}
|
||||
|
||||
private InputStream wrapInVerifySignatureStream(InputStream bufferedIn) {
|
||||
return new SignatureInputStream.VerifySignatures(
|
||||
bufferedIn, onePassSignatureChecks,
|
||||
detachedSignatureChecks, options,
|
||||
resultBuilder);
|
||||
}
|
||||
|
||||
private InputStream processPGPPackets(@Nonnull PGPObjectFactory objectFactory, int depth) throws IOException, PGPException {
|
||||
if (depth >= MAX_RECURSION_DEPTH) {
|
||||
throw new PGPException("Maximum recursion depth of packages exceeded.");
|
||||
|
@ -217,7 +224,7 @@ public final class DecryptionStreamFactory {
|
|||
return processPGPPackets(objectFactory, ++depth);
|
||||
}
|
||||
|
||||
private InputStream processPGPLiteralData(@Nonnull PGPObjectFactory objectFactory, PGPLiteralData pgpLiteralData, int depth) {
|
||||
private InputStream processPGPLiteralData(@Nonnull PGPObjectFactory objectFactory, PGPLiteralData pgpLiteralData, int depth) throws IOException {
|
||||
LOGGER.debug("Depth {}: Found PGPLiteralData", depth);
|
||||
InputStream literalDataInputStream = pgpLiteralData.getInputStream();
|
||||
|
||||
|
@ -225,13 +232,39 @@ public final class DecryptionStreamFactory {
|
|||
.setModificationDate(pgpLiteralData.getModificationTime())
|
||||
.setFileEncoding(StreamEncoding.fromCode(pgpLiteralData.getFormat()));
|
||||
|
||||
if (verifiableOnePassSignatures.isEmpty()) {
|
||||
if (onePassSignatureChecks.isEmpty()) {
|
||||
LOGGER.debug("No OnePassSignatures found -> We are done");
|
||||
return literalDataInputStream;
|
||||
}
|
||||
|
||||
return new SignatureVerifyingInputStream(literalDataInputStream,
|
||||
objectFactory, verifiableOnePassSignatures, options, resultBuilder);
|
||||
PGPSignatureList signatures = parseSignatures(objectFactory);
|
||||
List<PGPSignature> signatureList = SignatureUtils.toList(signatures);
|
||||
|
||||
for (int i = 0; i < onePassSignatureChecks.size(); i++) {
|
||||
onePassSignatureChecks.get(i).setSignature(signatureList.get(onePassSignatureChecks.size() - i - 1));
|
||||
}
|
||||
|
||||
return new SignatureInputStream.VerifySignatures(literalDataInputStream,
|
||||
onePassSignatureChecks, 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)
|
||||
|
@ -377,7 +410,7 @@ public final class DecryptionStreamFactory {
|
|||
private void processOnePassSignature(PGPOnePassSignature signature) throws PGPException {
|
||||
final long keyId = signature.getKeyID();
|
||||
|
||||
LOGGER.debug("Message contains OnePassSignature from {}", Long.toHexString(keyId));
|
||||
LOGGER.debug("Encountered OnePassSignature from {}", Long.toHexString(keyId));
|
||||
|
||||
// Find public key
|
||||
PGPPublicKeyRing verificationKeyRing = findSignatureVerificationKeyRing(keyId);
|
||||
|
@ -388,10 +421,8 @@ public final class DecryptionStreamFactory {
|
|||
PGPPublicKey verificationKey = verificationKeyRing.getPublicKey(keyId);
|
||||
|
||||
signature.init(verifierBuilderProvider, verificationKey);
|
||||
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(verificationKey);
|
||||
OnePassSignature onePassSignature = new OnePassSignature(signature, verificationKeyRing);
|
||||
resultBuilder.addOnePassSignature(onePassSignature);
|
||||
verifiableOnePassSignatures.put(fingerprint, onePassSignature);
|
||||
OnePassSignatureCheck onePassSignature = new OnePassSignatureCheck(signature, verificationKeyRing);
|
||||
onePassSignatureChecks.add(onePassSignature);
|
||||
}
|
||||
|
||||
private PGPSecretKeyRing findDecryptionKeyRing(long keyId) {
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
@ -34,17 +33,19 @@ import org.bouncycastle.openpgp.PGPSignature;
|
|||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||
import org.pgpainless.algorithm.StreamEncoding;
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||
import org.pgpainless.exception.SignatureValidationException;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.SubkeyIdentifier;
|
||||
import org.pgpainless.signature.DetachedSignature;
|
||||
import org.pgpainless.signature.OnePassSignature;
|
||||
import org.pgpainless.signature.cleartext_signatures.SignatureVerification;
|
||||
|
||||
public class OpenPgpMetadata {
|
||||
|
||||
private final Set<Long> recipientKeyIds;
|
||||
private final SubkeyIdentifier decryptionKey;
|
||||
private final List<OnePassSignature> onePassSignatures;
|
||||
private final List<DetachedSignature> detachedSignatures;
|
||||
private final List<SignatureVerification> verifiedInbandSignatures;
|
||||
private final List<SignatureVerification.Failure> invalidInbandSignatures;
|
||||
private final List<SignatureVerification> verifiedDetachedSignatures;
|
||||
private final List<SignatureVerification.Failure> invalidDetachedSignatures;
|
||||
private final SymmetricKeyAlgorithm symmetricKeyAlgorithm;
|
||||
private final CompressionAlgorithm compressionAlgorithm;
|
||||
private final String fileName;
|
||||
|
@ -55,8 +56,10 @@ public class OpenPgpMetadata {
|
|||
SubkeyIdentifier decryptionKey,
|
||||
SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||
CompressionAlgorithm algorithm,
|
||||
List<OnePassSignature> onePassSignatures,
|
||||
List<DetachedSignature> detachedSignatures,
|
||||
List<SignatureVerification> verifiedInbandSignatures,
|
||||
List<SignatureVerification.Failure> invalidInbandSignatures,
|
||||
List<SignatureVerification> verifiedDetachedSignatures,
|
||||
List<SignatureVerification.Failure> invalidDetachedSignatures,
|
||||
String fileName,
|
||||
Date modificationDate,
|
||||
StreamEncoding fileEncoding) {
|
||||
|
@ -65,8 +68,10 @@ public class OpenPgpMetadata {
|
|||
this.decryptionKey = decryptionKey;
|
||||
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
|
||||
this.compressionAlgorithm = algorithm;
|
||||
this.detachedSignatures = Collections.unmodifiableList(detachedSignatures);
|
||||
this.onePassSignatures = Collections.unmodifiableList(onePassSignatures);
|
||||
this.verifiedInbandSignatures = Collections.unmodifiableList(verifiedInbandSignatures);
|
||||
this.invalidInbandSignatures = Collections.unmodifiableList(invalidInbandSignatures);
|
||||
this.verifiedDetachedSignatures = Collections.unmodifiableList(verifiedDetachedSignatures);
|
||||
this.invalidDetachedSignatures = Collections.unmodifiableList(invalidDetachedSignatures);
|
||||
this.fileName = fileName;
|
||||
this.modificationDate = modificationDate;
|
||||
this.fileEncoding = fileEncoding;
|
||||
|
@ -128,11 +133,17 @@ public class OpenPgpMetadata {
|
|||
*/
|
||||
public @Nonnull Set<PGPSignature> getSignatures() {
|
||||
Set<PGPSignature> signatures = new HashSet<>();
|
||||
for (DetachedSignature detachedSignature : detachedSignatures) {
|
||||
signatures.add(detachedSignature.getSignature());
|
||||
for (SignatureVerification v : getVerifiedDetachedSignatures()) {
|
||||
signatures.add(v.getSignature());
|
||||
}
|
||||
for (OnePassSignature onePassSignature : onePassSignatures) {
|
||||
signatures.add(onePassSignature.getSignature());
|
||||
for (SignatureVerification v : getVerifiedInbandSignatures()) {
|
||||
signatures.add(v.getSignature());
|
||||
}
|
||||
for (SignatureVerification.Failure f : getInvalidDetachedSignatures()) {
|
||||
signatures.add(f.getSignatureVerification().getSignature());
|
||||
}
|
||||
for (SignatureVerification.Failure f : getInvalidInbandSignatures()) {
|
||||
signatures.add(f.getSignatureVerification().getSignature());
|
||||
}
|
||||
return signatures;
|
||||
}
|
||||
|
@ -158,20 +169,32 @@ public class OpenPgpMetadata {
|
|||
*/
|
||||
public Map<SubkeyIdentifier, PGPSignature> getVerifiedSignatures() {
|
||||
Map<SubkeyIdentifier, PGPSignature> verifiedSignatures = new ConcurrentHashMap<>();
|
||||
for (DetachedSignature detachedSignature : detachedSignatures) {
|
||||
if (detachedSignature.isVerified()) {
|
||||
verifiedSignatures.put(detachedSignature.getSigningKeyIdentifier(), detachedSignature.getSignature());
|
||||
}
|
||||
for (SignatureVerification detachedSignature : getVerifiedDetachedSignatures()) {
|
||||
verifiedSignatures.put(detachedSignature.getSigningKey(), detachedSignature.getSignature());
|
||||
}
|
||||
for (OnePassSignature onePassSignature : onePassSignatures) {
|
||||
if (onePassSignature.isVerified()) {
|
||||
verifiedSignatures.put(onePassSignature.getSigningKey(), onePassSignature.getSignature());
|
||||
}
|
||||
for (SignatureVerification inbandSignatures : verifiedInbandSignatures) {
|
||||
verifiedSignatures.put(inbandSignatures.getSigningKey(), inbandSignatures.getSignature());
|
||||
}
|
||||
|
||||
return verifiedSignatures;
|
||||
}
|
||||
|
||||
public List<SignatureVerification> getVerifiedInbandSignatures() {
|
||||
return verifiedInbandSignatures;
|
||||
}
|
||||
|
||||
public List<SignatureVerification> getVerifiedDetachedSignatures() {
|
||||
return verifiedDetachedSignatures;
|
||||
}
|
||||
|
||||
public List<SignatureVerification.Failure> getInvalidInbandSignatures() {
|
||||
return invalidInbandSignatures;
|
||||
}
|
||||
|
||||
public List<SignatureVerification.Failure> getInvalidDetachedSignatures() {
|
||||
return invalidDetachedSignatures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true, if the message is signed and at least one signature on the message was verified successfully.
|
||||
*
|
||||
|
@ -261,14 +284,18 @@ public class OpenPgpMetadata {
|
|||
|
||||
private final Set<Long> recipientFingerprints = new HashSet<>();
|
||||
private SubkeyIdentifier decryptionKey;
|
||||
private final List<DetachedSignature> detachedSignatures = new ArrayList<>();
|
||||
private final List<OnePassSignature> onePassSignatures = new ArrayList<>();
|
||||
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL;
|
||||
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
||||
private String fileName;
|
||||
private StreamEncoding fileEncoding;
|
||||
private Date modificationDate;
|
||||
|
||||
private final List<SignatureVerification> verifiedInbandSignatures = new ArrayList<>();
|
||||
private final List<SignatureVerification> verifiedDetachedSignatures = new ArrayList<>();
|
||||
private final List<SignatureVerification.Failure> invalidInbandSignatures = new ArrayList<>();
|
||||
private final List<SignatureVerification.Failure> invalidDetachedSignatures = new ArrayList<>();
|
||||
|
||||
|
||||
public Builder addRecipientKeyId(Long keyId) {
|
||||
this.recipientFingerprints.add(keyId);
|
||||
return this;
|
||||
|
@ -284,10 +311,6 @@ public class OpenPgpMetadata {
|
|||
return this;
|
||||
}
|
||||
|
||||
public List<DetachedSignature> getDetachedSignatures() {
|
||||
return detachedSignatures;
|
||||
}
|
||||
|
||||
public Builder setSymmetricKeyAlgorithm(SymmetricKeyAlgorithm symmetricKeyAlgorithm) {
|
||||
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
|
||||
return this;
|
||||
|
@ -308,18 +331,29 @@ public class OpenPgpMetadata {
|
|||
return this;
|
||||
}
|
||||
|
||||
public void addDetachedSignature(DetachedSignature signature) {
|
||||
this.detachedSignatures.add(signature);
|
||||
}
|
||||
|
||||
public void addOnePassSignature(OnePassSignature onePassSignature) {
|
||||
this.onePassSignatures.add(onePassSignature);
|
||||
}
|
||||
|
||||
public OpenPgpMetadata build() {
|
||||
return new OpenPgpMetadata(recipientFingerprints, decryptionKey,
|
||||
return new OpenPgpMetadata(
|
||||
recipientFingerprints, decryptionKey,
|
||||
symmetricKeyAlgorithm, compressionAlgorithm,
|
||||
onePassSignatures, detachedSignatures, fileName, modificationDate, fileEncoding);
|
||||
verifiedInbandSignatures, invalidInbandSignatures,
|
||||
verifiedDetachedSignatures, invalidDetachedSignatures,
|
||||
fileName, modificationDate, fileEncoding);
|
||||
}
|
||||
|
||||
public void addVerifiedInbandSignature(SignatureVerification signatureVerification) {
|
||||
this.verifiedInbandSignatures.add(signatureVerification);
|
||||
}
|
||||
|
||||
public void addVerifiedDetachedSignature(SignatureVerification signatureVerification) {
|
||||
this.verifiedDetachedSignatures.add(signatureVerification);
|
||||
}
|
||||
|
||||
public void addInvalidInbandSignature(SignatureVerification signatureVerification, SignatureValidationException e) {
|
||||
this.invalidInbandSignatures.add(new SignatureVerification.Failure(signatureVerification, e));
|
||||
}
|
||||
|
||||
public void addInvalidDetachedSignature(SignatureVerification signatureVerification, SignatureValidationException e) {
|
||||
this.invalidDetachedSignatures.add(new SignatureVerification.Failure(signatureVerification, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.decryption_verification;
|
||||
|
||||
import static org.pgpainless.signature.SignatureValidator.signatureWasCreatedInBounds;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.exception.SignatureValidationException;
|
||||
import org.pgpainless.policy.Policy;
|
||||
import org.pgpainless.signature.CertificateValidator;
|
||||
import org.pgpainless.signature.DetachedSignature;
|
||||
import org.pgpainless.signature.OnePassSignatureCheck;
|
||||
import org.pgpainless.signature.cleartext_signatures.SignatureVerification;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class SignatureInputStream extends FilterInputStream {
|
||||
|
||||
protected SignatureInputStream(InputStream inputStream) {
|
||||
super(inputStream);
|
||||
}
|
||||
|
||||
public static class VerifySignatures extends SignatureInputStream {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(VerifySignatures.class);
|
||||
|
||||
private final List<OnePassSignatureCheck> opSignatures;
|
||||
private final List<DetachedSignature> detachedSignatures;
|
||||
private final ConsumerOptions options;
|
||||
private final OpenPgpMetadata.Builder resultBuilder;
|
||||
|
||||
public VerifySignatures(
|
||||
InputStream literalDataStream,
|
||||
List<OnePassSignatureCheck> opSignatures,
|
||||
List<DetachedSignature> detachedSignatures,
|
||||
ConsumerOptions options,
|
||||
OpenPgpMetadata.Builder resultBuilder) {
|
||||
super(literalDataStream);
|
||||
this.opSignatures = opSignatures;
|
||||
this.detachedSignatures = detachedSignatures;
|
||||
this.options = options;
|
||||
this.resultBuilder = resultBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
final int data = super.read();
|
||||
final boolean endOfStream = data == -1;
|
||||
if (endOfStream) {
|
||||
verifyOnePassSignatures();
|
||||
verifyDetachedSignatures();
|
||||
} else {
|
||||
byte b = (byte) data;
|
||||
updateOnePassSignatures(b);
|
||||
updateDetachedSignatures(b);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@Nonnull byte[] b, int off, int len) throws IOException {
|
||||
int read = super.read(b, off, len);
|
||||
|
||||
final boolean endOfStream = read == -1;
|
||||
if (endOfStream) {
|
||||
verifyOnePassSignatures();
|
||||
verifyDetachedSignatures();
|
||||
} else {
|
||||
updateOnePassSignatures(b, off, read);
|
||||
updateDetachedSignatures(b, off, read);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
private synchronized void verifyOnePassSignatures() {
|
||||
Policy policy = PGPainless.getPolicy();
|
||||
for (OnePassSignatureCheck opSignature : opSignatures) {
|
||||
if (opSignature.getSignature() == null) {
|
||||
LOGGER.warn("Found OnePassSignature without respective signature packet -> skip");
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
signatureWasCreatedInBounds(options.getVerifyNotBefore(), options.getVerifyNotAfter()).verify(opSignature.getSignature());
|
||||
CertificateValidator.validateCertificateAndVerifyOnePassSignature(opSignature, policy);
|
||||
resultBuilder.addVerifiedInbandSignature(new SignatureVerification(opSignature.getSignature(), opSignature.getSigningKey()));
|
||||
} catch (SignatureValidationException e) {
|
||||
LOGGER.warn("One-pass-signature verification failed for signature made by key {}: {}",
|
||||
opSignature.getSigningKey(), e.getMessage(), e);
|
||||
resultBuilder.addInvalidInbandSignature(new SignatureVerification(opSignature.getSignature(), opSignature.getSigningKey()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyDetachedSignatures() {
|
||||
Policy policy = PGPainless.getPolicy();
|
||||
for (DetachedSignature s : detachedSignatures) {
|
||||
try {
|
||||
signatureWasCreatedInBounds(options.getVerifyNotBefore(), options.getVerifyNotAfter()).verify(s.getSignature());
|
||||
CertificateValidator.validateCertificateAndVerifyInitializedSignature(s.getSignature(), (PGPPublicKeyRing) s.getSigningKeyRing(), policy);
|
||||
resultBuilder.addVerifiedDetachedSignature(new SignatureVerification(s.getSignature(), s.getSigningKeyIdentifier()));
|
||||
} catch (SignatureValidationException e) {
|
||||
LOGGER.warn("One-pass-signature verification failed for signature made by key {}: {}",
|
||||
s.getSigningKeyIdentifier(), e.getMessage(), e);
|
||||
resultBuilder.addInvalidDetachedSignature(new SignatureVerification(s.getSignature(), s.getSigningKeyIdentifier()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateOnePassSignatures(byte data) {
|
||||
for (OnePassSignatureCheck opSignature : opSignatures) {
|
||||
opSignature.getOnePassSignature().update(data);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateOnePassSignatures(byte[] bytes, int offset, int length) {
|
||||
for (OnePassSignatureCheck opSignature : opSignatures) {
|
||||
opSignature.getOnePassSignature().update(bytes, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDetachedSignatures(byte b) {
|
||||
for (DetachedSignature detachedSignature : detachedSignatures) {
|
||||
detachedSignature.getSignature().update(b);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDetachedSignatures(byte[] b, int off, int read) {
|
||||
for (DetachedSignature detachedSignature : detachedSignatures) {
|
||||
detachedSignature.getSignature().update(b, off, read);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class CleartextSignatures extends SignatureInputStream {
|
||||
public CleartextSignatures(InputStream inputStream, List<PGPSignature> signatures) {
|
||||
super(inputStream);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.decryption_verification;
|
||||
|
||||
import static org.pgpainless.signature.SignatureValidator.signatureWasCreatedInBounds;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.exception.SignatureValidationException;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.policy.Policy;
|
||||
import org.pgpainless.signature.OnePassSignature;
|
||||
import org.pgpainless.signature.CertificateValidator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SignatureVerifyingInputStream extends FilterInputStream {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SignatureVerifyingInputStream.class);
|
||||
|
||||
private final PGPObjectFactory objectFactory;
|
||||
private final Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures;
|
||||
private final ConsumerOptions options;
|
||||
private final OpenPgpMetadata.Builder resultBuilder;
|
||||
|
||||
private boolean validated = false;
|
||||
|
||||
protected SignatureVerifyingInputStream(@Nonnull InputStream inputStream,
|
||||
@Nonnull PGPObjectFactory objectFactory,
|
||||
@Nonnull Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures,
|
||||
@Nonnull ConsumerOptions options,
|
||||
@Nonnull OpenPgpMetadata.Builder resultBuilder) {
|
||||
super(inputStream);
|
||||
this.objectFactory = objectFactory;
|
||||
this.options = options;
|
||||
this.resultBuilder = resultBuilder;
|
||||
this.onePassSignatures = onePassSignatures;
|
||||
|
||||
LOGGER.debug("Begin verifying OnePassSignatures");
|
||||
}
|
||||
|
||||
private void updateOnePassSignatures(byte data) {
|
||||
for (OnePassSignature signature : onePassSignatures.values()) {
|
||||
signature.getOnePassSignature().update(data);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateOnePassSignatures(byte[] b, int off, int len) {
|
||||
for (OnePassSignature signature : onePassSignatures.values()) {
|
||||
signature.getOnePassSignature().update(b, off, len);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateOnePassSignaturesIfNeeded() throws IOException {
|
||||
if (validated) {
|
||||
return;
|
||||
}
|
||||
validated = true;
|
||||
validateOnePassSignaturesIfAny();
|
||||
}
|
||||
|
||||
private void validateOnePassSignaturesIfAny() throws IOException {
|
||||
if (onePassSignatures.isEmpty()) {
|
||||
LOGGER.debug("No One-Pass-Signatures found -> No validation");
|
||||
return;
|
||||
}
|
||||
validateOnePassSignatures();
|
||||
}
|
||||
|
||||
private void validateOnePassSignatures() throws IOException {
|
||||
PGPSignatureList signatureList = findPgpSignatureList();
|
||||
|
||||
for (PGPSignature signature : signatureList) {
|
||||
try {
|
||||
OpenPgpV4Fingerprint fingerprint = findFingerprintForSignature(signature);
|
||||
OnePassSignature onePassSignature = findOnePassSignature(fingerprint);
|
||||
if (onePassSignature == null) {
|
||||
LOGGER.warn("Found Signature without respective OnePassSignature packet -> skip");
|
||||
continue;
|
||||
}
|
||||
|
||||
verifySignatureOrThrowSignatureException(signature, onePassSignature);
|
||||
} catch (SignatureValidationException e) {
|
||||
LOGGER.warn("One-pass-signature verification failed for signature made by key {}: {}",
|
||||
Long.toHexString(signature.getKeyID()), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void verifySignatureOrThrowSignatureException(PGPSignature signature, OnePassSignature onePassSignature)
|
||||
throws SignatureValidationException {
|
||||
Policy policy = PGPainless.getPolicy();
|
||||
signatureWasCreatedInBounds(options.getVerifyNotBefore(), options.getVerifyNotAfter()).verify(signature);
|
||||
CertificateValidator.validateCertificateAndVerifyOnePassSignature(signature, onePassSignature, policy);
|
||||
}
|
||||
|
||||
private OnePassSignature findOnePassSignature(OpenPgpV4Fingerprint fingerprint) {
|
||||
if (fingerprint != null) {
|
||||
return onePassSignatures.get(fingerprint);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private PGPSignatureList findPgpSignatureList() 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 OpenPgpV4Fingerprint findFingerprintForSignature(PGPSignature signature) {
|
||||
OpenPgpV4Fingerprint fingerprint = null;
|
||||
for (OpenPgpV4Fingerprint f : onePassSignatures.keySet()) {
|
||||
if (f.getKeyId() == signature.getKeyID()) {
|
||||
fingerprint = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
final int data = super.read();
|
||||
final boolean endOfStream = data == -1;
|
||||
if (endOfStream) {
|
||||
validateOnePassSignaturesIfNeeded();
|
||||
} else {
|
||||
updateOnePassSignatures((byte) data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@Nonnull byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@Nonnull byte[] b, int off, int len) throws IOException {
|
||||
int read = super.read(b, off, len);
|
||||
|
||||
final boolean endOfStream = read == -1;
|
||||
if (endOfStream) {
|
||||
validateOnePassSignaturesIfNeeded();
|
||||
} else {
|
||||
updateOnePassSignatures(b, off, read);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) {
|
||||
throw new UnsupportedOperationException("skip() is not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
throw new UnsupportedOperationException("mark() not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() {
|
||||
throw new UnsupportedOperationException("reset() is not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -265,16 +265,16 @@ public final class CertificateValidator {
|
|||
}
|
||||
|
||||
/**
|
||||
* Validate the signing key certificate and the given {@link OnePassSignature}.
|
||||
* Validate the signing key certificate and the given {@link OnePassSignatureCheck}.
|
||||
*
|
||||
* @param signature OpenPGP signature from the signed message
|
||||
* @param onePassSignature corresponding one-pass-signature
|
||||
* @param policy policy
|
||||
* @return true if the certificate is valid and the signature is correct, false otherwise.
|
||||
* @throws SignatureValidationException in case of a validation error
|
||||
*/
|
||||
public static boolean validateCertificateAndVerifyOnePassSignature(PGPSignature signature, OnePassSignature onePassSignature, Policy policy)
|
||||
public static boolean validateCertificateAndVerifyOnePassSignature(OnePassSignatureCheck onePassSignature, Policy policy)
|
||||
throws SignatureValidationException {
|
||||
PGPSignature signature = onePassSignature.getSignature();
|
||||
validateCertificate(signature, onePassSignature.getVerificationKeys(), policy);
|
||||
PGPPublicKey signingKey = onePassSignature.getVerificationKeys().getPublicKey(signature.getKeyID());
|
||||
verifyOnePassSignature(signature, signingKey, onePassSignature, policy);
|
||||
|
|
|
@ -19,31 +19,36 @@ import org.bouncycastle.openpgp.PGPException;
|
|||
import org.bouncycastle.openpgp.PGPOnePassSignature;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.pgpainless.decryption_verification.SignatureInputStream;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.SubkeyIdentifier;
|
||||
|
||||
/**
|
||||
* Tuple-class that bundles together a {@link PGPOnePassSignature} object, a {@link PGPPublicKeyRing}
|
||||
* destined to verify the signature, the {@link PGPSignature} itself and a record of whether or not the signature
|
||||
* destined to verify the signature, the {@link PGPSignature} itself and a record of whether the signature
|
||||
* was verified.
|
||||
*/
|
||||
public class OnePassSignature {
|
||||
public class OnePassSignatureCheck {
|
||||
private final PGPOnePassSignature onePassSignature;
|
||||
private final PGPPublicKeyRing verificationKeys;
|
||||
private PGPSignature signature;
|
||||
private boolean verified;
|
||||
|
||||
/**
|
||||
* Create a new {@link OnePassSignature}.
|
||||
* Create a new {@link OnePassSignatureCheck}.
|
||||
*
|
||||
* @param onePassSignature one-pass signature packet used to initialize the signature verifier.
|
||||
* @param verificationKeys verification keys
|
||||
*/
|
||||
public OnePassSignature(PGPOnePassSignature onePassSignature, PGPPublicKeyRing verificationKeys) {
|
||||
public OnePassSignatureCheck(PGPOnePassSignature onePassSignature, PGPPublicKeyRing verificationKeys) {
|
||||
this.onePassSignature = onePassSignature;
|
||||
this.verificationKeys = verificationKeys;
|
||||
}
|
||||
|
||||
public void setSignature(PGPSignature signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the signature is verified.
|
||||
*
|
||||
|
@ -75,17 +80,16 @@ public class OnePassSignature {
|
|||
* Verify the one-pass signature.
|
||||
* Note: This method only checks if the signature itself is correct.
|
||||
* It does not check if the signing key was eligible to create the signature, or if the signature is expired etc.
|
||||
* Those checks are being done by {@link org.pgpainless.decryption_verification.SignatureVerifyingInputStream}.
|
||||
* Those checks are being done by {@link SignatureInputStream.VerifySignatures}.
|
||||
*
|
||||
* @param signature parsed-out signature
|
||||
* @return true if the signature was verified, false otherwise
|
||||
* @throws PGPException if signature verification fails with an exception.
|
||||
*/
|
||||
public boolean verify(PGPSignature signature) throws PGPException {
|
||||
this.verified = getOnePassSignature().verify(signature);
|
||||
if (verified) {
|
||||
this.signature = signature;
|
||||
public boolean verify() throws PGPException {
|
||||
if (signature == null) {
|
||||
throw new IllegalStateException("No comparison signature provided.");
|
||||
}
|
||||
this.verified = getOnePassSignature().verify(signature);
|
||||
return verified;
|
||||
}
|
||||
|
|
@ -337,4 +337,12 @@ public final class SignatureUtils {
|
|||
public static String getSignatureDigestPrefix(PGPSignature signature) {
|
||||
return Hex.toHexString(signature.getDigestPrefix());
|
||||
}
|
||||
|
||||
public static List<PGPSignature> toList(PGPSignatureList signatures) {
|
||||
List<PGPSignature> list = new ArrayList<>();
|
||||
for (PGPSignature signature : signatures) {
|
||||
list.add(signature);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -424,7 +424,7 @@ public final class SignatureVerifier {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean verifyOnePassSignature(PGPSignature signature, PGPPublicKey signingKey, OnePassSignature onePassSignature, Policy policy)
|
||||
public static boolean verifyOnePassSignature(PGPSignature signature, PGPPublicKey signingKey, OnePassSignatureCheck onePassSignature, Policy policy)
|
||||
throws SignatureValidationException {
|
||||
try {
|
||||
SignatureValidator.wasPossiblyMadeByKey(signingKey).verify(signature);
|
||||
|
@ -435,7 +435,7 @@ public final class SignatureVerifier {
|
|||
}
|
||||
|
||||
try {
|
||||
if (!onePassSignature.verify(signature)) {
|
||||
if (!onePassSignature.verify()) {
|
||||
throw new SignatureValidationException("Bad signature of key " + Long.toHexString(signingKey.getKeyID()));
|
||||
}
|
||||
} catch (PGPException e) {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.signature.cleartext_signatures;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.pgpainless.exception.SignatureValidationException;
|
||||
import org.pgpainless.key.SubkeyIdentifier;
|
||||
|
||||
public class SignatureVerification {
|
||||
|
||||
private final PGPSignature signature;
|
||||
private final SubkeyIdentifier signingKey;
|
||||
|
||||
public SignatureVerification(PGPSignature signature, SubkeyIdentifier signingKey) {
|
||||
this.signature = signature;
|
||||
this.signingKey = signingKey;
|
||||
}
|
||||
|
||||
public PGPSignature getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public SubkeyIdentifier getSigningKey() {
|
||||
return signingKey;
|
||||
}
|
||||
|
||||
public static class Failure {
|
||||
private final SignatureVerification signatureVerification;
|
||||
private final SignatureValidationException validationException;
|
||||
|
||||
public Failure(SignatureVerification verification, SignatureValidationException validationException) {
|
||||
this.signatureVerification = verification;
|
||||
this.validationException = validationException;
|
||||
}
|
||||
|
||||
public SignatureVerification getSignatureVerification() {
|
||||
return signatureVerification;
|
||||
}
|
||||
|
||||
public SignatureValidationException getValidationException() {
|
||||
return validationException;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -93,7 +93,7 @@ public class VerifyNotBeforeNotAfterTest {
|
|||
.withOptions(options);
|
||||
|
||||
OpenPgpMetadata metadata = processSignedData(verifier);
|
||||
assertTrue(metadata.getVerifiedSignatures().containsKey(new SubkeyIdentifier(certificate)));
|
||||
assertTrue(metadata.containsVerifiedSignatureFrom(certificate));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -30,6 +30,7 @@ public class DearmorImpl implements Dearmor {
|
|||
public Ready data(InputStream data) throws IOException {
|
||||
InputStream decoder = PGPUtil.getDecoderStream(data);
|
||||
return new Ready() {
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream) throws IOException {
|
||||
Streams.pipeAll(decoder, outputStream);
|
||||
|
|
Loading…
Reference in a new issue