1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-12-23 11:27:57 +01:00

Delete SelectSignatureFromKey class

This commit is contained in:
Paul Schaub 2021-05-28 22:21:03 +02:00
parent b38bcf042a
commit a23f2c4401
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
3 changed files with 0 additions and 769 deletions

View file

@ -1,594 +0,0 @@
/*
* 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;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyRing;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
/**
* Utility class to select signatures from keys based on certain criteria.
* This abstract class provides a method {@link #accept(PGPSignature, PGPPublicKey, PGPKeyRing)} which shall only
* return true if the provided signature is acceptable regarding the implementations selection criteria.
*
* The idea is to create an implementation of the class for each criterion, so that those criteria can be
* composed to create complex validity checks.
*/
public abstract class SelectSignatureFromKey {
private static final Logger LOGGER = Logger.getLogger(SelectSignatureFromKey.class.getName());
public abstract boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing);
public List<PGPSignature> select(List<PGPSignature> signatures, PGPPublicKey key, PGPKeyRing keyRing) {
List<PGPSignature> selected = new ArrayList<>();
for (PGPSignature signature : signatures) {
if (accept(signature, key, keyRing)) {
selected.add(signature);
}
}
return selected;
}
/**
* Criterion that checks if the signature is valid at the validation date.
* A signature is not valid if it was created after the validation date, or if it is expired at the validation date.
*
* creationTime &le; validationDate &lt; expirationDate.
*
* @param validationDate validation date
* @return criterion implementation
*/
public static SelectSignatureFromKey isValidAt(Date validationDate) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
Date expirationDate = SignatureUtils.getSignatureExpirationDate(signature);
return !signature.getCreationTime().after(validationDate) && (expirationDate == null || expirationDate.after(validationDate));
}
};
}
/**
* Criterion that checks if the provided signature is a valid subkey binding signature.
*
* A signature is only a valid subkey binding signature if it is of type {@link SignatureType#SUBKEY_BINDING},
* if it was created by the primary key, and - if the subkey is capable of signing - it contains a valid
* primary key binding signature.
*
* @param primaryKey primary key
* @param subkey subkey
* @return criterion to validate binding signatures
*/
public static SelectSignatureFromKey isValidSubkeyBindingSignature(PGPPublicKey primaryKey, PGPPublicKey subkey) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
if (!isOfType(SignatureType.SUBKEY_BINDING).accept(signature, key, keyRing)) {
return false;
}
if (signature.getKeyID() != primaryKey.getKeyID()) {
return false;
}
if (!isSigNotExpired().accept(signature, subkey, keyRing)) {
LOGGER.log(Level.INFO, "Subkey binding signature expired.");
return false;
}
// Check signature correctness
try {
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), primaryKey);
boolean subkeyBindingSigValid = signature.verifyCertification(primaryKey, subkey);
if (!subkeyBindingSigValid) {
return false;
}
} catch (PGPException e) {
LOGGER.log(Level.INFO, "Verification of subkey binding signature failed.", e);
return false;
}
List<KeyFlag> flags = KeyFlag.fromBitmask(signature.getHashedSubPackets().getKeyFlags());
boolean isSigningKey = flags.contains(KeyFlag.SIGN_DATA) || flags.contains(KeyFlag.CERTIFY_OTHER);
if (isSigningKey && !hasValidPrimaryKeyBindingSignatureSubpacket(subkey, primaryKey)
.accept(signature, subkey, keyRing)) {
LOGGER.log(Level.INFO, "Subkey binding signature on signing key does not carry valid primary key binding signature.");
return false;
}
return true;
}
};
}
/**
* Criterion that checks if a primary key binding signature is valid.
*
* @param subkey subkey
* @param primaryKey primary key
* @return criterion to validate primary key binding signatures
*/
public static SelectSignatureFromKey isValidPrimaryKeyBindingSignature(PGPPublicKey subkey, PGPPublicKey primaryKey) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
if (!isVersion4Signature().accept(signature, key, keyRing)) {
return false;
}
if (!isOfType(SignatureType.PRIMARYKEY_BINDING).accept(signature, key, keyRing)) {
return false;
}
if (signature.getKeyID() != subkey.getKeyID()) {
return false;
}
if (!isSigNotExpired().accept(signature, primaryKey, keyRing)) {
LOGGER.log(Level.INFO, "Primary key binding signature expired.");
return false;
}
// Check signature correctness
try {
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), subkey);
return signature.verifyCertification(primaryKey, subkey);
} catch (PGPException e) {
return false;
}
}
};
}
/**
* Criterion that checks if a signature has an embedded valid primary key binding signature.
* @param subkey subkey
* @param primaryKey primary key
* @return criterion
*/
public static SelectSignatureFromKey hasValidPrimaryKeyBindingSignatureSubpacket(PGPPublicKey subkey, PGPPublicKey primaryKey) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
try {
PGPSignatureList embeddedSignatures = SignatureSubpacketsUtil.getEmbeddedSignature(signature);
if (embeddedSignatures != null) {
for (PGPSignature embeddedSignature : embeddedSignatures) {
if (isValidPrimaryKeyBindingSignature(subkey, primaryKey).accept(embeddedSignature, subkey, keyRing)) {
return true;
}
}
}
} catch (PGPException e) {
LOGGER.log(Level.WARNING, "Cannot parse embedded signatures:", e);
}
return false;
}
};
}
/**
* Criterion that checks if a signature is a valid v4 direct-key signature.
* Note: This method does not check expiration.
*
* @param signer signing key
* @param signee signed key
* @return criterion
*/
public static SelectSignatureFromKey isValidDirectKeySignature(PGPPublicKey signer, PGPPublicKey signee) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
if (!isVersion4Signature().accept(signature, key, keyRing)) {
return false;
}
if (!isOfType(SignatureType.DIRECT_KEY).accept(signature, key, keyRing)) {
return false;
}
// Check signature correctness
try {
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signer);
return signature.verifyCertification(signee);
} catch (PGPException e) {
return false;
}
}
};
}
/**
* Criterion that checks if a signature is a valid key revocation signature.
*
* @param key primary key
* @return criterion
*/
public static SelectSignatureFromKey isValidKeyRevocationSignature(PGPPublicKey key) {
return and(
isVersion4Signature(),
isOfType(SignatureType.KEY_REVOCATION),
isCreatedBy(key),
isWellFormed(),
doesNotPredateKeyCreationDate(key),
isVerifyingSignatureOnKey(key, key)
);
}
/**
* Criterion that only accepts valid subkey revocation signatures.
*
* @return criterion
*/
public static SelectSignatureFromKey isValidSubkeyRevocationSignature() {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
return isValidSubkeyRevocationSignature(key, keyRing.getPublicKey())
.accept(signature, key, keyRing);
}
};
}
/**
* Criterion that only accepts valid subkey revocation signatures.
*
* @param subkey subkey
* @param primaryKey primary key
* @return criterion
*/
public static SelectSignatureFromKey isValidSubkeyRevocationSignature(PGPPublicKey subkey, PGPPublicKey primaryKey) {
return SelectSignatureFromKey.and(
isVersion4Signature(),
isOfType(SignatureType.SUBKEY_REVOCATION),
isCreatedBy(primaryKey),
isVerifyingSignatureOnKeys(primaryKey, subkey, primaryKey)
);
}
/**
* Criterion that only accepts signatures which are valid user-id revocations.
*
* @param revoker signing key
* @param userId user id
* @return criterion
*/
public static SelectSignatureFromKey isValidCertificationRevocationSignature(PGPPublicKey revoker, String userId) {
return and(
isVersion4Signature(),
isCreatedBy(revoker),
isOfType(SignatureType.CERTIFICATION_REVOCATION),
isValidSignatureOnUserId(userId, revoker)
);
}
/**
* Criterion that only accepts signatures which are valid signatures over a user-id.
* This method only checks signature correctness, not expiry etc.
*
* @param userId user-id
* @param signingKey signing key
* @return criterion
*/
public static SelectSignatureFromKey isValidSignatureOnUserId(String userId, PGPPublicKey signingKey) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
try {
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signingKey);
return signature.verifyCertification(userId, key);
} catch (PGPException e) {
LOGGER.log(Level.INFO, "Verification of signature on userID " + userId + " failed.", e);
return false;
}
}
};
}
/**
* Criterion that only accepts signatures which are valid signatures over a key.
* This method only checks signature correctness, not expiry etc.
*
* @param target signed key
* @param signer signing key
* @return criterion
*/
public static SelectSignatureFromKey isVerifyingSignatureOnKey(PGPPublicKey target, PGPPublicKey signer) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
try {
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signer);
boolean valid = signature.verifyCertification(target);
return valid;
} catch (PGPException e) {
LOGGER.log(Level.INFO, "Signature verification failed.", e);
return false;
}
}
};
}
/**
* Criterion that only accepts signatures which are correct binding signatures.
* This method only checks signature correctness, not expiry etc.
*
* @param primaryKey primary key
* @param subkey subkey
* @param signingKey signing key (either primary, or subkey)
* @return criterion
*/
public static SelectSignatureFromKey isVerifyingSignatureOnKeys(PGPPublicKey primaryKey, PGPPublicKey subkey, PGPPublicKey signingKey) {
if (signingKey.getKeyID() != primaryKey.getKeyID() && signingKey.getKeyID() != subkey.getKeyID()) {
throw new IllegalArgumentException("Signing key MUST be either the primary or subkey.");
}
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
try {
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signingKey);
return signature.verifyCertification(primaryKey, subkey);
} catch (PGPException e) {
LOGGER.log(Level.INFO, "Verification of " + SignatureType.valueOf(signature.getSignatureType()) + " signature failed.", e);
return false;
}
}
};
}
/**
* Criterion that only accepts certification signatures.
*
* Those are signature of the following types:
* - {@link SignatureType#NO_CERTIFICATION},
* - {@link SignatureType#CASUAL_CERTIFICATION},
* - {@link SignatureType#GENERIC_CERTIFICATION},
* - {@link SignatureType#POSITIVE_CERTIFICATION}.
*
* @return criterion
*/
public static SelectSignatureFromKey isCertification() {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
return signature.isCertification();
}
};
}
/**
* Criterion that only accepts "well formed" signatures.
* A signature is "well formed", iff it has a creation time subpacket and if it does not predate
* its creating keys creation time.
*
* @return criterion
*/
public static SelectSignatureFromKey isWellFormed() {
return and(
hasCreationTimeSubpacket(),
doesNotPredateKeyCreationDate()
);
}
/**
* Criterion that only accepts v4 signatures.
*
* @return criterion
*/
public static SelectSignatureFromKey isVersion4Signature() {
return isVersion(4);
}
/**
* Criterion that only accepts signatures which carry a creation time subpacket.
* According to the RFC, all signatures are required to have such a subpacket.
*
* @return criterion
*/
public static SelectSignatureFromKey hasCreationTimeSubpacket() {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
return signature.getHashedSubPackets().getSignatureCreationTime() != null;
}
};
}
/**
* Criterion that only accepts signatures that were created by the provided key.
*
* @param publicKey public key of the creation key pair
* @return criterion
*/
public static SelectSignatureFromKey isCreatedBy(PGPPublicKey publicKey) {
return isCreatedBy(publicKey.getKeyID());
}
/**
* Criterion that only accepts signatures which were created by the public key with the provided key id.
*
* @param keyId key id
* @return criterion
*/
public static SelectSignatureFromKey isCreatedBy(long keyId) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
return signature.getKeyID() == keyId;
}
};
}
/**
* Criterion that only accepts signatures which are not expired RIGHT NOW.
*
* @return criterion
*/
public static SelectSignatureFromKey isSigNotExpired() {
return isSigNotExpired(new Date());
}
/**
* Criterion that only accepts signatures which are not expired at comparisonDate.
*
* @param comparisonDate comparison date
* @return criterion
*/
public static SelectSignatureFromKey isSigNotExpired(Date comparisonDate) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
return !SignatureUtils.isSignatureExpired(signature, comparisonDate);
}
};
}
/**
* Criterion that only accepts signatures which do not predate their signing key's creation date.
*
* @return criterion
*/
public static SelectSignatureFromKey doesNotPredateKeyCreationDate() {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
PGPPublicKey creator = keyRing.getPublicKey(signature.getKeyID());
if (creator == null) {
return false;
}
return doesNotPredateKeyCreationDate(creator).accept(signature, key, keyRing);
}
};
}
/**
* Criterion that only accepts signatures which do not predate the creation date of the provided key.
*
* @param creator key
* @return criterion
*/
public static SelectSignatureFromKey doesNotPredateKeyCreationDate(PGPPublicKey creator) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
return !signature.getCreationTime().before(creator.getCreationTime());
}
};
}
/**
* Criterion that only accepts signatures of the provided signature version.
*
* @param version signature version
* @return criterion
*/
public static SelectSignatureFromKey isVersion(int version) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
return signature.getVersion() == version;
}
};
}
/**
* Criterion that only accepts signatures that are of the provided {@link SignatureType}.
*
* @param signatureType signature type that shall be accepted
* @return criterion
*/
public static SelectSignatureFromKey isOfType(SignatureType signatureType) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
return signature.getSignatureType() == signatureType.getCode();
}
};
}
/**
* Compose different {@link SelectSignatureFromKey} by combining them with a logic AND.
* A signature will only be accepted, iff it satisfies every selector from selectors.
*
* @param selectors one or more selectors
* @return combined selector using AND operator
*/
public static SelectSignatureFromKey and(SelectSignatureFromKey... selectors) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
for (SelectSignatureFromKey selector : selectors) {
if (!selector.accept(signature, key, keyRing)) {
return false;
}
}
return true;
}
};
}
/**
* Compose different {@link SelectSignatureFromKey} by combining them with a logic OR.
* A signature will only be accepted, iff it satisfies at least one selector from selectors.
*
* @param selectors one or more selectors
* @return combined selector using OR operator
*/
public static SelectSignatureFromKey or(SelectSignatureFromKey... selectors) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
boolean accept = false;
for (SelectSignatureFromKey selector : selectors) {
accept |= selector.accept(signature, key, keyRing);
}
return accept;
}
};
}
/**
* Negate the result of a {@link SelectSignatureFromKey} implementations {@link #accept(PGPSignature, PGPPublicKey, PGPKeyRing)}.
* The resulting {@link SelectSignatureFromKey} will only accept signatures that are rejected by the provided selector
* and reject those that are accepted by it.
*
* @param selector selector whose logic operation will be negated
* @return negated selector
*/
public static SelectSignatureFromKey not(SelectSignatureFromKey selector) {
return new SelectSignatureFromKey() {
@Override
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
return !selector.accept(signature, key, keyRing);
}
};
}
}

View file

@ -147,7 +147,6 @@ public class SignatureUtils {
/**
* Return true, iff a signature is valid.
*
* TODO: There is code duplication here ({@link SelectSignatureFromKey}, {@link SignatureChainValidator}, {@link SignatureValidator}).
* @param signature signature to validate
* @param issuer signing key
* @param target signed key

View file

@ -1,174 +0,0 @@
/*
* 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.util.selection.signature;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.IOException;
import java.util.Iterator;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.signature.SelectSignatureFromKey;
public class SelectSignatureFromKeyTest {
@Test
public void validKeyTest() throws IOException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
"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\n" +
"Db7pzk2vugYKrebvskFQaq0S8TwhHQ4n9GVrUnenFf2OAWYfRYmYbENUv+fQm22+\n" +
"EOxHWSVwB3NWl8albQxs/aPCi3nuPdtdTMU2fHLGDAZ9MGQesb/0tSJLWaqQRvqT\n" +
"k3llI1OqxGbYLaNXSz6nJDLsKK9v+6lFzxA5C8OOxGikHE7b9RJ6SGVNijItXtHo\n" +
"rVuAKayDfMKO+0jc25I+agMbfg6p4Ik5D+1LFzZtsSc6Ib6AKu+FLit6Ik74/nrr\n" +
"/ORSAoTpxnIyJlBu4DS3AUwRd/O7rke8FNVg6EpzaPazrqfY1eZ2YelEE4EO3xXm\n" +
"wcOLSPVwsLNoC3DdRRLtw5EItZy2z0QiARF+NsUYQQM5RCrQizxuzD5+nXg1AcaE\n" +
"ixnbju8StB8jT1m4ccJKHsObgi/cIPPsWm5+BUhV9RDLsMWnaVZ8f3tRAHy2TAld\n" +
"FiEE0aZuGiOxgsmYD3iM+/zIKgFeczAAAAv/DACScy69f/qohzub6e06b3sgmL1K\n" +
"foCMmFRAiEsDHUHunAb/KWBqkbJ8W6wP0COwh4tbmjUzwexMQyI4m58SLRYULcJ7\n" +
"kj3axMV0+JJyFoqUpCT06GpqQQIhZY7Y+AHz9FdVNEDjjUwb3mODx8zVyEg57T9C\n" +
"TfuLrrJDYpycfNJtxYy9qSMPHBiVGqlzqnyETOa312QquZuY6ucfTL8i8kXk5qtL\n" +
"jVHTnKogzrbTCWuKR8fzsxfZ9afdYXI3SMMsip4Ixx2mLM5tN9IeDI/DQnWetwB2\n" +
"Z0PEs7UcYcrn6UWs1X4P7jOmtLH+0d96I9ljd9SSmJ9dTr2cV62J/qtK+75hCBk8\n" +
"Lz+MNWzyAU3sVqGRhsBaLOqvb7K9p3bm6brEmGpBLeKrxuxjBER+7knqkTxSsb+S\n" +
"msO3lGrEnNEQIlcvoxLIGQiv9b0sblGM9lr40C0D84PEvajhuFAUTItoPfCIVVaT\n" +
"7Ry8/ZA6t0uQh9/B0hYblb07mJ92hCacoTx+APM=\n" +
"=yeYe\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key);
Iterator<PGPPublicKey> keyIt = publicKeys.getPublicKeys();
PGPPublicKey primaryKey = publicKeys.getPublicKey();
while (keyIt.hasNext()) {
PGPPublicKey publicKey = keyIt.next();
if (publicKey == primaryKey) {
continue;
}
boolean validBinding = false;
Iterator<PGPSignature> signatures = publicKey.getSignatures();
while (signatures.hasNext()) {
PGPSignature signature = signatures.next();
if (SelectSignatureFromKey.isValidSubkeyBindingSignature(primaryKey, publicKey).accept(signature, publicKey, publicKeys)) {
validBinding = true;
}
}
assertTrue(validBinding);
}
}
@Test
public void missingBackSigTest() throws IOException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
"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\n" +
"=uSRw\n" +
"-----END PGP PUBLIC KEY BLOCK-----\n";
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key);
Iterator<PGPPublicKey> keyIt = publicKeys.getPublicKeys();
PGPPublicKey primaryKey = publicKeys.getPublicKey();
while (keyIt.hasNext()) {
PGPPublicKey publicKey = keyIt.next();
if (publicKey == primaryKey) {
continue;
}
Iterator<PGPSignature> signatures = publicKey.getSignatures();
while (signatures.hasNext()) {
PGPSignature signature = signatures.next();
if (SelectSignatureFromKey.isValidSubkeyBindingSignature(primaryKey, publicKey).accept(signature, publicKey, publicKeys)) {
fail("Implementation MUST NOT accept this subkey as bound valid since the backsig is missing.");
}
}
}
}
}