From 29fbf21d01fe8bee7308cfd324c9f40460c9908b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 21 Jun 2018 15:18:19 +0200 Subject: [PATCH] Add util methods --- .../crypto/pgpainless/util/BCUtil.java | 126 ++++++++++++++++++ .../crypto/pgpainless/BCUtilTest.java | 43 +++++- 2 files changed, 166 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/util/BCUtil.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/util/BCUtil.java index 3f1d2e0a..a5c60963 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/util/BCUtil.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/util/BCUtil.java @@ -20,18 +20,33 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import de.vanitasvitae.crypto.pgpainless.algorithm.KeyFlag; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; +import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; public class BCUtil { + private static final Logger LOGGER = Logger.getLogger(BCUtil.class.getName()); + + /* + PGPXxxKeyRing -> PGPXxxKeyRingCollection + */ public static PGPPublicKeyRingCollection keyRingsToKeyRingCollection(PGPPublicKeyRing... rings) throws IOException, PGPException { return new PGPPublicKeyRingCollection(Arrays.asList(rings)); @@ -42,6 +57,10 @@ public class BCUtil { return new PGPSecretKeyRingCollection(Arrays.asList(rings)); } + /* + PGPSecretKeyRing -> PGPPublicKeyRing + */ + public static PGPPublicKeyRing publicKeyRingFromSecretKeyRing(PGPSecretKeyRing secring) { List list = new ArrayList<>(); for (Iterator i = secring.getPublicKeys(); i.hasNext(); ) { @@ -60,4 +79,111 @@ public class BCUtil { throw new AssertionError(e); } } + + /* + PGPXxxKeyRingCollection -> PGPXxxKeyRing + */ + + public static PGPSecretKeyRing getKeyRingFromCollection(PGPSecretKeyRingCollection collection, Long id) + throws PGPException { + PGPSecretKeyRing uncleanedRing = collection.getSecretKeyRing(id); + PGPPublicKeyRing publicKeys = publicKeyRingFromSecretKeyRing(uncleanedRing); + + // Determine ids of signed keys + Set signedKeyIds = new HashSet<>(); + signedKeyIds.add(id); // Add the signing key itself + Iterator signedPubKeys = publicKeys.getKeysWithSignaturesBy(id); + while (signedPubKeys.hasNext()) { + signedKeyIds.add(signedPubKeys.next().getKeyID()); + } + + PGPSecretKeyRing cleanedRing = uncleanedRing; + Iterator secretKeys = uncleanedRing.getSecretKeys(); + while (secretKeys.hasNext()) { + PGPSecretKey secretKey = secretKeys.next(); + if (!signedKeyIds.contains(secretKey.getKeyID())) { + cleanedRing = PGPSecretKeyRing.removeSecretKey(cleanedRing, secretKey); + } + } + return cleanedRing; + } + + public static PGPPublicKeyRing getKeyRingFromCollection(PGPPublicKeyRingCollection collection, Long id) + throws PGPException { + return removeUnsignedKeysFromKeyRing(collection.getPublicKeyRing(id), id); + } + + public static PGPPublicKeyRing removeUnsignedKeysFromKeyRing(PGPPublicKeyRing ring, Long masterKeyId) { + + Set signedKeyIds = new HashSet<>(); + signedKeyIds.add(masterKeyId); + Iterator signedKeys = ring.getKeysWithSignaturesBy(masterKeyId); + while (signedKeys.hasNext()) { + signedKeyIds.add(signedKeys.next().getKeyID()); + } + + PGPPublicKeyRing cleaned = ring; + + Iterator publicKeys = ring.getPublicKeys(); + while (publicKeys.hasNext()) { + PGPPublicKey publicKey = publicKeys.next(); + if (!signedKeyIds.contains(publicKey.getKeyID())) { + cleaned = PGPPublicKeyRing.removePublicKey(cleaned, publicKey); + } + } + + return cleaned; + } + + public static PGPPublicKey getMasterKeyFrom(PGPPublicKeyRing ring) { + Iterator it = ring.getPublicKeys(); + while (it.hasNext()) { + PGPPublicKey k = it.next(); + if (k.isMasterKey()) { + return k; + } + } + return null; + } + + public static Set signingKeyIds(PGPSecretKeyRing ring) { + Set ids = new HashSet<>(); + Iterator it = ring.getPublicKeys(); + while (it.hasNext()) { + PGPPublicKey k = it.next(); + + boolean signingKey = false; + + Iterator sit = k.getSignatures(); + while (sit.hasNext()) { + Object n = sit.next(); + if (!(n instanceof PGPSignature)) { + continue; + } + + PGPSignature s = (PGPSignature) n; + if (!s.hasSubpackets()) { + continue; + } + + try { + s.verifyCertification(ring.getPublicKey(s.getKeyID())); + } catch (PGPException e) { + LOGGER.log(Level.WARNING, "Could not verify signature on " + Long.toHexString(k.getKeyID()) + " made by " + Long.toHexString(s.getKeyID())); + continue; + } + + PGPSignatureSubpacketVector hashed = s.getHashedSubPackets(); + if (KeyFlag.fromInteger(hashed.getKeyFlags()).contains(KeyFlag.SIGN_DATA)) { + signingKey = true; + break; + } + } + + if (signingKey) { + ids.add(k.getKeyID()); + } + } + return ids; + } } \ No newline at end of file diff --git a/src/test/java/de/vanitasvitae/crypto/pgpainless/BCUtilTest.java b/src/test/java/de/vanitasvitae/crypto/pgpainless/BCUtilTest.java index 941fd8b8..d7ed08c7 100644 --- a/src/test/java/de/vanitasvitae/crypto/pgpainless/BCUtilTest.java +++ b/src/test/java/de/vanitasvitae/crypto/pgpainless/BCUtilTest.java @@ -22,36 +22,73 @@ import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; import de.vanitasvitae.crypto.pgpainless.util.BCUtil; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.junit.Test; public class BCUtilTest extends AbstractPGPainlessTest { + private static final Logger LOGGER = Logger.getLogger(BCUtil.class.getName()); + @Test public void test() - throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { + throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, + IOException { PGPSecretKeyRing sec = PGPainless.generateKeyRing().simpleEcKeyRing("donald@duck.tails"); PGPPublicKeyRing pub = BCUtil.publicKeyRingFromSecretKeyRing(sec); + LOGGER.log(Level.INFO, "Main ID: " + sec.getPublicKey().getKeyID() + " " + pub.getPublicKey().getKeyID()); + int secSize = 0; Iterator secPubIt = sec.getPublicKeys(); while (secPubIt.hasNext()) { - secPubIt.next(); + PGPPublicKey k = secPubIt.next(); + LOGGER.log(Level.INFO, "" + k.getKeyID() + " " + k.isEncryptionKey() + " " + k.isMasterKey()); secSize++; } + LOGGER.log(Level.INFO, "After BCUtil.publickKeyRingFromSecretKeyRing()"); int pubSize = 0; Iterator pubPubIt = pub.getPublicKeys(); while (pubPubIt.hasNext()) { - pubPubIt.next(); + PGPPublicKey k = pubPubIt.next(); + LOGGER.log(Level.INFO, "" + k.getKeyID() + " " + k.isEncryptionKey() + " " + k.isMasterKey()); pubSize++; } + LOGGER.log(Level.INFO, " Pub: " + pubSize + " Sec: " + secSize); assertEquals(secSize, pubSize); + + PGPSecretKeyRingCollection secCol = BCUtil.keyRingsToKeyRingCollection(sec); + + int secColSize = 0; + Iterator secColIt = secCol.getKeyRings(); + while (secColIt.hasNext()) { + PGPSecretKeyRing r = secColIt.next(); + LOGGER.log(Level.INFO, "" + r.getPublicKey().getKeyID()); + secColSize++; + } + + LOGGER.log(Level.INFO, "SecCol: " + secColSize); + + PGPPublicKeyRingCollection pubCol = BCUtil.keyRingsToKeyRingCollection(pub); + + int pubColSize = 0; + Iterator pubColIt = pubCol.getKeyRings(); + while (pubColIt.hasNext()) { + PGPPublicKeyRing r = pubColIt.next(); + LOGGER.log(Level.INFO, "" + r.getPublicKey().getKeyID()); + pubColSize++; + } + + LOGGER.log(Level.INFO, "PubCol: " + pubColSize); } }