From 67f26538ce499d3d201fe217709542ba8360ebff Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 10 Apr 2024 17:02:37 +0700 Subject: Add various fingerprint-related methods in OpenPgp --- crypto/src/openpgp/PgpPublicKey.cs | 16 +++---- crypto/src/openpgp/PgpPublicKeyRing.cs | 19 ++++++-- crypto/src/openpgp/PgpPublicKeyRingBundle.cs | 72 ++++++++++++++++++++-------- crypto/src/openpgp/PgpSecretKeyRing.cs | 48 ++++++++++++++++++- crypto/src/openpgp/PgpSecretKeyRingBundle.cs | 34 ++++++------- crypto/test/src/openpgp/test/PGPRSATest.cs | 14 +++++- crypto/test/src/openpgp/test/PgpEdDsaTest.cs | 6 ++- 7 files changed, 153 insertions(+), 56 deletions(-) (limited to 'crypto') diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs index fa924ff37..672ce9548 100644 --- a/crypto/src/openpgp/PgpPublicKey.cs +++ b/crypto/src/openpgp/PgpPublicKey.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Drawing; using System.IO; using Org.BouncyCastle.Asn1.Cryptlib; @@ -11,6 +10,7 @@ using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Math.EC.Rfc7748; @@ -115,14 +115,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } else { - this.keyId = (long)(((ulong)fingerprint[fingerprint.Length - 8] << 56) - | ((ulong)fingerprint[fingerprint.Length - 7] << 48) - | ((ulong)fingerprint[fingerprint.Length - 6] << 40) - | ((ulong)fingerprint[fingerprint.Length - 5] << 32) - | ((ulong)fingerprint[fingerprint.Length - 4] << 24) - | ((ulong)fingerprint[fingerprint.Length - 3] << 16) - | ((ulong)fingerprint[fingerprint.Length - 2] << 8) - | (ulong)fingerprint[fingerprint.Length - 1]); + this.keyId = (long)Pack.BE_To_UInt64(fingerprint, fingerprint.Length - 8); if (key is RsaPublicBcpgKey) { @@ -490,6 +483,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return (byte[]) fingerprint.Clone(); } + public bool HasFingerprint(byte[] fingerprint) + { + return Arrays.AreEqual(this.fingerprint, fingerprint); + } + /// /// Check if this key has an algorithm type that makes it suitable to use for encryption. /// diff --git a/crypto/src/openpgp/PgpPublicKeyRing.cs b/crypto/src/openpgp/PgpPublicKeyRing.cs index 46eecd726..f50dd915c 100644 --- a/crypto/src/openpgp/PgpPublicKeyRing.cs +++ b/crypto/src/openpgp/PgpPublicKeyRing.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.IO; -using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Bcpg.OpenPgp @@ -80,6 +79,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return null; } + /// Return the public key with the passed in fingerprint if it is present. + public virtual PgpPublicKey GetPublicKey(byte[] fingerprint) + { + foreach (PgpPublicKey k in keys) + { + if (k.HasFingerprint(fingerprint)) + return k; + } + + return null; + } + /// Allows enumeration of all the public keys. /// An IEnumerable of PgpPublicKey objects. public virtual IEnumerable GetPublicKeys() @@ -239,17 +250,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp public static PgpPublicKeyRing Join(PgpPublicKeyRing first, PgpPublicKeyRing second, bool joinTrustPackets, bool allowSubkeySigsOnNonSubkey) { - if (!Arrays.AreEqual(first.GetPublicKey().GetFingerprint(), second.GetPublicKey().GetFingerprint())) + if (!second.GetPublicKey().HasFingerprint(first.GetPublicKey().GetFingerprint())) throw new ArgumentException("Cannot merge certificates with differing primary keys."); var secondKeys = new HashSet(); - foreach (var key in second.GetPublicKeys()) + foreach (var key in second.keys) { secondKeys.Add(key.KeyId); } var merged = new List(); - foreach (var key in first.GetPublicKeys()) + foreach (var key in first.keys) { var copy = second.GetPublicKey(key.KeyId); if (copy != null) diff --git a/crypto/src/openpgp/PgpPublicKeyRingBundle.cs b/crypto/src/openpgp/PgpPublicKeyRingBundle.cs index 473d0ae5b..1940c979e 100644 --- a/crypto/src/openpgp/PgpPublicKeyRingBundle.cs +++ b/crypto/src/openpgp/PgpPublicKeyRingBundle.cs @@ -8,10 +8,10 @@ using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Bcpg.OpenPgp { - /// - /// Often a PGP key ring file is made up of a succession of master/sub-key key rings. - /// If you want to read an entire public key file in one hit this is the class for you. - /// + /// + /// Often a PGP key ring file is made up of a succession of master/sub-key key rings. + /// If you want to read an entire public key file in one hit this is the class for you. + /// public class PgpPublicKeyRingBundle { private readonly IDictionary m_pubRings; @@ -66,7 +66,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// Allow enumeration of the public key rings making up this collection. public IEnumerable GetKeyRings() { - return CollectionUtilities.Proxy(m_pubRings.Values); + return CollectionUtilities.Proxy(KeyRings); } /// Allow enumeration of the key rings associated with the passed in userId. @@ -96,7 +96,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp var compareInfo = CultureInfo.InvariantCulture.CompareInfo; var compareOptions = ignoreCase ? CompareOptions.OrdinalIgnoreCase : CompareOptions.Ordinal; - foreach (PgpPublicKeyRing pubRing in GetKeyRings()) + foreach (PgpPublicKeyRing pubRing in KeyRings) { foreach (string nextUserID in pubRing.GetPublicKey().GetUserIds()) { @@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// The ID of the public key to return. public PgpPublicKey GetPublicKey(long keyId) { - foreach (PgpPublicKeyRing pubRing in GetKeyRings()) + foreach (PgpPublicKeyRing pubRing in KeyRings) { PgpPublicKey pub = pubRing.GetPublicKey(keyId); if (pub != null) @@ -135,7 +135,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp if (m_pubRings.TryGetValue(keyId, out var keyRing)) return keyRing; - foreach (PgpPublicKeyRing pubRing in GetKeyRings()) + foreach (PgpPublicKeyRing pubRing in KeyRings) { if (pubRing.GetPublicKey(keyId) != null) return pubRing; @@ -144,11 +144,39 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return null; } - /// - /// Return true if a key matching the passed in key ID is present, false otherwise. - /// - /// key ID to look for. - public bool Contains(long keyID) + /// Return the PGP public key associated with the given key fingerprint. + /// the public key fingerprint to match against. + public PgpPublicKey GetPublicKey(byte[] fingerprint) + { + foreach (PgpPublicKeyRing pubRing in KeyRings) + { + PgpPublicKey pub = pubRing.GetPublicKey(fingerprint); + if (pub != null) + return pub; + } + + return null; + } + + /// Return the public key ring which contains the key associated with the given key fingerprint. + /// + /// the public key fingerprint to match against. + public PgpPublicKeyRing GetPublicKeyRing(byte[] fingerprint) + { + foreach (PgpPublicKeyRing pubRing in KeyRings) + { + if (pubRing.GetPublicKey(fingerprint) != null) + return pubRing; + } + + return null; + } + + /// + /// Return true if a key matching the passed in key ID is present, false otherwise. + /// + /// key ID to look for. + public bool Contains(long keyID) { return GetPublicKey(keyID) != null; } @@ -170,14 +198,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - /// - /// Return a new bundle containing the contents of the passed in bundle and - /// the passed in public key ring. - /// - /// The PgpPublicKeyRingBundle the key ring is to be added to. - /// The key ring to be added. - /// A new PgpPublicKeyRingBundle merging the current one with the passed in key ring. - /// If the keyId for the passed in key ring is already present. + private ICollection KeyRings => m_pubRings.Values; + + /// + /// Return a new bundle containing the contents of the passed in bundle and + /// the passed in public key ring. + /// + /// The PgpPublicKeyRingBundle the key ring is to be added to. + /// The key ring to be added. + /// A new PgpPublicKeyRingBundle merging the current one with the passed in key ring. + /// If the keyId for the passed in key ring is already present. public static PgpPublicKeyRingBundle AddPublicKeyRing(PgpPublicKeyRingBundle bundle, PgpPublicKeyRing publicKeyRing) { diff --git a/crypto/src/openpgp/PgpSecretKeyRing.cs b/crypto/src/openpgp/PgpSecretKeyRing.cs index a070aa132..ff644545f 100644 --- a/crypto/src/openpgp/PgpSecretKeyRing.cs +++ b/crypto/src/openpgp/PgpSecretKeyRing.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using Org.BouncyCastle.Security; -using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Bcpg.OpenPgp @@ -115,6 +114,38 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return keys[0].PublicKey; } + /// Return the public key referred to by the passed in keyID if it is present. + public PgpPublicKey GetPublicKey(long keyID) + { + PgpSecretKey key = GetSecretKey(keyID); + if (key != null) + return key.PublicKey; + + foreach (PgpPublicKey k in extraPubKeys) + { + if (keyID == k.KeyId) + return k; + } + + return null; + } + + /// Return the public key with the passed in fingerprint if it is present. + public PgpPublicKey GetPublicKey(byte[] fingerprint) + { + PgpSecretKey key = GetSecretKey(fingerprint); + if (key != null) + return key.PublicKey; + + foreach (PgpPublicKey k in extraPubKeys) + { + if (k.HasFingerprint(fingerprint)) + return k; + } + + return null; + } + /** * Return any keys carrying a signature issued by the key represented by keyID. * @@ -165,6 +196,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return CollectionUtilities.Proxy(keys); } + /// Return the secret key referred to by the passed in keyID if it is present. public PgpSecretKey GetSecretKey(long keyId) { foreach (PgpSecretKey k in keys) @@ -176,6 +208,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return null; } + /// Return the secret key associated with the passed in fingerprint if it is present. + public PgpSecretKey GetSecretKey(byte[] fingerprint) + { + foreach (PgpSecretKey k in keys) + { + if (k.PublicKey.HasFingerprint(fingerprint)) + return k; + } + + return null; + } + /// /// Return an iterator of the public keys in the secret key ring that /// have no matching private key. At the moment only personal certificate data @@ -247,7 +291,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp { var newKeys = new List(ring.keys.Count); - foreach (PgpSecretKey secretKey in ring.GetSecretKeys()) + foreach (PgpSecretKey secretKey in ring.keys) { if (secretKey.IsPrivateKeyEmpty) { diff --git a/crypto/src/openpgp/PgpSecretKeyRingBundle.cs b/crypto/src/openpgp/PgpSecretKeyRingBundle.cs index 695c882b7..fe9a2461a 100644 --- a/crypto/src/openpgp/PgpSecretKeyRingBundle.cs +++ b/crypto/src/openpgp/PgpSecretKeyRingBundle.cs @@ -8,10 +8,10 @@ using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Bcpg.OpenPgp { - /// - /// Often a PGP key ring file is made up of a succession of master/sub-key key rings. - /// If you want to read an entire secret key file in one hit this is the class for you. - /// + /// + /// Often a PGP key ring file is made up of a succession of master/sub-key key rings. + /// If you want to read an entire secret key file in one hit this is the class for you. + /// public class PgpSecretKeyRingBundle { private readonly IDictionary m_secretRings; @@ -66,7 +66,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// Allow enumeration of the secret key rings making up this collection. public IEnumerable GetKeyRings() { - return CollectionUtilities.Proxy(m_secretRings.Values); + return CollectionUtilities.Proxy(KeyRings); } /// Allow enumeration of the key rings associated with the passed in userId. @@ -96,7 +96,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp var compareInfo = CultureInfo.InvariantCulture.CompareInfo; var compareOptions = ignoreCase ? CompareOptions.OrdinalIgnoreCase : CompareOptions.Ordinal; - foreach (PgpSecretKeyRing secRing in GetKeyRings()) + foreach (PgpSecretKeyRing secRing in KeyRings) { foreach (string nextUserID in secRing.GetSecretKey().UserIds) { @@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// The ID of the secret key to return. public PgpSecretKey GetSecretKey(long keyId) { - foreach (PgpSecretKeyRing secRing in GetKeyRings()) + foreach (PgpSecretKeyRing secRing in KeyRings) { PgpSecretKey sec = secRing.GetSecretKey(keyId); if (sec != null) @@ -135,7 +135,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp if (m_secretRings.TryGetValue(keyId, out var keyRing)) return keyRing; - foreach (PgpSecretKeyRing secretRing in GetKeyRings()) + foreach (PgpSecretKeyRing secretRing in KeyRings) { if (secretRing.GetSecretKey(keyId) != null) return secretRing; @@ -170,14 +170,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - /// - /// Return a new bundle containing the contents of the passed in bundle and - /// the passed in secret key ring. - /// - /// The PgpSecretKeyRingBundle the key ring is to be added to. - /// The key ring to be added. - /// A new PgpSecretKeyRingBundle merging the current one with the passed in key ring. - /// If the keyId for the passed in key ring is already present. + private ICollection KeyRings => m_secretRings.Values; + + /// + /// Return a new bundle containing the contents of the passed in bundle and + /// the passed in secret key ring. + /// + /// The PgpSecretKeyRingBundle the key ring is to be added to. + /// The key ring to be added. + /// A new PgpSecretKeyRingBundle merging the current one with the passed in key ring. + /// If the keyId for the passed in key ring is already present. public static PgpSecretKeyRingBundle AddSecretKeyRing(PgpSecretKeyRingBundle bundle, PgpSecretKeyRing secretKeyRing) { diff --git a/crypto/test/src/openpgp/test/PGPRSATest.cs b/crypto/test/src/openpgp/test/PGPRSATest.cs index 6de95fbeb..56d761c16 100644 --- a/crypto/test/src/openpgp/test/PGPRSATest.cs +++ b/crypto/test/src/openpgp/test/PGPRSATest.cs @@ -325,7 +325,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests PgpPublicKey pubKey = pgpPub.GetPublicKey(); - if (!Arrays.AreEqual(pubKey.GetFingerprint(), Hex.Decode("4FFB9F0884266C715D1CEAC804A3BBFA"))) + byte[] expectedVersion3 = Hex.Decode("4FFB9F0884266C715D1CEAC804A3BBFA"); + if (!Arrays.AreEqual(pubKey.GetFingerprint(), expectedVersion3)) + { + Fail("version 3 fingerprint test failed"); + } + if (!pubKey.HasFingerprint(expectedVersion3)) { Fail("version 3 fingerprint test failed"); } @@ -337,10 +342,15 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests pubKey = pgpPub.GetPublicKey(); - if (!Arrays.AreEqual(pubKey.GetFingerprint(), Hex.Decode("3062363c1046a01a751946bb35586146fdf3f373"))) + byte[] expectedVersion4 = Hex.Decode("3062363c1046a01a751946bb35586146fdf3f373"); + if (!Arrays.AreEqual(pubKey.GetFingerprint(), expectedVersion4)) { Fail("version 4 fingerprint test failed"); } + if (!pubKey.HasFingerprint(expectedVersion4)) + { + Fail("version 4 fingerprint test failed"); + } } private void MixedTest( diff --git a/crypto/test/src/openpgp/test/PgpEdDsaTest.cs b/crypto/test/src/openpgp/test/PgpEdDsaTest.cs index f67d19a7f..c3cdd53f0 100644 --- a/crypto/test/src/openpgp/test/PgpEdDsaTest.cs +++ b/crypto/test/src/openpgp/test/PgpEdDsaTest.cs @@ -202,8 +202,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests PgpPublicKeyRing pubKeyRing = new PgpPublicKeyRing(aIn); - IsTrue(AreEqual(Hex.Decode("EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E"), - pubKeyRing.GetPublicKey().GetFingerprint())); + IsTrue(AreEqual(pubKeyRing.GetPublicKey().GetFingerprint(), + Hex.Decode("EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E"))); + IsTrue(pubKeyRing.GetPublicKey().HasFingerprint( + Hex.Decode("EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E"))); aIn = new ArmoredInputStream(new MemoryStream(Strings.ToByteArray(edDSASecretKey), false)); -- cgit 1.4.1