diff --git a/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionStreamFactory.java b/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionStreamFactory.java index f89d5573..56f04549 100644 --- a/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionStreamFactory.java +++ b/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionStreamFactory.java @@ -48,6 +48,7 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; import org.pgpainless.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm; +import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector; public final class DecryptionStreamFactory { @@ -63,7 +64,7 @@ public final class DecryptionStreamFactory { private final PainlessResult.Builder resultBuilder = PainlessResult.getBuilder(); private final PGPContentVerifierBuilderProvider verifierBuilderProvider = new BcPGPContentVerifierBuilderProvider(); private final KeyFingerPrintCalculator fingerCalc = new BcKeyFingerprintCalculator(); - private final Map verifiableOnePassSignatures = new HashMap<>(); + private final Map verifiableOnePassSignatures = new HashMap<>(); private DecryptionStreamFactory(PGPSecretKeyRingCollection decryptionKeys, SecretKeyRingProtector decryptor, @@ -162,7 +163,7 @@ public final class DecryptionStreamFactory { LOGGER.log(LEVEL, "Found respective secret key " + Long.toHexString(keyId)); encryptedSessionKey = encryptedData; decryptionKey = secretKey.extractPrivateKey(decryptionKeyDecryptor.getDecryptor(keyId)); - resultBuilder.setDecryptionKeyId(keyId); + resultBuilder.setDecryptionFingerprint(new OpenPgpV4Fingerprint(secretKey)); } } @@ -198,7 +199,7 @@ public final class DecryptionStreamFactory { while (iterator.hasNext()) { PGPOnePassSignature signature = iterator.next(); final long keyId = signature.getKeyID(); - resultBuilder.addSignatureKeyId(keyId); + resultBuilder.addUnverifiedSignatureKeyId(keyId); LOGGER.log(LEVEL, "Message contains OnePassSignature from " + Long.toHexString(keyId)); @@ -236,7 +237,7 @@ public final class DecryptionStreamFactory { } signature.init(verifierBuilderProvider, verificationKey); - verifiableOnePassSignatures.put(keyId, signature); + verifiableOnePassSignatures.put(new OpenPgpV4Fingerprint(verificationKey), signature); } } } diff --git a/src/main/java/org/pgpainless/pgpainless/decryption_verification/PainlessResult.java b/src/main/java/org/pgpainless/pgpainless/decryption_verification/PainlessResult.java index 9fce0d8f..997550cd 100644 --- a/src/main/java/org/pgpainless/pgpainless/decryption_verification/PainlessResult.java +++ b/src/main/java/org/pgpainless/pgpainless/decryption_verification/PainlessResult.java @@ -19,36 +19,39 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.pgpainless.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm; +import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint; public class PainlessResult { private final Set recipientKeyIds; - private final Long decryptionKeyId; + private final OpenPgpV4Fingerprint decryptionFingerprint; + private final Set unverifiedSignatureKeyIds; + private final Set verifiedSignaturesFingerprints; + private final SymmetricKeyAlgorithm symmetricKeyAlgorithm; private final CompressionAlgorithm compressionAlgorithm; private final boolean integrityProtected; - private final Set signatureKeyIds; - private final Set verifiedSignatureKeyIds; public PainlessResult(Set recipientKeyIds, - Long decryptionKeyId, + OpenPgpV4Fingerprint decryptionFingerprint, SymmetricKeyAlgorithm symmetricKeyAlgorithm, CompressionAlgorithm algorithm, boolean integrityProtected, - Set signatureKeyIds, - Set verifiedSignatureKeyIds) { + Set unverifiedSignatureKeyIds, + Set verifiedSignaturesFingerprints) { this.recipientKeyIds = Collections.unmodifiableSet(recipientKeyIds); - this.decryptionKeyId = decryptionKeyId; + this.decryptionFingerprint = decryptionFingerprint; this.symmetricKeyAlgorithm = symmetricKeyAlgorithm; this.compressionAlgorithm = algorithm; this.integrityProtected = integrityProtected; - this.signatureKeyIds = Collections.unmodifiableSet(signatureKeyIds); - this.verifiedSignatureKeyIds = Collections.unmodifiableSet(verifiedSignatureKeyIds); + this.unverifiedSignatureKeyIds = Collections.unmodifiableSet(unverifiedSignatureKeyIds); + this.verifiedSignaturesFingerprints = Collections.unmodifiableSet(verifiedSignaturesFingerprints); } public Set getRecipientKeyIds() { @@ -59,8 +62,8 @@ public class PainlessResult { return !getRecipientKeyIds().isEmpty(); } - public Long getDecryptionKeyId() { - return decryptionKeyId; + public OpenPgpV4Fingerprint getDecryptionFingerprint() { + return decryptionFingerprint; } public SymmetricKeyAlgorithm getSymmetricKeyAlgorithm() { @@ -75,26 +78,26 @@ public class PainlessResult { return integrityProtected; } - public Set getAllSignatureKeyIds() { - return signatureKeyIds; + public Set getAllSignatureKeyFingerprints() { + return unverifiedSignatureKeyIds; } public boolean isSigned() { - return !signatureKeyIds.isEmpty(); + return !unverifiedSignatureKeyIds.isEmpty(); } - public Set getVerifiedSignatureKeyIds() { - return verifiedSignatureKeyIds; + public Set getVerifiedSignaturesFingerprints() { + return verifiedSignaturesFingerprints; } public boolean isVerified() { - return !verifiedSignatureKeyIds.isEmpty(); + return !verifiedSignaturesFingerprints.isEmpty(); } - public boolean containsVerifiedSignatureFrom(PGPPublicKeyRing publicKeys) { + public boolean containsVerifiedSignatureFrom(PGPPublicKeyRing publicKeys) throws PGPException { for (PGPPublicKey key : publicKeys) { - long id = key.getKeyID(); - if (verifiedSignatureKeyIds.contains(id)) { + OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(key); + if (verifiedSignaturesFingerprints.contains(fingerprint)) { return true; } } @@ -107,21 +110,21 @@ public class PainlessResult { static class Builder { - private final Set recipientKeyIds = new HashSet<>(); - private Long decryptionKeyId; + private final Set recipientFingerprints = new HashSet<>(); + private OpenPgpV4Fingerprint decryptionFingerprint; + private final Set unverifiedSignatureKeyIds = new HashSet<>(); + private final Set verifiedSignatureFingerprints = new HashSet<>(); private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL; private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED; private boolean integrityProtected = false; - private final Set signatureKeyIds = new HashSet<>(); - private final Set verifiedSignatureKeyIds = new HashSet<>(); - public Builder addRecipientKeyId(long id) { - this.recipientKeyIds.add(id); + public Builder addRecipientKeyId(Long keyId) { + this.recipientFingerprints.add(keyId); return this; } - public Builder setDecryptionKeyId(long id) { - this.decryptionKeyId = id; + public Builder setDecryptionFingerprint(OpenPgpV4Fingerprint fingerprint) { + this.decryptionFingerprint = fingerprint; return this; } @@ -130,13 +133,13 @@ public class PainlessResult { return this; } - public Builder addSignatureKeyId(long id) { - this.signatureKeyIds.add(id); + public Builder addUnverifiedSignatureKeyId(Long keyId) { + this.unverifiedSignatureKeyIds.add(keyId); return this; } - public Builder addVerifiedSignatureKeyId(long id) { - this.verifiedSignatureKeyIds.add(id); + public Builder addVerifiedSignatureFingerprint(OpenPgpV4Fingerprint fingerprint) { + this.verifiedSignatureFingerprints.add(fingerprint); return this; } @@ -151,7 +154,7 @@ public class PainlessResult { } public PainlessResult build() { - return new PainlessResult(recipientKeyIds, decryptionKeyId, symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected, signatureKeyIds, verifiedSignatureKeyIds); + return new PainlessResult(recipientFingerprints, decryptionFingerprint, symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected, unverifiedSignatureKeyIds, verifiedSignatureFingerprints); } } } diff --git a/src/main/java/org/pgpainless/pgpainless/decryption_verification/SignatureVerifyingInputStream.java b/src/main/java/org/pgpainless/pgpainless/decryption_verification/SignatureVerifyingInputStream.java index 19997cc7..3343ac00 100644 --- a/src/main/java/org/pgpainless/pgpainless/decryption_verification/SignatureVerifyingInputStream.java +++ b/src/main/java/org/pgpainless/pgpainless/decryption_verification/SignatureVerifyingInputStream.java @@ -28,6 +28,7 @@ import org.bouncycastle.openpgp.PGPObjectFactory; import org.bouncycastle.openpgp.PGPOnePassSignature; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignatureList; +import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint; public class SignatureVerifyingInputStream extends FilterInputStream { @@ -35,14 +36,14 @@ public class SignatureVerifyingInputStream extends FilterInputStream { private static final Level LEVEL = Level.INFO; private final PGPObjectFactory objectFactory; - private final Map onePassSignatures; + private final Map onePassSignatures; private final PainlessResult.Builder resultBuilder; private boolean validated = false; protected SignatureVerifyingInputStream(InputStream inputStream, PGPObjectFactory objectFactory, - Map onePassSignatures, + Map onePassSignatures, PainlessResult.Builder resultBuilder) { super(inputStream); this.objectFactory = objectFactory; @@ -94,16 +95,25 @@ public class SignatureVerifyingInputStream extends FilterInputStream { } for (PGPSignature signature : signatureList) { - PGPOnePassSignature onePassSignature = onePassSignatures.get(signature.getKeyID()); - if (onePassSignature == null) { + OpenPgpV4Fingerprint fingerprint = null; + for (OpenPgpV4Fingerprint f : onePassSignatures.keySet()) { + if (f.getKeyId() == signature.getKeyID()) { + fingerprint = f; + break; + } + } + + PGPOnePassSignature onePassSignature; + if (fingerprint == null || (onePassSignature = onePassSignatures.get(fingerprint)) == null) { LOGGER.log(LEVEL, "Found Signature without respective OnePassSignature packet -> skip"); continue; } + if (!onePassSignature.verify(signature)) { throw new SignatureException("Bad Signature of key " + signature.getKeyID()); } else { LOGGER.log(LEVEL, "Verified signature of key " + Long.toHexString(signature.getKeyID())); - resultBuilder.addVerifiedSignatureKeyId(signature.getKeyID()); + resultBuilder.addVerifiedSignatureFingerprint(fingerprint); } } } catch (PGPException | SignatureException e) { diff --git a/src/main/java/org/pgpainless/pgpainless/key/OpenPgpV4Fingerprint.java b/src/main/java/org/pgpainless/pgpainless/key/OpenPgpV4Fingerprint.java index 1158c1a5..8e453821 100644 --- a/src/main/java/org/pgpainless/pgpainless/key/OpenPgpV4Fingerprint.java +++ b/src/main/java/org/pgpainless/pgpainless/key/OpenPgpV4Fingerprint.java @@ -1,5 +1,4 @@ -/** - * +/* * Copyright 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +15,7 @@ */ package org.pgpainless.pgpainless.key; -import javax.xml.bind.DatatypeConverter; +import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Arrays; @@ -93,7 +92,12 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable