2021-10-07 15:48:52 +02:00
|
|
|
// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2018-07-18 18:23:06 +02:00
|
|
|
package org.pgpainless.decryption_verification;
|
2018-06-06 18:46:41 +02:00
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
2021-09-02 18:01:06 +02:00
|
|
|
import java.util.ArrayList;
|
2021-10-08 14:03:12 +02:00
|
|
|
import java.util.HashMap;
|
2021-10-18 16:01:19 +02:00
|
|
|
import java.util.HashSet;
|
2018-06-06 18:46:41 +02:00
|
|
|
import java.util.Iterator;
|
2020-08-24 14:55:06 +02:00
|
|
|
import java.util.List;
|
2021-10-08 14:03:12 +02:00
|
|
|
import java.util.Map;
|
2022-03-22 15:09:09 +01:00
|
|
|
import java.util.NoSuchElementException;
|
2021-08-23 00:47:26 +02:00
|
|
|
import java.util.Set;
|
2020-12-27 01:56:18 +01:00
|
|
|
import javax.annotation.Nonnull;
|
2021-11-24 14:51:16 +01:00
|
|
|
import javax.annotation.Nullable;
|
2018-06-06 18:46:41 +02:00
|
|
|
|
2021-07-27 15:09:59 +02:00
|
|
|
import org.bouncycastle.bcpg.ArmoredInputStream;
|
2018-06-06 18:46:41 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPCompressedData;
|
2020-12-26 19:04:27 +01:00
|
|
|
import org.bouncycastle.openpgp.PGPEncryptedData;
|
2018-06-06 18:46:41 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPEncryptedDataList;
|
|
|
|
import org.bouncycastle.openpgp.PGPException;
|
|
|
|
import org.bouncycastle.openpgp.PGPLiteralData;
|
|
|
|
import org.bouncycastle.openpgp.PGPObjectFactory;
|
|
|
|
import org.bouncycastle.openpgp.PGPOnePassSignature;
|
|
|
|
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
|
2020-12-26 19:04:27 +01:00
|
|
|
import org.bouncycastle.openpgp.PGPPBEEncryptedData;
|
2018-06-06 18:46:41 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
|
|
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
|
|
|
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
|
|
|
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
|
|
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
2021-01-09 16:16:17 +01:00
|
|
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
2021-10-15 14:58:17 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPSessionKey;
|
2020-08-24 14:55:06 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPSignature;
|
2022-02-19 15:41:43 +01:00
|
|
|
import org.bouncycastle.openpgp.PGPSignatureList;
|
2018-06-06 18:46:41 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPUtil;
|
2020-12-26 19:04:27 +01:00
|
|
|
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
|
2018-06-06 18:46:41 +02:00
|
|
|
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
|
|
|
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
2021-10-15 14:58:17 +02:00
|
|
|
import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory;
|
2021-05-17 13:47:46 +02:00
|
|
|
import org.pgpainless.PGPainless;
|
2018-07-18 18:23:06 +02:00
|
|
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
2021-06-15 17:08:40 +02:00
|
|
|
import org.pgpainless.algorithm.EncryptionPurpose;
|
2021-04-25 00:28:48 +02:00
|
|
|
import org.pgpainless.algorithm.StreamEncoding;
|
2018-07-18 18:23:06 +02:00
|
|
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
2022-02-19 15:41:43 +01:00
|
|
|
import org.pgpainless.decryption_verification.cleartext_signatures.ClearsignedMessageUtil;
|
|
|
|
import org.pgpainless.decryption_verification.cleartext_signatures.MultiPassStrategy;
|
2022-02-11 13:58:50 +01:00
|
|
|
import org.pgpainless.exception.FinalIOException;
|
2021-04-27 12:27:25 +02:00
|
|
|
import org.pgpainless.exception.MessageNotIntegrityProtectedException;
|
2021-05-28 23:20:25 +02:00
|
|
|
import org.pgpainless.exception.MissingDecryptionMethodException;
|
2021-06-15 17:35:58 +02:00
|
|
|
import org.pgpainless.exception.MissingLiteralDataException;
|
2021-10-18 16:01:19 +02:00
|
|
|
import org.pgpainless.exception.MissingPassphraseException;
|
2021-10-08 14:03:12 +02:00
|
|
|
import org.pgpainless.exception.SignatureValidationException;
|
2021-05-17 13:47:46 +02:00
|
|
|
import org.pgpainless.exception.UnacceptableAlgorithmException;
|
2020-12-27 01:56:18 +01:00
|
|
|
import org.pgpainless.implementation.ImplementationFactory;
|
2021-04-26 13:38:12 +02:00
|
|
|
import org.pgpainless.key.SubkeyIdentifier;
|
2021-06-15 17:08:40 +02:00
|
|
|
import org.pgpainless.key.info.KeyRingInfo;
|
2021-09-15 16:33:03 +02:00
|
|
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
2021-05-14 13:18:34 +02:00
|
|
|
import org.pgpainless.key.protection.UnlockSecretKey;
|
2021-12-14 15:03:45 +01:00
|
|
|
import org.pgpainless.signature.SignatureUtils;
|
2021-11-03 13:30:16 +01:00
|
|
|
import org.pgpainless.signature.consumer.DetachedSignatureCheck;
|
|
|
|
import org.pgpainless.signature.consumer.OnePassSignatureCheck;
|
2022-02-19 15:41:43 +01:00
|
|
|
import org.pgpainless.util.ArmoredInputStreamFactory;
|
2020-12-26 19:04:27 +01:00
|
|
|
import org.pgpainless.util.Passphrase;
|
2021-10-15 14:58:17 +02:00
|
|
|
import org.pgpainless.util.SessionKey;
|
2021-09-15 16:33:03 +02:00
|
|
|
import org.pgpainless.util.Tuple;
|
2021-08-23 13:29:57 +02:00
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
2018-06-06 18:46:41 +02:00
|
|
|
|
2018-07-02 21:40:59 +02:00
|
|
|
public final class DecryptionStreamFactory {
|
2018-06-06 18:46:41 +02:00
|
|
|
|
2021-10-18 16:01:19 +02:00
|
|
|
|
2021-08-23 13:29:57 +02:00
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(DecryptionStreamFactory.class);
|
2021-12-07 23:46:44 +01:00
|
|
|
// Maximum nesting depth of packets (e.g. compression, encryption...)
|
|
|
|
private static final int MAX_PACKET_NESTING_DEPTH = 16;
|
2018-06-19 17:14:37 +02:00
|
|
|
|
2021-06-15 17:08:40 +02:00
|
|
|
private final ConsumerOptions options;
|
2018-07-23 16:23:23 +02:00
|
|
|
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
2021-09-02 18:01:06 +02:00
|
|
|
private final List<OnePassSignatureCheck> onePassSignatureChecks = new ArrayList<>();
|
2021-10-03 13:47:20 +02:00
|
|
|
private final List<DetachedSignatureCheck> detachedSignatureChecks = new ArrayList<>();
|
2021-10-08 14:03:12 +02:00
|
|
|
private final Map<Long, OnePassSignatureCheck> onePassSignaturesWithMissingCert = new HashMap<>();
|
2021-09-02 18:01:06 +02:00
|
|
|
|
2021-07-15 16:55:13 +02:00
|
|
|
private static final PGPContentVerifierBuilderProvider verifierBuilderProvider =
|
|
|
|
ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider();
|
2021-09-06 15:14:13 +02:00
|
|
|
private IntegrityProtectedInputStream integrityProtectedEncryptedInputStream;
|
2018-06-06 18:46:41 +02:00
|
|
|
|
2021-09-02 18:01:06 +02:00
|
|
|
|
2021-09-06 17:15:17 +02:00
|
|
|
public static DecryptionStream create(@Nonnull InputStream inputStream,
|
|
|
|
@Nonnull ConsumerOptions options)
|
|
|
|
throws PGPException, IOException {
|
|
|
|
DecryptionStreamFactory factory = new DecryptionStreamFactory(options);
|
2022-04-22 20:53:44 +02:00
|
|
|
OpenPgpInputStream openPgpIn = new OpenPgpInputStream(inputStream);
|
|
|
|
return factory.parseOpenPGPDataAndCreateDecryptionStream(openPgpIn);
|
2021-09-06 17:15:17 +02:00
|
|
|
}
|
|
|
|
|
2021-06-15 17:08:40 +02:00
|
|
|
public DecryptionStreamFactory(ConsumerOptions options) {
|
|
|
|
this.options = options;
|
2021-08-23 00:47:26 +02:00
|
|
|
initializeDetachedSignatures(options.getDetachedSignatures());
|
|
|
|
}
|
2021-07-27 15:09:59 +02:00
|
|
|
|
2021-08-23 00:47:26 +02:00
|
|
|
private void initializeDetachedSignatures(Set<PGPSignature> signatures) {
|
|
|
|
for (PGPSignature signature : signatures) {
|
2021-07-31 22:24:39 +02:00
|
|
|
long issuerKeyId = SignatureUtils.determineIssuerKeyId(signature);
|
|
|
|
PGPPublicKeyRing signingKeyRing = findSignatureVerificationKeyRing(issuerKeyId);
|
2021-07-27 15:09:59 +02:00
|
|
|
if (signingKeyRing == null) {
|
2021-12-28 13:32:50 +01:00
|
|
|
SignatureValidationException ex = new SignatureValidationException(
|
|
|
|
"Missing verification certificate " + Long.toHexString(issuerKeyId));
|
2021-10-08 14:03:12 +02:00
|
|
|
resultBuilder.addInvalidDetachedSignature(new SignatureVerification(signature, null), ex);
|
2021-07-27 15:09:59 +02:00
|
|
|
continue;
|
|
|
|
}
|
2021-07-31 22:24:39 +02:00
|
|
|
PGPPublicKey signingKey = signingKeyRing.getPublicKey(issuerKeyId);
|
2021-07-27 15:09:59 +02:00
|
|
|
SubkeyIdentifier signingKeyIdentifier = new SubkeyIdentifier(signingKeyRing, signingKey.getKeyID());
|
|
|
|
try {
|
2021-09-02 18:01:06 +02:00
|
|
|
signature.init(verifierBuilderProvider, signingKey);
|
2021-12-28 13:32:50 +01:00
|
|
|
DetachedSignatureCheck detachedSignature =
|
|
|
|
new DetachedSignatureCheck(signature, signingKeyRing, signingKeyIdentifier);
|
2021-09-02 18:01:06 +02:00
|
|
|
detachedSignatureChecks.add(detachedSignature);
|
2021-07-27 15:09:59 +02:00
|
|
|
} catch (PGPException e) {
|
2021-12-28 13:32:50 +01:00
|
|
|
SignatureValidationException ex = new SignatureValidationException(
|
|
|
|
"Cannot verify detached signature made by " + signingKeyIdentifier + ".", e);
|
2021-10-08 14:03:12 +02:00
|
|
|
resultBuilder.addInvalidDetachedSignature(new SignatureVerification(signature, signingKeyIdentifier), ex);
|
2021-07-27 15:09:59 +02:00
|
|
|
}
|
|
|
|
}
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
|
|
|
|
2022-04-22 20:53:44 +02:00
|
|
|
private DecryptionStream parseOpenPGPDataAndCreateDecryptionStream(OpenPgpInputStream openPgpIn)
|
2021-12-28 13:32:50 +01:00
|
|
|
throws IOException, PGPException {
|
2022-04-22 20:53:44 +02:00
|
|
|
|
2022-02-19 15:41:43 +01:00
|
|
|
InputStream pgpInStream;
|
|
|
|
InputStream outerDecodingStream;
|
2021-10-30 15:00:04 +02:00
|
|
|
PGPObjectFactory objectFactory;
|
2021-06-15 17:08:40 +02:00
|
|
|
|
2022-04-22 20:53:44 +02:00
|
|
|
// Non-OpenPGP data. We are probably verifying detached signatures
|
2022-04-22 21:33:13 +02:00
|
|
|
if (openPgpIn.isNonOpenPgp() || options.isForceNonOpenPgpData()) {
|
2022-04-22 20:53:44 +02:00
|
|
|
outerDecodingStream = openPgpIn;
|
|
|
|
pgpInStream = wrapInVerifySignatureStream(outerDecodingStream, null);
|
|
|
|
return new DecryptionStream(pgpInStream, resultBuilder, integrityProtectedEncryptedInputStream, null);
|
|
|
|
}
|
2021-08-29 13:35:27 +02:00
|
|
|
|
2022-05-05 12:43:44 +02:00
|
|
|
// Data appears to be OpenPGP message,
|
|
|
|
// or we handle it as such, since user provided a session-key for decryption
|
|
|
|
if (openPgpIn.isLikelyOpenPgpMessage() ||
|
|
|
|
(openPgpIn.isBinaryOpenPgp() && options.getSessionKey() != null)) {
|
2022-04-22 20:53:44 +02:00
|
|
|
outerDecodingStream = openPgpIn;
|
2022-02-19 15:41:43 +01:00
|
|
|
objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(outerDecodingStream);
|
2021-06-15 17:35:58 +02:00
|
|
|
// Parse OpenPGP message
|
2022-02-19 15:41:43 +01:00
|
|
|
pgpInStream = processPGPPackets(objectFactory, 1);
|
|
|
|
return new DecryptionStream(pgpInStream,
|
2022-04-22 20:53:44 +02:00
|
|
|
resultBuilder, integrityProtectedEncryptedInputStream, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (openPgpIn.isAsciiArmored()) {
|
|
|
|
ArmoredInputStream armoredInputStream = ArmoredInputStreamFactory.get(openPgpIn);
|
|
|
|
if (armoredInputStream.isClearText()) {
|
2022-06-19 17:31:48 +02:00
|
|
|
resultBuilder.setCleartextSigned();
|
2022-04-22 20:53:44 +02:00
|
|
|
return parseCleartextSignedMessage(armoredInputStream);
|
2021-07-15 16:55:13 +02:00
|
|
|
} else {
|
2022-04-22 20:53:44 +02:00
|
|
|
outerDecodingStream = armoredInputStream;
|
|
|
|
objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(outerDecodingStream);
|
|
|
|
// Parse OpenPGP message
|
|
|
|
pgpInStream = processPGPPackets(objectFactory, 1);
|
|
|
|
return new DecryptionStream(pgpInStream,
|
|
|
|
resultBuilder, integrityProtectedEncryptedInputStream,
|
|
|
|
outerDecodingStream);
|
2021-07-15 16:55:13 +02:00
|
|
|
}
|
2021-06-15 17:35:58 +02:00
|
|
|
}
|
|
|
|
|
2022-04-22 20:53:44 +02:00
|
|
|
throw new PGPException("Not sure how to handle the input stream.");
|
2022-02-19 15:41:43 +01:00
|
|
|
}
|
|
|
|
|
2022-04-22 20:53:44 +02:00
|
|
|
private DecryptionStream parseCleartextSignedMessage(ArmoredInputStream armorIn)
|
2022-02-19 15:41:43 +01:00
|
|
|
throws IOException, PGPException {
|
|
|
|
resultBuilder.setCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED)
|
|
|
|
.setFileEncoding(StreamEncoding.TEXT);
|
|
|
|
|
|
|
|
MultiPassStrategy multiPassStrategy = options.getMultiPassStrategy();
|
|
|
|
PGPSignatureList signatures = ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage(armorIn, multiPassStrategy.getMessageOutputStream());
|
|
|
|
|
|
|
|
for (PGPSignature signature : signatures) {
|
|
|
|
options.addVerificationOfDetachedSignature(signature);
|
|
|
|
}
|
|
|
|
|
|
|
|
initializeDetachedSignatures(options.getDetachedSignatures());
|
|
|
|
|
|
|
|
InputStream verifyIn = wrapInVerifySignatureStream(multiPassStrategy.getMessageInputStream(), null);
|
|
|
|
return new DecryptionStream(verifyIn, resultBuilder, integrityProtectedEncryptedInputStream,
|
|
|
|
null);
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
|
|
|
|
2021-11-24 14:51:16 +01:00
|
|
|
private InputStream wrapInVerifySignatureStream(InputStream bufferedIn, @Nullable PGPObjectFactory objectFactory) {
|
2021-09-02 18:01:06 +02:00
|
|
|
return new SignatureInputStream.VerifySignatures(
|
2021-10-23 16:44:40 +02:00
|
|
|
bufferedIn, objectFactory, onePassSignatureChecks,
|
|
|
|
onePassSignaturesWithMissingCert, detachedSignatureChecks, options,
|
2021-09-02 18:01:06 +02:00
|
|
|
resultBuilder);
|
|
|
|
}
|
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
private InputStream processPGPPackets(@Nonnull PGPObjectFactory objectFactory, int depth)
|
|
|
|
throws IOException, PGPException {
|
2021-12-07 23:46:44 +01:00
|
|
|
if (depth >= MAX_PACKET_NESTING_DEPTH) {
|
2021-12-14 13:14:56 +01:00
|
|
|
throw new PGPException("Maximum depth of nested packages exceeded.");
|
2021-02-19 21:37:54 +01:00
|
|
|
}
|
2020-01-10 17:12:13 +01:00
|
|
|
Object nextPgpObject;
|
2022-02-11 13:58:50 +01:00
|
|
|
try {
|
|
|
|
while ((nextPgpObject = objectFactory.nextObject()) != null) {
|
|
|
|
if (nextPgpObject instanceof PGPEncryptedDataList) {
|
|
|
|
return processPGPEncryptedDataList((PGPEncryptedDataList) nextPgpObject, depth);
|
|
|
|
}
|
|
|
|
if (nextPgpObject instanceof PGPCompressedData) {
|
|
|
|
return processPGPCompressedData((PGPCompressedData) nextPgpObject, depth);
|
|
|
|
}
|
|
|
|
if (nextPgpObject instanceof PGPOnePassSignatureList) {
|
|
|
|
return processOnePassSignatureList(objectFactory, (PGPOnePassSignatureList) nextPgpObject, depth);
|
|
|
|
}
|
|
|
|
if (nextPgpObject instanceof PGPLiteralData) {
|
|
|
|
return processPGPLiteralData(objectFactory, (PGPLiteralData) nextPgpObject, depth);
|
|
|
|
}
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
2022-02-11 13:58:50 +01:00
|
|
|
} catch (FinalIOException e) {
|
|
|
|
throw e;
|
|
|
|
} catch (IOException e) {
|
2022-02-15 14:21:28 +01:00
|
|
|
if (depth == 1 && e.getMessage().contains("invalid armor")) {
|
|
|
|
throw e;
|
|
|
|
}
|
2022-02-11 13:58:50 +01:00
|
|
|
if (depth == 1 && e.getMessage().contains("unknown object in stream:")) {
|
|
|
|
throw new MissingLiteralDataException("No Literal Data Packet found.");
|
|
|
|
} else {
|
|
|
|
throw new FinalIOException(e);
|
2020-01-10 17:12:13 +01:00
|
|
|
}
|
|
|
|
}
|
2018-06-06 18:46:41 +02:00
|
|
|
|
2021-06-15 17:35:58 +02:00
|
|
|
throw new MissingLiteralDataException("No Literal Data Packet found");
|
2020-01-10 17:12:13 +01:00
|
|
|
}
|
2018-06-06 18:46:41 +02:00
|
|
|
|
2021-02-19 21:37:54 +01:00
|
|
|
private InputStream processPGPEncryptedDataList(PGPEncryptedDataList pgpEncryptedDataList, int depth)
|
2020-01-10 17:12:13 +01:00
|
|
|
throws PGPException, IOException {
|
2021-08-23 13:29:57 +02:00
|
|
|
LOGGER.debug("Depth {}: Encountered PGPEncryptedDataList", depth);
|
2021-10-15 14:58:17 +02:00
|
|
|
|
|
|
|
SessionKey sessionKey = options.getSessionKey();
|
|
|
|
if (sessionKey != null) {
|
|
|
|
integrityProtectedEncryptedInputStream = decryptWithProvidedSessionKey(pgpEncryptedDataList, sessionKey);
|
|
|
|
InputStream decodedDataStream = PGPUtil.getDecoderStream(integrityProtectedEncryptedInputStream);
|
2021-12-14 15:03:45 +01:00
|
|
|
PGPObjectFactory factory = ImplementationFactory.getInstance().getPGPObjectFactory(decodedDataStream);
|
2021-10-15 14:58:17 +02:00
|
|
|
return processPGPPackets(factory, ++depth);
|
|
|
|
}
|
|
|
|
|
2021-09-06 17:15:17 +02:00
|
|
|
InputStream decryptedDataStream = decryptSessionKey(pgpEncryptedDataList);
|
2021-07-15 16:55:13 +02:00
|
|
|
InputStream decodedDataStream = PGPUtil.getDecoderStream(decryptedDataStream);
|
2021-12-14 15:03:45 +01:00
|
|
|
PGPObjectFactory factory = ImplementationFactory.getInstance().getPGPObjectFactory(decodedDataStream);
|
2021-07-15 16:55:13 +02:00
|
|
|
return processPGPPackets(factory, ++depth);
|
2020-01-10 17:12:13 +01:00
|
|
|
}
|
2018-06-06 18:46:41 +02:00
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
private IntegrityProtectedInputStream decryptWithProvidedSessionKey(
|
|
|
|
PGPEncryptedDataList pgpEncryptedDataList,
|
|
|
|
SessionKey sessionKey)
|
|
|
|
throws PGPException {
|
2022-09-13 20:26:13 +02:00
|
|
|
SessionKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance()
|
|
|
|
.getSessionKeyDataDecryptorFactory(sessionKey);
|
2021-10-15 14:58:17 +02:00
|
|
|
InputStream decryptedDataStream = null;
|
|
|
|
PGPEncryptedData encryptedData = null;
|
|
|
|
for (PGPEncryptedData pgpEncryptedData : pgpEncryptedDataList) {
|
|
|
|
encryptedData = pgpEncryptedData;
|
|
|
|
if (!options.isIgnoreMDCErrors() && !encryptedData.isIntegrityProtected()) {
|
|
|
|
throw new MessageNotIntegrityProtectedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (encryptedData instanceof PGPPBEEncryptedData) {
|
|
|
|
PGPPBEEncryptedData pbeEncrypted = (PGPPBEEncryptedData) encryptedData;
|
|
|
|
decryptedDataStream = pbeEncrypted.getDataStream(decryptorFactory);
|
|
|
|
break;
|
|
|
|
} else if (encryptedData instanceof PGPPublicKeyEncryptedData) {
|
|
|
|
PGPPublicKeyEncryptedData pkEncrypted = (PGPPublicKeyEncryptedData) encryptedData;
|
|
|
|
decryptedDataStream = pkEncrypted.getDataStream(decryptorFactory);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (decryptedDataStream == null) {
|
|
|
|
throw new PGPException("No valid PGP data encountered.");
|
|
|
|
}
|
|
|
|
|
|
|
|
resultBuilder.setSessionKey(sessionKey);
|
|
|
|
throwIfAlgorithmIsRejected(sessionKey.getAlgorithm());
|
2021-12-28 13:32:50 +01:00
|
|
|
integrityProtectedEncryptedInputStream =
|
|
|
|
new IntegrityProtectedInputStream(decryptedDataStream, encryptedData, options);
|
2021-10-15 14:58:17 +02:00
|
|
|
return integrityProtectedEncryptedInputStream;
|
|
|
|
}
|
|
|
|
|
2021-02-19 21:37:54 +01:00
|
|
|
private InputStream processPGPCompressedData(PGPCompressedData pgpCompressedData, int depth)
|
2020-01-10 17:12:13 +01:00
|
|
|
throws PGPException, IOException {
|
2022-03-22 15:09:09 +01:00
|
|
|
try {
|
|
|
|
CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.requireFromId(pgpCompressedData.getAlgorithm());
|
|
|
|
LOGGER.debug("Depth {}: Encountered PGPCompressedData: {}", depth, compressionAlgorithm);
|
|
|
|
resultBuilder.setCompressionAlgorithm(compressionAlgorithm);
|
|
|
|
} catch (NoSuchElementException e) {
|
|
|
|
throw new PGPException("Unknown compression algorithm encountered.", e);
|
|
|
|
}
|
2020-01-10 17:12:13 +01:00
|
|
|
|
2021-07-15 16:55:13 +02:00
|
|
|
InputStream inflatedDataStream = pgpCompressedData.getDataStream();
|
|
|
|
InputStream decodedDataStream = PGPUtil.getDecoderStream(inflatedDataStream);
|
2021-12-14 15:03:45 +01:00
|
|
|
PGPObjectFactory objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(decodedDataStream);
|
2020-01-10 17:12:13 +01:00
|
|
|
|
2021-02-19 21:37:54 +01:00
|
|
|
return processPGPPackets(objectFactory, ++depth);
|
2020-01-10 17:12:13 +01:00
|
|
|
}
|
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
private InputStream processOnePassSignatureList(
|
|
|
|
@Nonnull PGPObjectFactory objectFactory,
|
|
|
|
PGPOnePassSignatureList onePassSignatures,
|
|
|
|
int depth)
|
2020-01-10 17:12:13 +01:00
|
|
|
throws PGPException, IOException {
|
2021-08-23 13:29:57 +02:00
|
|
|
LOGGER.debug("Depth {}: Encountered PGPOnePassSignatureList of size {}", depth, onePassSignatures.size());
|
2020-01-10 17:12:13 +01:00
|
|
|
initOnePassSignatures(onePassSignatures);
|
2021-10-23 16:44:40 +02:00
|
|
|
return processPGPPackets(objectFactory, depth);
|
2020-01-10 17:12:13 +01:00
|
|
|
}
|
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
private InputStream processPGPLiteralData(
|
|
|
|
@Nonnull PGPObjectFactory objectFactory,
|
|
|
|
PGPLiteralData pgpLiteralData,
|
|
|
|
int depth) {
|
2021-08-23 13:29:57 +02:00
|
|
|
LOGGER.debug("Depth {}: Found PGPLiteralData", depth);
|
2020-01-10 17:12:13 +01:00
|
|
|
InputStream literalDataInputStream = pgpLiteralData.getInputStream();
|
2021-07-15 16:55:13 +02:00
|
|
|
|
|
|
|
resultBuilder.setFileName(pgpLiteralData.getFileName())
|
|
|
|
.setModificationDate(pgpLiteralData.getModificationTime())
|
2022-03-22 15:09:09 +01:00
|
|
|
.setFileEncoding(StreamEncoding.requireFromCode(pgpLiteralData.getFormat()));
|
2020-01-10 17:12:13 +01:00
|
|
|
|
2021-10-08 14:03:12 +02:00
|
|
|
if (onePassSignatureChecks.isEmpty() && onePassSignaturesWithMissingCert.isEmpty()) {
|
2021-08-23 13:29:57 +02:00
|
|
|
LOGGER.debug("No OnePassSignatures found -> We are done");
|
2020-01-10 17:12:13 +01:00
|
|
|
return literalDataInputStream;
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
|
|
|
|
2021-10-23 16:44:40 +02:00
|
|
|
return new SignatureInputStream.VerifySignatures(literalDataInputStream, objectFactory,
|
|
|
|
onePassSignatureChecks, onePassSignaturesWithMissingCert, detachedSignatureChecks, options, resultBuilder) {
|
2021-09-02 18:01:06 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-09-06 17:15:17 +02:00
|
|
|
private InputStream decryptSessionKey(@Nonnull PGPEncryptedDataList encryptedDataList)
|
2018-06-06 18:46:41 +02:00
|
|
|
throws PGPException {
|
2020-12-26 19:04:27 +01:00
|
|
|
Iterator<PGPEncryptedData> encryptedDataIterator = encryptedDataList.getEncryptedDataObjects();
|
2020-01-10 17:12:13 +01:00
|
|
|
if (!encryptedDataIterator.hasNext()) {
|
2018-06-19 17:14:37 +02:00
|
|
|
throw new PGPException("Decryption failed - EncryptedDataList has no items");
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
PGPPrivateKey decryptionKey = null;
|
|
|
|
PGPPublicKeyEncryptedData encryptedSessionKey = null;
|
2021-09-15 16:33:03 +02:00
|
|
|
|
|
|
|
List<PGPPBEEncryptedData> passphraseProtected = new ArrayList<>();
|
|
|
|
List<PGPPublicKeyEncryptedData> publicKeyProtected = new ArrayList<>();
|
|
|
|
List<Tuple<SubkeyIdentifier, PGPPublicKeyEncryptedData>> postponedDueToMissingPassphrase = new ArrayList<>();
|
|
|
|
|
|
|
|
// Sort PKESK and SKESK packets
|
2020-01-10 17:12:13 +01:00
|
|
|
while (encryptedDataIterator.hasNext()) {
|
2020-12-26 19:04:27 +01:00
|
|
|
PGPEncryptedData encryptedData = encryptedDataIterator.next();
|
2021-10-01 13:54:51 +02:00
|
|
|
|
|
|
|
if (!encryptedData.isIntegrityProtected() && !options.isIgnoreMDCErrors()) {
|
2021-04-27 12:27:25 +02:00
|
|
|
throw new MessageNotIntegrityProtectedException();
|
|
|
|
}
|
2021-06-15 17:08:40 +02:00
|
|
|
|
2021-09-15 16:33:03 +02:00
|
|
|
// SKESK
|
2021-04-27 12:27:25 +02:00
|
|
|
if (encryptedData instanceof PGPPBEEncryptedData) {
|
2021-09-15 16:33:03 +02:00
|
|
|
passphraseProtected.add((PGPPBEEncryptedData) encryptedData);
|
|
|
|
}
|
|
|
|
// PKESK
|
|
|
|
else if (encryptedData instanceof PGPPublicKeyEncryptedData) {
|
|
|
|
publicKeyProtected.add((PGPPublicKeyEncryptedData) encryptedData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try decryption with passphrases first
|
|
|
|
for (PGPPBEEncryptedData pbeEncryptedData : passphraseProtected) {
|
|
|
|
for (Passphrase passphrase : options.getDecryptionPassphrases()) {
|
|
|
|
PBEDataDecryptorFactory passphraseDecryptor = ImplementationFactory.getInstance()
|
|
|
|
.getPBEDataDecryptorFactory(passphrase);
|
|
|
|
try {
|
|
|
|
InputStream decryptedDataStream = pbeEncryptedData.getDataStream(passphraseDecryptor);
|
|
|
|
|
2021-10-15 14:58:17 +02:00
|
|
|
PGPSessionKey pgpSessionKey = pbeEncryptedData.getSessionKey(passphraseDecryptor);
|
|
|
|
SessionKey sessionKey = new SessionKey(pgpSessionKey);
|
|
|
|
resultBuilder.setSessionKey(sessionKey);
|
|
|
|
|
|
|
|
throwIfAlgorithmIsRejected(sessionKey.getAlgorithm());
|
2021-09-15 16:33:03 +02:00
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
integrityProtectedEncryptedInputStream =
|
|
|
|
new IntegrityProtectedInputStream(decryptedDataStream, pbeEncryptedData, options);
|
2021-09-15 16:33:03 +02:00
|
|
|
|
|
|
|
return integrityProtectedEncryptedInputStream;
|
|
|
|
} catch (PGPException e) {
|
|
|
|
LOGGER.debug("Probable passphrase mismatch, skip PBE encrypted data block", e);
|
2020-12-26 19:04:27 +01:00
|
|
|
}
|
2021-05-14 13:18:34 +02:00
|
|
|
}
|
2021-09-15 16:33:03 +02:00
|
|
|
}
|
2021-06-15 17:08:40 +02:00
|
|
|
|
2021-09-15 16:33:03 +02:00
|
|
|
// Then try decryption with public key encryption
|
|
|
|
for (PGPPublicKeyEncryptedData publicKeyEncryptedData : publicKeyProtected) {
|
|
|
|
PGPPrivateKey privateKey = null;
|
|
|
|
if (options.getDecryptionKeys().isEmpty()) {
|
|
|
|
break;
|
|
|
|
}
|
2021-06-15 17:08:40 +02:00
|
|
|
|
2021-09-15 16:33:03 +02:00
|
|
|
long keyId = publicKeyEncryptedData.getKeyID();
|
|
|
|
// Wildcard KeyID
|
|
|
|
if (keyId == 0L) {
|
|
|
|
LOGGER.debug("Hidden recipient detected. Try to decrypt with all available secret keys.");
|
|
|
|
for (PGPSecretKeyRing secretKeys : options.getDecryptionKeys()) {
|
|
|
|
if (privateKey != null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
KeyRingInfo info = new KeyRingInfo(secretKeys);
|
2021-11-02 11:30:44 +01:00
|
|
|
List<PGPPublicKey> encryptionSubkeys = info.getEncryptionSubkeys(EncryptionPurpose.ANY);
|
2021-09-15 16:33:03 +02:00
|
|
|
for (PGPPublicKey pubkey : encryptionSubkeys) {
|
|
|
|
PGPSecretKey secretKey = secretKeys.getSecretKey(pubkey.getKeyID());
|
|
|
|
// Skip missing secret key
|
|
|
|
if (secretKey == null) {
|
|
|
|
continue;
|
2021-01-09 16:16:17 +01:00
|
|
|
}
|
2021-09-15 16:33:03 +02:00
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
privateKey = tryPublicKeyDecryption(secretKeys, secretKey, publicKeyEncryptedData,
|
|
|
|
postponedDueToMissingPassphrase, true);
|
2020-12-26 19:04:27 +01:00
|
|
|
}
|
|
|
|
}
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
2021-09-15 16:33:03 +02:00
|
|
|
// Non-wildcard key-id
|
|
|
|
else {
|
|
|
|
LOGGER.debug("PGPEncryptedData is encrypted for key {}", Long.toHexString(keyId));
|
|
|
|
resultBuilder.addRecipientKeyId(keyId);
|
|
|
|
|
|
|
|
PGPSecretKeyRing secretKeys = findDecryptionKeyRing(keyId);
|
|
|
|
if (secretKeys == null) {
|
|
|
|
LOGGER.debug("Missing certificate of {}. Skip.", Long.toHexString(keyId));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-12-13 12:27:32 +01:00
|
|
|
// Make sure that the recipient key is encryption capable and non-expired
|
|
|
|
KeyRingInfo info = new KeyRingInfo(secretKeys);
|
|
|
|
List<PGPPublicKey> encryptionSubkeys = info.getEncryptionSubkeys(EncryptionPurpose.ANY);
|
|
|
|
|
|
|
|
PGPSecretKey secretKey = null;
|
|
|
|
for (PGPPublicKey pubkey : encryptionSubkeys) {
|
|
|
|
if (pubkey.getKeyID() == keyId) {
|
|
|
|
secretKey = secretKeys.getSecretKey(keyId);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (secretKey == null) {
|
|
|
|
LOGGER.debug("Key " + Long.toHexString(keyId) + " is not valid or not capable for decryption.");
|
2021-12-13 12:43:08 +01:00
|
|
|
} else {
|
2021-12-28 13:32:50 +01:00
|
|
|
privateKey = tryPublicKeyDecryption(secretKeys, secretKey, publicKeyEncryptedData,
|
|
|
|
postponedDueToMissingPassphrase, true);
|
2021-12-13 12:27:32 +01:00
|
|
|
}
|
2021-09-15 16:33:03 +02:00
|
|
|
}
|
|
|
|
if (privateKey == null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
decryptionKey = privateKey;
|
|
|
|
encryptedSessionKey = publicKeyEncryptedData;
|
2021-10-27 13:09:39 +02:00
|
|
|
break;
|
2021-09-15 16:33:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Try postponed keys with missing passphrases (will cause missing passphrase callbacks to fire)
|
|
|
|
if (encryptedSessionKey == null) {
|
2021-10-18 16:01:19 +02:00
|
|
|
|
|
|
|
if (options.getMissingKeyPassphraseStrategy() == MissingKeyPassphraseStrategy.THROW_EXCEPTION) {
|
|
|
|
// Non-interactive mode: Throw an exception with all locked decryption keys
|
|
|
|
Set<SubkeyIdentifier> keyIds = new HashSet<>();
|
|
|
|
for (Tuple<SubkeyIdentifier, ?> k : postponedDueToMissingPassphrase) {
|
|
|
|
keyIds.add(k.getA());
|
2021-09-15 16:33:03 +02:00
|
|
|
}
|
2021-10-19 18:13:23 +02:00
|
|
|
if (!keyIds.isEmpty()) {
|
|
|
|
throw new MissingPassphraseException(keyIds);
|
|
|
|
}
|
2021-10-18 16:01:19 +02:00
|
|
|
}
|
|
|
|
else if (options.getMissingKeyPassphraseStrategy() == MissingKeyPassphraseStrategy.INTERACTIVE) {
|
|
|
|
// Interactive mode: Fire protector callbacks to get passphrases interactively
|
|
|
|
for (Tuple<SubkeyIdentifier, PGPPublicKeyEncryptedData> missingPassphrases : postponedDueToMissingPassphrase) {
|
|
|
|
SubkeyIdentifier keyId = missingPassphrases.getA();
|
|
|
|
PGPPublicKeyEncryptedData publicKeyEncryptedData = missingPassphrases.getB();
|
|
|
|
PGPSecretKeyRing secretKeys = findDecryptionKeyRing(keyId.getKeyId());
|
|
|
|
PGPSecretKey secretKey = secretKeys.getSecretKey(keyId.getSubkeyId());
|
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
PGPPrivateKey privateKey = tryPublicKeyDecryption(secretKeys, secretKey, publicKeyEncryptedData,
|
|
|
|
postponedDueToMissingPassphrase, false);
|
2021-10-18 16:01:19 +02:00
|
|
|
if (privateKey == null) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-15 16:33:03 +02:00
|
|
|
|
2021-10-18 16:01:19 +02:00
|
|
|
decryptionKey = privateKey;
|
|
|
|
encryptedSessionKey = publicKeyEncryptedData;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw new IllegalStateException("Invalid PostponedKeysStrategy set in consumer options.");
|
2021-09-15 16:33:03 +02:00
|
|
|
}
|
2021-10-18 16:01:19 +02:00
|
|
|
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
2021-09-15 16:33:03 +02:00
|
|
|
|
2021-06-15 17:08:40 +02:00
|
|
|
return decryptWith(encryptedSessionKey, decryptionKey);
|
|
|
|
}
|
2018-06-06 18:46:41 +02:00
|
|
|
|
2021-09-15 16:33:03 +02:00
|
|
|
/**
|
|
|
|
* Try decryption of the provided public-key-encrypted-data using the given secret key.
|
|
|
|
* If the secret key is encrypted and the secret key protector does not have a passphrase available and the boolean
|
|
|
|
* postponeIfMissingPassphrase is true, data decryption is postponed by pushing a tuple of the encrypted data decryption key
|
|
|
|
* identifier to the postponed list.
|
|
|
|
*
|
|
|
|
* This method only returns a non-null private key, if the private key is able to decrypt the message successfully.
|
|
|
|
*
|
|
|
|
* @param secretKeys secret key ring
|
|
|
|
* @param secretKey secret key
|
|
|
|
* @param publicKeyEncryptedData encrypted data which is tried to decrypt using the secret key
|
|
|
|
* @param postponed list of postponed decryptions due to missing secret key passphrases
|
|
|
|
* @param postponeIfMissingPassphrase flag to specify whether missing secret key passphrases should result in postponed decryption
|
|
|
|
* @return private key if decryption is successful, null if decryption is unsuccessful or postponed
|
|
|
|
*
|
|
|
|
* @throws PGPException in case of an OpenPGP error
|
|
|
|
*/
|
|
|
|
private PGPPrivateKey tryPublicKeyDecryption(
|
|
|
|
PGPSecretKeyRing secretKeys,
|
|
|
|
PGPSecretKey secretKey,
|
|
|
|
PGPPublicKeyEncryptedData publicKeyEncryptedData,
|
|
|
|
List<Tuple<SubkeyIdentifier, PGPPublicKeyEncryptedData>> postponed,
|
|
|
|
boolean postponeIfMissingPassphrase) throws PGPException {
|
|
|
|
SecretKeyRingProtector protector = options.getSecretKeyProtector(secretKeys);
|
|
|
|
|
|
|
|
if (postponeIfMissingPassphrase && !protector.hasPassphraseFor(secretKey.getKeyID())) {
|
|
|
|
// Postpone decryption with key with missing passphrase
|
|
|
|
SubkeyIdentifier identifier = new SubkeyIdentifier(secretKeys, secretKey.getKeyID());
|
|
|
|
postponed.add(new Tuple<>(identifier, publicKeyEncryptedData));
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(
|
|
|
|
secretKey, protector.getDecryptor(secretKey.getKeyID()));
|
|
|
|
|
|
|
|
// test if we have the right private key
|
|
|
|
PublicKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance()
|
|
|
|
.getPublicKeyDataDecryptorFactory(privateKey);
|
|
|
|
try {
|
|
|
|
publicKeyEncryptedData.getSymmetricAlgorithm(decryptorFactory); // will only succeed if we have the right secret key
|
|
|
|
LOGGER.debug("Found correct decryption key {}.", Long.toHexString(secretKey.getKeyID()));
|
|
|
|
resultBuilder.setDecryptionKey(new SubkeyIdentifier(secretKeys, privateKey.getKeyID()));
|
|
|
|
return privateKey;
|
|
|
|
} catch (PGPException | ClassCastException e) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:08:40 +02:00
|
|
|
private InputStream decryptWith(PGPPublicKeyEncryptedData encryptedSessionKey, PGPPrivateKey decryptionKey)
|
|
|
|
throws PGPException {
|
2021-09-15 16:33:03 +02:00
|
|
|
if (decryptionKey == null || encryptedSessionKey == null) {
|
2021-05-28 23:20:25 +02:00
|
|
|
throw new MissingDecryptionMethodException("Decryption failed - No suitable decryption key or passphrase found");
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
|
|
|
|
2021-02-17 21:04:05 +01:00
|
|
|
PublicKeyDataDecryptorFactory dataDecryptor = ImplementationFactory.getInstance()
|
2020-12-27 01:56:18 +01:00
|
|
|
.getPublicKeyDataDecryptorFactory(decryptionKey);
|
2020-12-26 19:04:27 +01:00
|
|
|
|
2021-10-15 14:58:17 +02:00
|
|
|
PGPSessionKey pgpSessionKey = encryptedSessionKey.getSessionKey(dataDecryptor);
|
|
|
|
SessionKey sessionKey = new SessionKey(pgpSessionKey);
|
|
|
|
resultBuilder.setSessionKey(sessionKey);
|
|
|
|
|
|
|
|
SymmetricKeyAlgorithm symmetricKeyAlgorithm = sessionKey.getAlgorithm();
|
2021-02-17 20:07:54 +01:00
|
|
|
if (symmetricKeyAlgorithm == SymmetricKeyAlgorithm.NULL) {
|
2021-08-23 13:29:57 +02:00
|
|
|
LOGGER.debug("Message is unencrypted");
|
2021-05-17 13:47:46 +02:00
|
|
|
} else {
|
2021-08-23 13:29:57 +02:00
|
|
|
LOGGER.debug("Message is encrypted using {}", symmetricKeyAlgorithm);
|
2021-02-17 20:07:54 +01:00
|
|
|
}
|
2021-05-17 13:47:46 +02:00
|
|
|
throwIfAlgorithmIsRejected(symmetricKeyAlgorithm);
|
2018-06-19 17:14:37 +02:00
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
integrityProtectedEncryptedInputStream = new IntegrityProtectedInputStream(
|
|
|
|
encryptedSessionKey.getDataStream(dataDecryptor), encryptedSessionKey, options);
|
2021-09-06 15:14:13 +02:00
|
|
|
return integrityProtectedEncryptedInputStream;
|
2021-05-17 13:47:46 +02:00
|
|
|
}
|
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
private void throwIfAlgorithmIsRejected(SymmetricKeyAlgorithm algorithm)
|
|
|
|
throws UnacceptableAlgorithmException {
|
2021-09-13 19:20:19 +02:00
|
|
|
if (!PGPainless.getPolicy().getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(algorithm)) {
|
2021-05-17 13:47:46 +02:00
|
|
|
throw new UnacceptableAlgorithmException("Data is "
|
2021-12-28 13:32:50 +01:00
|
|
|
+ (algorithm == SymmetricKeyAlgorithm.NULL ?
|
|
|
|
"unencrypted" :
|
|
|
|
"encrypted with symmetric algorithm " + algorithm) + " which is not acceptable as per PGPainless' policy.\n" +
|
2021-05-17 13:47:46 +02:00
|
|
|
"To mark this algorithm as acceptable, use PGPainless.getPolicy().setSymmetricKeyDecryptionAlgorithmPolicy().");
|
2021-02-17 21:04:05 +01:00
|
|
|
}
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
private void initOnePassSignatures(@Nonnull PGPOnePassSignatureList onePassSignatureList)
|
|
|
|
throws PGPException {
|
2018-06-06 18:46:41 +02:00
|
|
|
Iterator<PGPOnePassSignature> iterator = onePassSignatureList.iterator();
|
|
|
|
if (!iterator.hasNext()) {
|
2018-06-19 17:14:37 +02:00
|
|
|
throw new PGPException("Verification failed - No OnePassSignatures found");
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
|
|
|
|
2020-01-10 15:12:04 +01:00
|
|
|
processOnePassSignatures(iterator);
|
|
|
|
}
|
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
private void processOnePassSignatures(Iterator<PGPOnePassSignature> signatures)
|
|
|
|
throws PGPException {
|
2020-01-10 15:12:04 +01:00
|
|
|
while (signatures.hasNext()) {
|
|
|
|
PGPOnePassSignature signature = signatures.next();
|
2020-01-10 17:12:13 +01:00
|
|
|
processOnePassSignature(signature);
|
|
|
|
}
|
|
|
|
}
|
2018-06-06 18:46:41 +02:00
|
|
|
|
2021-12-28 13:32:50 +01:00
|
|
|
private void processOnePassSignature(PGPOnePassSignature signature)
|
|
|
|
throws PGPException {
|
2020-01-10 17:12:13 +01:00
|
|
|
final long keyId = signature.getKeyID();
|
2018-07-02 20:46:27 +02:00
|
|
|
|
2021-09-02 18:01:06 +02:00
|
|
|
LOGGER.debug("Encountered OnePassSignature from {}", Long.toHexString(keyId));
|
2018-07-02 20:46:27 +02:00
|
|
|
|
2020-01-10 17:12:13 +01:00
|
|
|
// Find public key
|
2021-04-26 13:38:12 +02:00
|
|
|
PGPPublicKeyRing verificationKeyRing = findSignatureVerificationKeyRing(keyId);
|
|
|
|
if (verificationKeyRing == null) {
|
2021-10-08 14:03:12 +02:00
|
|
|
onePassSignaturesWithMissingCert.put(keyId, new OnePassSignatureCheck(signature, null));
|
2020-01-10 17:12:13 +01:00
|
|
|
return;
|
|
|
|
}
|
2021-04-26 13:38:12 +02:00
|
|
|
PGPPublicKey verificationKey = verificationKeyRing.getPublicKey(keyId);
|
2018-07-02 20:46:27 +02:00
|
|
|
|
2020-01-10 17:12:13 +01:00
|
|
|
signature.init(verifierBuilderProvider, verificationKey);
|
2021-09-02 18:01:06 +02:00
|
|
|
OnePassSignatureCheck onePassSignature = new OnePassSignatureCheck(signature, verificationKeyRing);
|
|
|
|
onePassSignatureChecks.add(onePassSignature);
|
2020-01-10 17:12:13 +01:00
|
|
|
}
|
2018-07-02 20:46:27 +02:00
|
|
|
|
2021-06-15 17:08:40 +02:00
|
|
|
private PGPSecretKeyRing findDecryptionKeyRing(long keyId) {
|
|
|
|
for (PGPSecretKeyRing key : options.getDecryptionKeys()) {
|
|
|
|
if (key.getSecretKey(keyId) != null) {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:38:12 +02:00
|
|
|
private PGPPublicKeyRing findSignatureVerificationKeyRing(long keyId) {
|
|
|
|
PGPPublicKeyRing verificationKeyRing = null;
|
2021-06-15 17:08:40 +02:00
|
|
|
for (PGPPublicKeyRing publicKeyRing : options.getCertificates()) {
|
2021-04-26 13:38:12 +02:00
|
|
|
PGPPublicKey verificationKey = publicKeyRing.getPublicKey(keyId);
|
2020-01-10 17:12:13 +01:00
|
|
|
if (verificationKey != null) {
|
2021-08-23 13:29:57 +02:00
|
|
|
LOGGER.debug("Found public key {} for signature verification", Long.toHexString(keyId));
|
2021-04-26 13:38:12 +02:00
|
|
|
verificationKeyRing = publicKeyRing;
|
2020-01-10 17:12:13 +01:00
|
|
|
break;
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
2020-01-10 17:12:13 +01:00
|
|
|
}
|
2018-07-02 20:46:27 +02:00
|
|
|
|
2021-06-15 17:08:40 +02:00
|
|
|
if (verificationKeyRing == null && options.getMissingCertificateCallback() != null) {
|
|
|
|
verificationKeyRing = options.getMissingCertificateCallback().onMissingPublicKeyEncountered(keyId);
|
2021-05-29 12:19:12 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:38:12 +02:00
|
|
|
return verificationKeyRing;
|
2018-06-06 18:46:41 +02:00
|
|
|
}
|
|
|
|
}
|