mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-27 06:42:05 +01:00
Begin introducing new Decryption API
This commit is contained in:
parent
8fffa3079a
commit
70c4dcd1d2
5 changed files with 293 additions and 63 deletions
|
@ -0,0 +1,188 @@
|
||||||
|
package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
import org.pgpainless.util.Passphrase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for decryption and signature verification.
|
||||||
|
*/
|
||||||
|
public class ConsumerOptions {
|
||||||
|
|
||||||
|
private Date verifyNotBefore;
|
||||||
|
private Date verifyNotAfter;
|
||||||
|
|
||||||
|
// Set of verification keys
|
||||||
|
private Set<PGPPublicKeyRing> certificates = new HashSet<>();
|
||||||
|
private Set<PGPSignature> detachedSignatures = new HashSet<>();
|
||||||
|
private MissingPublicKeyCallback missingCertificateCallback = null;
|
||||||
|
|
||||||
|
// Session key for decryption without passphrase/key
|
||||||
|
private byte[] sessionKey = null;
|
||||||
|
|
||||||
|
private final Map<PGPSecretKeyRing, SecretKeyRingProtector> decryptionKeys = new HashMap<>();
|
||||||
|
private final Set<Passphrase> decryptionPassphrases = new HashSet<>();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consider signatures made before the given timestamp invalid.
|
||||||
|
*
|
||||||
|
* @param timestamp timestamp
|
||||||
|
* @return options
|
||||||
|
*/
|
||||||
|
public ConsumerOptions verifyNotBefore(Date timestamp) {
|
||||||
|
this.verifyNotBefore = timestamp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consider signatures made after the given timestamp invalid.
|
||||||
|
*
|
||||||
|
* @param timestamp timestamp
|
||||||
|
* @return options
|
||||||
|
*/
|
||||||
|
public ConsumerOptions verifyNotAfter(Date timestamp) {
|
||||||
|
this.verifyNotAfter = timestamp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a certificate (public key ring) for signature verification.
|
||||||
|
*
|
||||||
|
* @param verificationCert certificate for signature verification
|
||||||
|
* @return options
|
||||||
|
*/
|
||||||
|
public ConsumerOptions addVerificationCert(PGPPublicKeyRing verificationCert) {
|
||||||
|
this.certificates.add(verificationCert);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a set of certificates (public key rings) for signature verification.
|
||||||
|
*
|
||||||
|
* @param verificationCerts certificates for signature verification
|
||||||
|
* @return options
|
||||||
|
*/
|
||||||
|
public ConsumerOptions addVerificationCerts(PGPPublicKeyRingCollection verificationCerts) {
|
||||||
|
for (PGPPublicKeyRing certificate : verificationCerts) {
|
||||||
|
addVerificationCert(certificate);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a detached signature for the signature verification process.
|
||||||
|
*
|
||||||
|
* @param detachedSignature detached signature
|
||||||
|
* @return options
|
||||||
|
*/
|
||||||
|
public ConsumerOptions addVerificationOfDetachedSignature(PGPSignature detachedSignature) {
|
||||||
|
detachedSignatures.add(detachedSignature);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a callback that's used when a certificate (public key) is missing for signature verification.
|
||||||
|
*
|
||||||
|
* @param callback callback
|
||||||
|
* @return options
|
||||||
|
*/
|
||||||
|
public ConsumerOptions setMissingCertificateCallback(MissingPublicKeyCallback callback) {
|
||||||
|
this.missingCertificateCallback = callback;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt decryption using a session key.
|
||||||
|
*
|
||||||
|
* Note: PGPainless does not yet support decryption with session keys.
|
||||||
|
* TODO: Implement
|
||||||
|
*
|
||||||
|
* @see <a href="https://datatracker.ietf.org/doc/html/rfc4880#section-2.1">RFC4880 on Session Keys</a>
|
||||||
|
*
|
||||||
|
* @param sessionKey session key
|
||||||
|
* @return options
|
||||||
|
*/
|
||||||
|
public ConsumerOptions setSessionKey(@Nonnull byte[] sessionKey) {
|
||||||
|
this.sessionKey = sessionKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a key for message decryption.
|
||||||
|
* The key is expected to be unencrypted.
|
||||||
|
*
|
||||||
|
* @param key unencrypted key
|
||||||
|
* @return options
|
||||||
|
*/
|
||||||
|
public ConsumerOptions addDecryptionKey(@Nonnull PGPSecretKeyRing key) {
|
||||||
|
return addDecryptionKey(key, SecretKeyRingProtector.unprotectedKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a key for message decryption. If the key is encrypted, the {@link SecretKeyRingProtector} is used to decrypt it
|
||||||
|
* when needed.
|
||||||
|
*
|
||||||
|
* @param key key
|
||||||
|
* @param keyRingProtector protector for the secret key
|
||||||
|
* @return options
|
||||||
|
*/
|
||||||
|
public ConsumerOptions addDecryptionKey(@Nonnull PGPSecretKeyRing key, @Nonnull SecretKeyRingProtector keyRingProtector) {
|
||||||
|
decryptionKeys.put(key, keyRingProtector);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a passphrase for message decryption.
|
||||||
|
*
|
||||||
|
* @param passphrase passphrase
|
||||||
|
* @return options
|
||||||
|
*/
|
||||||
|
public ConsumerOptions addDecryptionPassphrase(@Nonnull Passphrase passphrase) {
|
||||||
|
decryptionPassphrases.add(passphrase);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<PGPSecretKeyRing> getDecryptionKeys() {
|
||||||
|
return Collections.unmodifiableSet(decryptionKeys.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Passphrase> getDecryptionPassphrases() {
|
||||||
|
return Collections.unmodifiableSet(decryptionPassphrases);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<PGPPublicKeyRing> getCertificates() {
|
||||||
|
return Collections.unmodifiableSet(certificates);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MissingPublicKeyCallback getMissingCertificateCallback() {
|
||||||
|
return missingCertificateCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SecretKeyRingProtector getSecretKeyProtector(PGPSecretKeyRing decryptionKeyRing) {
|
||||||
|
return decryptionKeys.get(decryptionKeyRing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<PGPSignature> getDetachedSignatures() {
|
||||||
|
return Collections.unmodifiableSet(detachedSignatures);
|
||||||
|
}
|
||||||
|
}
|
|
@ -63,6 +63,15 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
||||||
|
|
||||||
class DecryptWithImpl implements DecryptWith {
|
class DecryptWithImpl implements DecryptWith {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DecryptionStream withOptions(ConsumerOptions consumerOptions) throws PGPException, IOException {
|
||||||
|
if (consumerOptions == null) {
|
||||||
|
throw new IllegalArgumentException("Consumer options cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecryptionStreamFactory.create(inputStream, consumerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Verify decryptWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRingCollection secretKeyRings) {
|
public Verify decryptWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRingCollection secretKeyRings) {
|
||||||
DecryptionBuilder.this.decryptionKeys = secretKeyRings;
|
DecryptionBuilder.this.decryptionKeys = secretKeyRings;
|
||||||
|
@ -219,8 +228,27 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DecryptionStream build() throws IOException, PGPException {
|
public DecryptionStream build() throws IOException, PGPException {
|
||||||
return DecryptionStreamFactory.create(inputStream, decryptionKeys, decryptionKeyDecryptor,
|
ConsumerOptions options = new ConsumerOptions();
|
||||||
decryptionPassphrase, detachedSignatures, verificationKeys, missingPublicKeyCallback);
|
|
||||||
|
for (PGPSecretKeyRing decryptionKey : (decryptionKeys != null ? decryptionKeys : Collections.<PGPSecretKeyRing>emptyList())) {
|
||||||
|
options.addDecryptionKey(decryptionKey, decryptionKeyDecryptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PGPPublicKeyRing certificate : (verificationKeys != null ? verificationKeys : Collections.<PGPPublicKeyRing>emptyList())) {
|
||||||
|
options.addVerificationCert(certificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PGPSignature detachedSignature : (detachedSignatures != null ? detachedSignatures : Collections.<PGPSignature>emptyList())) {
|
||||||
|
options.addVerificationOfDetachedSignature(detachedSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.setMissingCertificateCallback(missingPublicKeyCallback);
|
||||||
|
|
||||||
|
if (decryptionPassphrase != null) {
|
||||||
|
options.addDecryptionPassphrase(decryptionPassphrase);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecryptionStreamFactory.create(inputStream, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,8 @@ public interface DecryptionBuilderInterface {
|
||||||
|
|
||||||
interface DecryptWith {
|
interface DecryptWith {
|
||||||
|
|
||||||
|
DecryptionStream withOptions(ConsumerOptions consumerOptions) throws PGPException, IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypt the encrypted data using the secret keys found in the provided {@link PGPSecretKeyRingCollection}.
|
* Decrypt the encrypted data using the secret keys found in the provided {@link PGPSecretKeyRingCollection}.
|
||||||
* Here it is assumed that the secret keys are not password protected.
|
* Here it is assumed that the secret keys are not password protected.
|
||||||
|
|
|
@ -54,6 +54,7 @@ import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
||||||
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
|
import org.pgpainless.algorithm.EncryptionPurpose;
|
||||||
import org.pgpainless.algorithm.StreamEncoding;
|
import org.pgpainless.algorithm.StreamEncoding;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
import org.pgpainless.exception.MessageNotIntegrityProtectedException;
|
import org.pgpainless.exception.MessageNotIntegrityProtectedException;
|
||||||
|
@ -62,6 +63,7 @@ import org.pgpainless.exception.UnacceptableAlgorithmException;
|
||||||
import org.pgpainless.implementation.ImplementationFactory;
|
import org.pgpainless.implementation.ImplementationFactory;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||||
import org.pgpainless.signature.DetachedSignature;
|
import org.pgpainless.signature.DetachedSignature;
|
||||||
|
@ -75,11 +77,7 @@ public final class DecryptionStreamFactory {
|
||||||
private static final Level LEVEL = Level.FINE;
|
private static final Level LEVEL = Level.FINE;
|
||||||
private static final int MAX_RECURSION_DEPTH = 16;
|
private static final int MAX_RECURSION_DEPTH = 16;
|
||||||
|
|
||||||
private final PGPSecretKeyRingCollection decryptionKeys;
|
private final ConsumerOptions options;
|
||||||
private final SecretKeyRingProtector decryptionKeyDecryptor;
|
|
||||||
private final Passphrase decryptionPassphrase;
|
|
||||||
private final Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
|
|
||||||
private final MissingPublicKeyCallback missingPublicKeyCallback;
|
|
||||||
|
|
||||||
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
||||||
private static final PGPContentVerifierBuilderProvider verifierBuilderProvider = ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider();
|
private static final PGPContentVerifierBuilderProvider verifierBuilderProvider = ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider();
|
||||||
|
@ -87,33 +85,16 @@ public final class DecryptionStreamFactory {
|
||||||
private final Map<OpenPgpV4Fingerprint, OnePassSignature> verifiableOnePassSignatures = new HashMap<>();
|
private final Map<OpenPgpV4Fingerprint, OnePassSignature> verifiableOnePassSignatures = new HashMap<>();
|
||||||
private final List<IntegrityProtectedInputStream> integrityProtectedStreams = new ArrayList<>();
|
private final List<IntegrityProtectedInputStream> integrityProtectedStreams = new ArrayList<>();
|
||||||
|
|
||||||
private DecryptionStreamFactory(@Nullable PGPSecretKeyRingCollection decryptionKeys,
|
public DecryptionStreamFactory(ConsumerOptions options) {
|
||||||
@Nullable SecretKeyRingProtector decryptor,
|
this.options = options;
|
||||||
@Nullable Passphrase decryptionPassphrase,
|
|
||||||
@Nullable Set<PGPPublicKeyRing> verificationKeys,
|
|
||||||
@Nullable MissingPublicKeyCallback missingPublicKeyCallback) {
|
|
||||||
this.decryptionKeys = decryptionKeys;
|
|
||||||
this.decryptionKeyDecryptor = decryptor;
|
|
||||||
this.decryptionPassphrase = decryptionPassphrase;
|
|
||||||
this.verificationKeys.addAll(verificationKeys != null ? verificationKeys : Collections.emptyList());
|
|
||||||
this.missingPublicKeyCallback = missingPublicKeyCallback;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DecryptionStream create(@Nonnull InputStream inputStream,
|
public static DecryptionStream create(@Nonnull InputStream inputStream,
|
||||||
@Nullable PGPSecretKeyRingCollection decryptionKeys,
|
@Nonnull ConsumerOptions options) throws PGPException, IOException {
|
||||||
@Nullable SecretKeyRingProtector decryptor,
|
InputStream pgpInputStream = inputStream;
|
||||||
@Nullable Passphrase decryptionPassphrase,
|
DecryptionStreamFactory factory = new DecryptionStreamFactory(options);
|
||||||
@Nullable List<PGPSignature> detachedSignatures,
|
|
||||||
@Nullable Set<PGPPublicKeyRing> verificationKeys,
|
|
||||||
@Nullable MissingPublicKeyCallback missingPublicKeyCallback)
|
|
||||||
throws IOException, PGPException {
|
|
||||||
InputStream pgpInputStream;
|
|
||||||
DecryptionStreamFactory factory = new DecryptionStreamFactory(decryptionKeys, decryptor,
|
|
||||||
decryptionPassphrase, verificationKeys, missingPublicKeyCallback);
|
|
||||||
|
|
||||||
if (detachedSignatures != null) {
|
for (PGPSignature signature : options.getDetachedSignatures()) {
|
||||||
pgpInputStream = inputStream;
|
|
||||||
for (PGPSignature signature : detachedSignatures) {
|
|
||||||
PGPPublicKeyRing signingKeyRing = factory.findSignatureVerificationKeyRing(signature.getKeyID());
|
PGPPublicKeyRing signingKeyRing = factory.findSignatureVerificationKeyRing(signature.getKeyID());
|
||||||
if (signingKeyRing == null) {
|
if (signingKeyRing == null) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -123,11 +104,11 @@ public final class DecryptionStreamFactory {
|
||||||
factory.resultBuilder.addDetachedSignature(
|
factory.resultBuilder.addDetachedSignature(
|
||||||
new DetachedSignature(signature, signingKeyRing, new SubkeyIdentifier(signingKeyRing, signature.getKeyID())));
|
new DetachedSignature(signature, signingKeyRing, new SubkeyIdentifier(signingKeyRing, signature.getKeyID())));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
PGPObjectFactory objectFactory = new PGPObjectFactory(
|
PGPObjectFactory objectFactory = new PGPObjectFactory(
|
||||||
PGPUtil.getDecoderStream(inputStream), keyFingerprintCalculator);
|
PGPUtil.getDecoderStream(inputStream), keyFingerprintCalculator);
|
||||||
pgpInputStream = factory.processPGPPackets(objectFactory, 1);
|
pgpInputStream = factory.processPGPPackets(objectFactory, 1);
|
||||||
}
|
|
||||||
return new DecryptionStream(pgpInputStream, factory.resultBuilder, factory.integrityProtectedStreams);
|
return new DecryptionStream(pgpInputStream, factory.resultBuilder, factory.integrityProtectedStreams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,50 +191,68 @@ public final class DecryptionStreamFactory {
|
||||||
while (encryptedDataIterator.hasNext()) {
|
while (encryptedDataIterator.hasNext()) {
|
||||||
PGPEncryptedData encryptedData = encryptedDataIterator.next();
|
PGPEncryptedData encryptedData = encryptedDataIterator.next();
|
||||||
|
|
||||||
|
// TODO: Can we just skip non-integrity-protected packages?
|
||||||
if (!encryptedData.isIntegrityProtected()) {
|
if (!encryptedData.isIntegrityProtected()) {
|
||||||
throw new MessageNotIntegrityProtectedException();
|
throw new MessageNotIntegrityProtectedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data is passphrase encrypted
|
// Data is passphrase encrypted
|
||||||
if (encryptedData instanceof PGPPBEEncryptedData) {
|
if (encryptedData instanceof PGPPBEEncryptedData) {
|
||||||
PGPPBEEncryptedData pbeEncryptedData = (PGPPBEEncryptedData) encryptedData;
|
PGPPBEEncryptedData pbeEncryptedData = (PGPPBEEncryptedData) encryptedData;
|
||||||
if (decryptionPassphrase != null) {
|
for (Passphrase passphrase : options.getDecryptionPassphrases()) {
|
||||||
PBEDataDecryptorFactory passphraseDecryptor = ImplementationFactory.getInstance()
|
PBEDataDecryptorFactory passphraseDecryptor = ImplementationFactory.getInstance()
|
||||||
.getPBEDataDecryptorFactory(decryptionPassphrase);
|
.getPBEDataDecryptorFactory(passphrase);
|
||||||
|
try {
|
||||||
|
InputStream decryptedDataStream = pbeEncryptedData.getDataStream(passphraseDecryptor);
|
||||||
|
|
||||||
SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.fromId(
|
SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.fromId(
|
||||||
pbeEncryptedData.getSymmetricAlgorithm(passphraseDecryptor));
|
pbeEncryptedData.getSymmetricAlgorithm(passphraseDecryptor));
|
||||||
throwIfAlgorithmIsRejected(symmetricKeyAlgorithm);
|
throwIfAlgorithmIsRejected(symmetricKeyAlgorithm);
|
||||||
resultBuilder.setSymmetricKeyAlgorithm(symmetricKeyAlgorithm);
|
resultBuilder.setSymmetricKeyAlgorithm(symmetricKeyAlgorithm);
|
||||||
|
|
||||||
try {
|
return decryptedDataStream;
|
||||||
return pbeEncryptedData.getDataStream(passphraseDecryptor);
|
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
LOGGER.log(LEVEL, "Probable passphrase mismatch, skip PBE encrypted data block", e);
|
LOGGER.log(LEVEL, "Probable passphrase mismatch, skip PBE encrypted data block", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// data is public key encrypted
|
// data is public key encrypted
|
||||||
else if (encryptedData instanceof PGPPublicKeyEncryptedData) {
|
else if (encryptedData instanceof PGPPublicKeyEncryptedData) {
|
||||||
|
if (options.getDecryptionKeys().isEmpty()) {
|
||||||
|
|
||||||
|
}
|
||||||
PGPPublicKeyEncryptedData publicKeyEncryptedData = (PGPPublicKeyEncryptedData) encryptedData;
|
PGPPublicKeyEncryptedData publicKeyEncryptedData = (PGPPublicKeyEncryptedData) encryptedData;
|
||||||
long keyId = publicKeyEncryptedData.getKeyID();
|
long keyId = publicKeyEncryptedData.getKeyID();
|
||||||
if (decryptionKeys != null) {
|
if (!options.getDecryptionKeys().isEmpty()) {
|
||||||
// Known key id
|
// Known key id
|
||||||
if (keyId != 0) {
|
if (keyId != 0) {
|
||||||
LOGGER.log(LEVEL, "PGPEncryptedData is encrypted for key " + Long.toHexString(keyId));
|
LOGGER.log(LEVEL, "PGPEncryptedData is encrypted for key " + Long.toHexString(keyId));
|
||||||
resultBuilder.addRecipientKeyId(keyId);
|
resultBuilder.addRecipientKeyId(keyId);
|
||||||
PGPSecretKey secretKey = decryptionKeys.getSecretKey(keyId);
|
PGPSecretKeyRing decryptionKeyRing = findDecryptionKeyRing(keyId);
|
||||||
if (secretKey != null) {
|
if (decryptionKeyRing != null) {
|
||||||
|
PGPSecretKey secretKey = decryptionKeyRing.getSecretKey(keyId);
|
||||||
LOGGER.log(LEVEL, "Found respective secret key " + Long.toHexString(keyId));
|
LOGGER.log(LEVEL, "Found respective secret key " + Long.toHexString(keyId));
|
||||||
// Watch out! This assignment is possibly done multiple times.
|
// Watch out! This assignment is possibly done multiple times.
|
||||||
encryptedSessionKey = publicKeyEncryptedData;
|
encryptedSessionKey = publicKeyEncryptedData;
|
||||||
decryptionKey = UnlockSecretKey.unlockSecretKey(secretKey, decryptionKeyDecryptor);
|
decryptionKey = UnlockSecretKey.unlockSecretKey(secretKey, options.getSecretKeyProtector(decryptionKeyRing));
|
||||||
resultBuilder.setDecryptionFingerprint(new OpenPgpV4Fingerprint(secretKey));
|
resultBuilder.setDecryptionFingerprint(new OpenPgpV4Fingerprint(secretKey));
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
// Hidden recipient
|
// Hidden recipient
|
||||||
|
else {
|
||||||
LOGGER.log(LEVEL, "Hidden recipient detected. Try to decrypt with all available secret keys.");
|
LOGGER.log(LEVEL, "Hidden recipient detected. Try to decrypt with all available secret keys.");
|
||||||
outerloop: for (PGPSecretKeyRing ring : decryptionKeys) {
|
outerloop: for (PGPSecretKeyRing ring : options.getDecryptionKeys()) {
|
||||||
for (PGPSecretKey key : ring) {
|
KeyRingInfo info = new KeyRingInfo(ring);
|
||||||
PGPPrivateKey privateKey = key.extractPrivateKey(decryptionKeyDecryptor.getDecryptor(key.getKeyID()));
|
List<PGPPublicKey> encryptionSubkeys = info.getEncryptionSubkeys(EncryptionPurpose.STORAGE_AND_COMMUNICATIONS);
|
||||||
|
for (PGPPublicKey pubkey : encryptionSubkeys) {
|
||||||
|
PGPSecretKey key = ring.getSecretKey(pubkey.getKeyID());
|
||||||
|
if (key == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(key, options.getSecretKeyProtector(ring).getDecryptor(key.getKeyID()));
|
||||||
PublicKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance().getPublicKeyDataDecryptorFactory(privateKey);
|
PublicKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance().getPublicKeyDataDecryptorFactory(privateKey);
|
||||||
try {
|
try {
|
||||||
publicKeyEncryptedData.getSymmetricAlgorithm(decryptorFactory); // will only succeed if we have the right secret key
|
publicKeyEncryptedData.getSymmetricAlgorithm(decryptorFactory); // will only succeed if we have the right secret key
|
||||||
|
@ -271,7 +270,11 @@ public final class DecryptionStreamFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return decryptWith(encryptedSessionKey, decryptionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream decryptWith(PGPPublicKeyEncryptedData encryptedSessionKey, PGPPrivateKey decryptionKey)
|
||||||
|
throws PGPException {
|
||||||
if (decryptionKey == null) {
|
if (decryptionKey == null) {
|
||||||
throw new MissingDecryptionMethodException("Decryption failed - No suitable decryption key or passphrase found");
|
throw new MissingDecryptionMethodException("Decryption failed - No suitable decryption key or passphrase found");
|
||||||
}
|
}
|
||||||
|
@ -339,9 +342,18 @@ public final class DecryptionStreamFactory {
|
||||||
verifiableOnePassSignatures.put(fingerprint, onePassSignature);
|
verifiableOnePassSignatures.put(fingerprint, onePassSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PGPSecretKeyRing findDecryptionKeyRing(long keyId) {
|
||||||
|
for (PGPSecretKeyRing key : options.getDecryptionKeys()) {
|
||||||
|
if (key.getSecretKey(keyId) != null) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private PGPPublicKeyRing findSignatureVerificationKeyRing(long keyId) {
|
private PGPPublicKeyRing findSignatureVerificationKeyRing(long keyId) {
|
||||||
PGPPublicKeyRing verificationKeyRing = null;
|
PGPPublicKeyRing verificationKeyRing = null;
|
||||||
for (PGPPublicKeyRing publicKeyRing : verificationKeys) {
|
for (PGPPublicKeyRing publicKeyRing : options.getCertificates()) {
|
||||||
PGPPublicKey verificationKey = publicKeyRing.getPublicKey(keyId);
|
PGPPublicKey verificationKey = publicKeyRing.getPublicKey(keyId);
|
||||||
if (verificationKey != null) {
|
if (verificationKey != null) {
|
||||||
LOGGER.log(LEVEL, "Found public key " + Long.toHexString(keyId) + " for signature verification");
|
LOGGER.log(LEVEL, "Found public key " + Long.toHexString(keyId) + " for signature verification");
|
||||||
|
@ -350,8 +362,8 @@ public final class DecryptionStreamFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verificationKeyRing == null && missingPublicKeyCallback != null) {
|
if (verificationKeyRing == null && options.getMissingCertificateCallback() != null) {
|
||||||
verificationKeyRing = missingPublicKeyCallback.onMissingPublicKeyEncountered(keyId);
|
verificationKeyRing = options.getMissingCertificateCallback().onMissingPublicKeyEncountered(keyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return verificationKeyRing;
|
return verificationKeyRing;
|
||||||
|
|
|
@ -109,7 +109,7 @@ public class SymmetricEncryptionTest {
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("org.pgpainless.util.TestUtil#provideImplementationFactories")
|
@MethodSource("org.pgpainless.util.TestUtil#provideImplementationFactories")
|
||||||
public void testMissmatchPassphraseFails(ImplementationFactory implementationFactory) throws IOException, PGPException {
|
public void testMismatchPassphraseFails(ImplementationFactory implementationFactory) throws IOException, PGPException {
|
||||||
ImplementationFactory.setFactoryImplementation(implementationFactory);
|
ImplementationFactory.setFactoryImplementation(implementationFactory);
|
||||||
|
|
||||||
byte[] bytes = new byte[5000];
|
byte[] bytes = new byte[5000];
|
||||||
|
|
Loading…
Reference in a new issue