mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-27 13:28:00 +01:00
Add support for checking for revoked userIds
This commit is contained in:
parent
7864add645
commit
9aae9ecc93
3 changed files with 302 additions and 0 deletions
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
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.Collections;
|
||||
import java.util.Date;
|
||||
|
@ -23,6 +26,7 @@ import java.util.List;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
|
@ -33,6 +37,8 @@ import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
|||
import org.pgpainless.algorithm.SignatureType;
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||
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}.
|
||||
|
@ -56,6 +62,18 @@ public class KeyRingInfo {
|
|||
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.
|
||||
* The first key in the list being the primary key.
|
||||
|
@ -85,6 +103,17 @@ public class KeyRingInfo {
|
|||
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.
|
||||
* If the key ring is a {@link PGPPublicKeyRing}, then return an empty list.
|
||||
|
@ -136,6 +165,25 @@ public class KeyRingInfo {
|
|||
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.
|
||||
*
|
||||
|
@ -259,4 +307,77 @@ public class KeyRingInfo {
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -90,6 +90,26 @@ public class KeyRingUtils {
|
|||
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}.
|
||||
*
|
||||
|
|
|
@ -15,13 +15,23 @@
|
|||
*/
|
||||
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 org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
||||
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
|
||||
import org.pgpainless.algorithm.HashAlgorithm;
|
||||
import org.pgpainless.algorithm.SignatureType;
|
||||
import org.pgpainless.implementation.ImplementationFactory;
|
||||
|
||||
public class SignatureUtils {
|
||||
|
||||
|
@ -51,4 +61,155 @@ public class SignatureUtils {
|
|||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue