1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-11-30 08:12:06 +01:00

Add support for checking for revoked userIds

This commit is contained in:
Paul Schaub 2021-01-22 16:52:09 +01:00
parent 7864add645
commit 9aae9ecc93
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
3 changed files with 302 additions and 0 deletions

View file

@ -15,6 +15,9 @@
*/ */
package org.pgpainless.key.info; package org.pgpainless.key.info;
import static org.pgpainless.key.util.SignatureUtils.getLatestValidSignature;
import static org.pgpainless.key.util.SignatureUtils.sortByCreationTimeAscending;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@ -23,6 +26,7 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyRing; import org.bouncycastle.openpgp.PGPKeyRing;
import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRing;
@ -33,6 +37,8 @@ import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.algorithm.SignatureType; import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.key.util.SignatureUtils;
/** /**
* Utility class to quickly extract certain information from a {@link PGPPublicKeyRing}/{@link PGPSecretKeyRing}. * Utility class to quickly extract certain information from a {@link PGPPublicKeyRing}/{@link PGPSecretKeyRing}.
@ -56,6 +62,18 @@ public class KeyRingInfo {
return keys.getPublicKey(); return keys.getPublicKey();
} }
public PGPPublicKey getPublicKey(OpenPgpV4Fingerprint fingerprint) {
return getPublicKey(fingerprint.getKeyId());
}
public PGPPublicKey getPublicKey(long keyId) {
return keys.getPublicKey(keyId);
}
public static PGPPublicKey getPublicKey(PGPKeyRing keyRing, long keyId) {
return keyRing.getPublicKey(keyId);
}
/** /**
* Return all {@link PGPPublicKey PGPPublicKeys} of this key ring. * Return all {@link PGPPublicKey PGPPublicKeys} of this key ring.
* The first key in the list being the primary key. * The first key in the list being the primary key.
@ -85,6 +103,17 @@ public class KeyRingInfo {
return null; return null;
} }
public PGPSecretKey getSecretKey(OpenPgpV4Fingerprint fingerprint) {
return getSecretKey(fingerprint.getKeyId());
}
public PGPSecretKey getSecretKey(long keyId) {
if (keys instanceof PGPSecretKeyRing) {
return ((PGPSecretKeyRing) keys).getSecretKey(keyId);
}
return null;
}
/** /**
* Return all secret keys of the key ring. * Return all secret keys of the key ring.
* If the key ring is a {@link PGPPublicKeyRing}, then return an empty list. * If the key ring is a {@link PGPPublicKeyRing}, then return an empty list.
@ -136,6 +165,25 @@ public class KeyRingInfo {
return userIds; return userIds;
} }
public List<String> getValidUserIds() {
List<String> valid = new ArrayList<>();
List<String> userIds = getUserIds();
for (String userId : userIds) {
if (isUserIdValid(userId)) {
valid.add(userId);
}
}
return valid;
}
public boolean isUserIdValid(String userId) {
try {
return SignatureUtils.isUserIdValid(getPublicKey(), userId);
} catch (PGPException e) {
return false;
}
}
/** /**
* Return a list of all user-ids of the primary key that appear to be email-addresses. * Return a list of all user-ids of the primary key that appear to be email-addresses.
* *
@ -259,4 +307,77 @@ public class KeyRingInfo {
} }
return true; return true;
} }
public List<PGPSignature> getSelfSignatures() {
return getSelfSignatures(new OpenPgpV4Fingerprint(getPublicKey()));
}
public List<PGPSignature> getSelfSignatures(OpenPgpV4Fingerprint subkeyFingerprint) {
return getSelfSignatures(subkeyFingerprint.getKeyId());
}
public List<PGPSignature> getSelfSignatures(long subkeyId) {
PGPPublicKey publicKey = KeyRingUtils.requirePublicKeyFrom(keys, subkeyId);
Iterator<PGPSignature> it = publicKey.getSignaturesForKeyID(subkeyId);
List<PGPSignature> signatures = new ArrayList<>();
while (it.hasNext()) {
signatures.add(it.next());
}
sortByCreationTimeAscending(signatures);
return signatures;
}
public PGPSignature getLatestValidSelfSignature() throws PGPException {
return getLatestValidSelfSignature(new OpenPgpV4Fingerprint(getPublicKey()));
}
public PGPSignature getLatestValidSelfSignature(OpenPgpV4Fingerprint fingerprint) throws PGPException {
return getLatestValidSelfSignature(fingerprint.getKeyId());
}
public PGPSignature getLatestValidSelfSignature(long subkeyId) throws PGPException {
PGPPublicKey publicKey = KeyRingUtils.requirePublicKeyFrom(keys, subkeyId);
List<PGPSignature> signatures = getSelfSignatures(subkeyId);
return getLatestValidSignature(publicKey, signatures, keys);
}
public List<PGPSignature> getBindingSignatures(OpenPgpV4Fingerprint fingerprint) {
return getBindingSignatures(fingerprint.getKeyId());
}
public List<PGPSignature> getBindingSignatures(long subkeyId) {
if (subkeyId == getKeyId()) {
return Collections.emptyList();
}
PGPPublicKey publicKey = KeyRingUtils.requirePublicKeyFrom(keys, subkeyId);
return SignatureUtils.getBindingSignatures(publicKey, getKeyId());
}
public PGPSignature getLatestValidBindingSignature(long subKeyID) throws PGPException {
PGPPublicKey publicKey = KeyRingUtils.requirePublicKeyFrom(keys, subKeyID);
List<PGPSignature> signatures = getBindingSignatures(subKeyID);
return getLatestValidSignature(publicKey, signatures, keys);
}
public PGPSignature getLatestValidSelfOrBindingSignature() throws PGPException {
return getLatestValidSelfOrBindingSignature(new OpenPgpV4Fingerprint(getPublicKey()));
}
public PGPSignature getLatestValidSelfOrBindingSignature(OpenPgpV4Fingerprint fingerprint) throws PGPException {
return getLatestValidSelfOrBindingSignature(fingerprint.getKeyId());
}
public PGPSignature getLatestValidSelfOrBindingSignature(long subKeyId) throws PGPException {
PGPSignature self = getLatestValidSelfSignature(subKeyId);
PGPSignature binding = getLatestValidBindingSignature(subKeyId);
if (self == null) {
return binding;
}
if (binding == null) {
return self;
}
return self.getCreationTime().after(binding.getCreationTime()) ? self : binding;
}
} }

View file

@ -90,6 +90,26 @@ public class KeyRingUtils {
return null; return null;
} }
public static PGPPublicKey getPublicKeyFrom(PGPKeyRing keyRing, long subKeyId) {
return keyRing.getPublicKey(subKeyId);
}
public static PGPPublicKey requirePublicKeyFrom(PGPKeyRing keyRing, long subKeyId) {
PGPPublicKey publicKey = getPublicKeyFrom(keyRing, subKeyId);
if (publicKey == null) {
throw new IllegalArgumentException("KeyRing does not contain public key with keyID " + Long.toHexString(subKeyId));
}
return publicKey;
}
public static PGPSecretKey requireSecretKeyFrom(PGPSecretKeyRing keyRing, long subKeyId) {
PGPSecretKey secretKey = keyRing.getSecretKey(subKeyId);
if (secretKey == null) {
throw new IllegalArgumentException("KeyRing does not contain secret key with keyID " + Long.toHexString(subKeyId));
}
return secretKey;
}
/** /**
* Extract a {@link PGPPublicKeyRing} containing all public keys from the provided {@link PGPSecretKeyRing}. * Extract a {@link PGPPublicKeyRing} containing all public keys from the provided {@link PGPSecretKeyRing}.
* *

View file

@ -15,13 +15,23 @@
*/ */
package org.pgpainless.key.util; package org.pgpainless.key.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List; import java.util.List;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyRing;
import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator; import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.implementation.ImplementationFactory;
public class SignatureUtils { public class SignatureUtils {
@ -51,4 +61,155 @@ public class SignatureUtils {
} }
return preferredHashAlgorithms.get(0); return preferredHashAlgorithms.get(0);
} }
public static PGPSignature getLatestValidSignature(PGPPublicKey publicKey, List<PGPSignature> signatures, PGPKeyRing keyRing) throws PGPException {
List<PGPSignature> valid = new ArrayList<>();
for (PGPSignature signature : signatures) {
long issuerID = signature.getKeyID();
PGPPublicKey issuer = KeyRingUtils.getPublicKeyFrom(keyRing, issuerID);
if (issuer == null) {
continue;
}
if (!isSignatureValid(signature, issuer, publicKey)) {
continue;
}
if (isSignatureExpired(signature)) {
continue;
}
valid.add(signature);
}
sortByCreationTimeAscending(valid);
return valid.isEmpty() ? null : valid.get(valid.size() - 1);
}
public static boolean isSignatureValid(PGPSignature signature, PGPPublicKey issuer, PGPPublicKey target) throws PGPException {
SignatureType signatureType = SignatureType.valueOf(signature.getSignatureType());
switch (signatureType) {
case BINARY_DOCUMENT:
case CANONICAL_TEXT_DOCUMENT:
case STANDALONE:
case TIMESTAMP:
case THIRD_PARTY_CONFIRMATION:
throw new IllegalArgumentException("Signature is not a key signature.");
case GENERIC_CERTIFICATION:
case NO_CERTIFICATION:
case CASUAL_CERTIFICATION:
case POSITIVE_CERTIFICATION:
case DIRECT_KEY:
case KEY_REVOCATION:
case CERTIFICATION_REVOCATION:
return isSelfSignatureValid(signature, issuer);
case SUBKEY_BINDING:
case PRIMARYKEY_BINDING:
case SUBKEY_REVOCATION:
return isKeyOnKeySignatureValid(signature, issuer, target);
}
return false;
}
public static boolean isKeyOnKeySignatureValid(PGPSignature signature, PGPPublicKey issuer, PGPPublicKey target) throws PGPException {
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), issuer);
return signature.verifyCertification(issuer, target);
}
public static boolean isSelfSignatureValid(PGPSignature signature, PGPPublicKey publicKey) throws PGPException {
switch (signature.getSignatureType()) {
case PGPSignature.POSITIVE_CERTIFICATION:
case PGPSignature.DEFAULT_CERTIFICATION:
for (Iterator<String> it = publicKey.getUserIDs(); it.hasNext(); ) {
String userId = it.next();
boolean valid = isSelfSignatureOnUserIdValid(signature, userId, publicKey);
if (valid) {
return true;
}
}
}
return false;
}
public static boolean isSelfSignatureOnUserIdValid(PGPSignature signature, String userId, PGPPublicKey publicKey) throws PGPException {
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), publicKey);
return signature.verifyCertification(userId, publicKey);
}
public static boolean isSignatureExpired(PGPSignature signature) {
long expiration = signature.getHashedSubPackets().getSignatureExpirationTime();
if (expiration == 0) {
return false;
}
Date now = new Date();
Date creation = signature.getCreationTime();
return now.after(new Date(creation.getTime() + 1000 * expiration));
}
public static void sortByCreationTimeAscending(List<PGPSignature> signatures) {
Collections.sort(signatures, new Comparator<PGPSignature>() {
@Override
public int compare(PGPSignature s1, PGPSignature s2) {
return s1.getCreationTime().compareTo(s2.getCreationTime());
}
});
}
public static List<PGPSignature> getBindingSignatures(PGPPublicKey subKey, long primaryKeyId) {
List<PGPSignature> signatures = new ArrayList<>();
List<PGPSignature> bindingSigs = getSignaturesOfTypes(subKey, SignatureType.SUBKEY_BINDING);
for (PGPSignature signature : bindingSigs) {
if (signature.getKeyID() != primaryKeyId) {
continue;
}
signatures.add(signature);
}
return signatures;
}
public static List<PGPSignature> getSignaturesOfTypes(PGPPublicKey publicKey, SignatureType... types) {
List<PGPSignature> signatures = new ArrayList<>();
for (SignatureType type : types) {
Iterator<?> it = publicKey.getSignaturesOfType(type.getCode());
while (it.hasNext()) {
Object o = it.next();
if (o instanceof PGPSignature) {
signatures.add((PGPSignature) o);
}
}
}
sortByCreationTimeAscending(signatures);
return signatures;
}
public static List<PGPSignature> getSignaturesForUserId(PGPPublicKey publicKey, String userId) {
List<PGPSignature> signatures = new ArrayList<>();
Iterator<?> it = publicKey.getSignaturesForID(userId);
while (it != null && it.hasNext()) {
Object o = it.next();
if (o instanceof PGPSignature) {
signatures.add((PGPSignature) o);
}
}
sortByCreationTimeAscending(signatures);
return signatures;
}
public static PGPSignature getLatestSelfSignatureForUserId(PGPPublicKey publicKey, String userId) throws PGPException {
List<PGPSignature> valid = new ArrayList<>();
List<PGPSignature> signatures = getSignaturesForUserId(publicKey, userId);
for (PGPSignature signature : signatures) {
if (isSelfSignatureOnUserIdValid(signature, userId, publicKey)) {
valid.add(signature);
}
}
return valid.isEmpty() ? null : valid.get(valid.size() - 1);
}
public static boolean isUserIdValid(PGPPublicKey publicKey, String userId) throws PGPException {
PGPSignature latestSelfSig = getLatestSelfSignatureForUserId(publicKey, userId);
if (latestSelfSig == null) {
return false;
}
return latestSelfSig.getSignatureType() != SignatureType.CERTIFICATION_REVOCATION.getCode();
}
} }