diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2024-04-10 17:02:37 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2024-04-10 17:02:37 +0700 |
commit | 67f26538ce499d3d201fe217709542ba8360ebff (patch) | |
tree | 27845cdbc49b88405170c32a766711cbd83178b6 | |
parent | Fix CCM input length check (diff) | |
download | BouncyCastle.NET-ed25519-67f26538ce499d3d201fe217709542ba8360ebff.tar.xz |
Add various fingerprint-related methods in OpenPgp
-rw-r--r-- | crypto/src/openpgp/PgpPublicKey.cs | 16 | ||||
-rw-r--r-- | crypto/src/openpgp/PgpPublicKeyRing.cs | 19 | ||||
-rw-r--r-- | crypto/src/openpgp/PgpPublicKeyRingBundle.cs | 72 | ||||
-rw-r--r-- | crypto/src/openpgp/PgpSecretKeyRing.cs | 48 | ||||
-rw-r--r-- | crypto/src/openpgp/PgpSecretKeyRingBundle.cs | 34 | ||||
-rw-r--r-- | crypto/test/src/openpgp/test/PGPRSATest.cs | 14 | ||||
-rw-r--r-- | crypto/test/src/openpgp/test/PgpEdDsaTest.cs | 6 |
7 files changed, 153 insertions, 56 deletions
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); + } + /// <summary> /// Check if this key has an algorithm type that makes it suitable to use for encryption. /// </summary> 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; } + /// <summary>Return the public key with the passed in fingerprint if it is present.</summary> + public virtual PgpPublicKey GetPublicKey(byte[] fingerprint) + { + foreach (PgpPublicKey k in keys) + { + if (k.HasFingerprint(fingerprint)) + return k; + } + + return null; + } + /// <summary>Allows enumeration of all the public keys.</summary> /// <returns>An <c>IEnumerable</c> of <c>PgpPublicKey</c> objects.</returns> public virtual IEnumerable<PgpPublicKey> 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<long>(); - foreach (var key in second.GetPublicKeys()) + foreach (var key in second.keys) { secondKeys.Add(key.KeyId); } var merged = new List<PgpPublicKey>(); - 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 { - /// <remarks> - /// 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. - /// </remarks> + /// <remarks> + /// 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. + /// </remarks> public class PgpPublicKeyRingBundle { private readonly IDictionary<long, PgpPublicKeyRing> m_pubRings; @@ -66,7 +66,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// <summary>Allow enumeration of the public key rings making up this collection.</summary> public IEnumerable<PgpPublicKeyRing> GetKeyRings() { - return CollectionUtilities.Proxy(m_pubRings.Values); + return CollectionUtilities.Proxy(KeyRings); } /// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary> @@ -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 /// <param name="keyId">The ID of the public key to return.</param> 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; } - /// <summary> - /// Return true if a key matching the passed in key ID is present, false otherwise. - /// </summary> - /// <param name="keyID">key ID to look for.</param> - public bool Contains(long keyID) + /// <summary>Return the PGP public key associated with the given key fingerprint.</summary> + /// <param name="fingerprint">the public key fingerprint to match against.</param> + public PgpPublicKey GetPublicKey(byte[] fingerprint) + { + foreach (PgpPublicKeyRing pubRing in KeyRings) + { + PgpPublicKey pub = pubRing.GetPublicKey(fingerprint); + if (pub != null) + return pub; + } + + return null; + } + + /// <summary>Return the public key ring which contains the key associated with the given key fingerprint. + /// </summary> + /// <param name="fingerprint">the public key fingerprint to match against.</param> + public PgpPublicKeyRing GetPublicKeyRing(byte[] fingerprint) + { + foreach (PgpPublicKeyRing pubRing in KeyRings) + { + if (pubRing.GetPublicKey(fingerprint) != null) + return pubRing; + } + + return null; + } + + /// <summary> + /// Return true if a key matching the passed in key ID is present, false otherwise. + /// </summary> + /// <param name="keyID">key ID to look for.</param> + public bool Contains(long keyID) { return GetPublicKey(keyID) != null; } @@ -170,14 +198,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - /// <summary> - /// Return a new bundle containing the contents of the passed in bundle and - /// the passed in public key ring. - /// </summary> - /// <param name="bundle">The <c>PgpPublicKeyRingBundle</c> the key ring is to be added to.</param> - /// <param name="publicKeyRing">The key ring to be added.</param> - /// <returns>A new <c>PgpPublicKeyRingBundle</c> merging the current one with the passed in key ring.</returns> - /// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception> + private ICollection<PgpPublicKeyRing> KeyRings => m_pubRings.Values; + + /// <summary> + /// Return a new bundle containing the contents of the passed in bundle and + /// the passed in public key ring. + /// </summary> + /// <param name="bundle">The <c>PgpPublicKeyRingBundle</c> the key ring is to be added to.</param> + /// <param name="publicKeyRing">The key ring to be added.</param> + /// <returns>A new <c>PgpPublicKeyRingBundle</c> merging the current one with the passed in key ring.</returns> + /// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception> 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; } + /// <summary>Return the public key referred to by the passed in keyID if it is present.</summary> + 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; + } + + /// <summary>Return the public key with the passed in fingerprint if it is present.</summary> + 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); } + /// <summary>Return the secret key referred to by the passed in keyID if it is present.</summary> public PgpSecretKey GetSecretKey(long keyId) { foreach (PgpSecretKey k in keys) @@ -176,6 +208,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return null; } + /// <summary>Return the secret key associated with the passed in fingerprint if it is present.</summary> + public PgpSecretKey GetSecretKey(byte[] fingerprint) + { + foreach (PgpSecretKey k in keys) + { + if (k.PublicKey.HasFingerprint(fingerprint)) + return k; + } + + return null; + } + /// <summary> /// 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<PgpSecretKey>(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 { - /// <remarks> - /// 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. - /// </remarks> + /// <remarks> + /// 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. + /// </remarks> public class PgpSecretKeyRingBundle { private readonly IDictionary<long, PgpSecretKeyRing> m_secretRings; @@ -66,7 +66,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// <summary>Allow enumeration of the secret key rings making up this collection.</summary> public IEnumerable<PgpSecretKeyRing> GetKeyRings() { - return CollectionUtilities.Proxy(m_secretRings.Values); + return CollectionUtilities.Proxy(KeyRings); } /// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary> @@ -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 /// <param name="keyId">The ID of the secret key to return.</param> 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 } } - /// <summary> - /// Return a new bundle containing the contents of the passed in bundle and - /// the passed in secret key ring. - /// </summary> - /// <param name="bundle">The <c>PgpSecretKeyRingBundle</c> the key ring is to be added to.</param> - /// <param name="secretKeyRing">The key ring to be added.</param> - /// <returns>A new <c>PgpSecretKeyRingBundle</c> merging the current one with the passed in key ring.</returns> - /// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception> + private ICollection<PgpSecretKeyRing> KeyRings => m_secretRings.Values; + + /// <summary> + /// Return a new bundle containing the contents of the passed in bundle and + /// the passed in secret key ring. + /// </summary> + /// <param name="bundle">The <c>PgpSecretKeyRingBundle</c> the key ring is to be added to.</param> + /// <param name="secretKeyRing">The key ring to be added.</param> + /// <returns>A new <c>PgpSecretKeyRingBundle</c> merging the current one with the passed in key ring.</returns> + /// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception> 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)); |