using System; using System.Collections; using System.IO; using Org.BouncyCastle.Utilities; 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. /// public class PgpPublicKeyRingBundle { private readonly IDictionary pubRings; private readonly IList order; private PgpPublicKeyRingBundle( IDictionary pubRings, IList order) { this.pubRings = pubRings; this.order = order; } public PgpPublicKeyRingBundle( byte[] encoding) : this(new MemoryStream(encoding, false)) { } /// Build a PgpPublicKeyRingBundle from the passed in input stream. /// Input stream containing data. /// If a problem parsing the stream occurs. /// If an object is encountered which isn't a PgpPublicKeyRing. public PgpPublicKeyRingBundle( Stream inputStream) : this(new PgpObjectFactory(inputStream).AllPgpObjects()) { } public PgpPublicKeyRingBundle( IEnumerable e) { this.pubRings = Platform.CreateHashtable(); this.order = Platform.CreateArrayList(); foreach (object obj in e) { PgpPublicKeyRing pgpPub = obj as PgpPublicKeyRing; if (pgpPub == null) { throw new PgpException(Platform.GetTypeName(obj) + " found where PgpPublicKeyRing expected"); } long key = pgpPub.GetPublicKey().KeyId; pubRings.Add(key, pgpPub); order.Add(key); } } [Obsolete("Use 'Count' property instead")] public int Size { get { return order.Count; } } /// Return the number of key rings in this collection. public int Count { get { return order.Count; } } /// Allow enumeration of the public key rings making up this collection. public IEnumerable GetKeyRings() { return new EnumerableProxy(pubRings.Values); } /// Allow enumeration of the key rings associated with the passed in userId. /// The user ID to be matched. /// An IEnumerable of key rings which matched (possibly none). public IEnumerable GetKeyRings( string userId) { return GetKeyRings(userId, false, false); } /// Allow enumeration of the key rings associated with the passed in userId. /// The user ID to be matched. /// If true, userId need only be a substring of an actual ID string to match. /// An IEnumerable of key rings which matched (possibly none). public IEnumerable GetKeyRings( string userId, bool matchPartial) { return GetKeyRings(userId, matchPartial, false); } /// Allow enumeration of the key rings associated with the passed in userId. /// The user ID to be matched. /// If true, userId need only be a substring of an actual ID string to match. /// If true, case is ignored in user ID comparisons. /// An IEnumerable of key rings which matched (possibly none). public IEnumerable GetKeyRings( string userId, bool matchPartial, bool ignoreCase) { IList rings = Platform.CreateArrayList(); if (ignoreCase) { userId = Platform.ToUpperInvariant(userId); } foreach (PgpPublicKeyRing pubRing in GetKeyRings()) { foreach (string nextUserID in pubRing.GetPublicKey().GetUserIds()) { string next = nextUserID; if (ignoreCase) { next = Platform.ToUpperInvariant(next); } if (matchPartial) { if (Platform.IndexOf(next, userId) > -1) { rings.Add(pubRing); } } else { if (next.Equals(userId)) { rings.Add(pubRing); } } } } return new EnumerableProxy(rings); } /// Return the PGP public key associated with the given key id. /// The ID of the public key to return. public PgpPublicKey GetPublicKey( long keyId) { foreach (PgpPublicKeyRing pubRing in GetKeyRings()) { PgpPublicKey pub = pubRing.GetPublicKey(keyId); if (pub != null) { return pub; } } return null; } /// Return the public key ring which contains the key referred to by keyId /// key ID to match against public PgpPublicKeyRing GetPublicKeyRing( long keyId) { if (pubRings.Contains(keyId)) { return (PgpPublicKeyRing)pubRings[keyId]; } foreach (PgpPublicKeyRing pubRing in GetKeyRings()) { PgpPublicKey pub = pubRing.GetPublicKey(keyId); if (pub != 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; } public byte[] GetEncoded() { MemoryStream bOut = new MemoryStream(); Encode(bOut); return bOut.ToArray(); } public void Encode( Stream outStr) { BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); foreach (long key in order) { PgpPublicKeyRing sec = (PgpPublicKeyRing) pubRings[key]; sec.Encode(bcpgOut); } } /// /// 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) { long key = publicKeyRing.GetPublicKey().KeyId; if (bundle.pubRings.Contains(key)) { throw new ArgumentException("Bundle already contains a key with a keyId for the passed in ring."); } IDictionary newPubRings = Platform.CreateHashtable(bundle.pubRings); IList newOrder = Platform.CreateArrayList(bundle.order); newPubRings[key] = publicKeyRing; newOrder.Add(key); return new PgpPublicKeyRingBundle(newPubRings, newOrder); } /// /// Return a new bundle containing the contents of the passed in bundle with /// the passed in public key ring removed. /// /// The PgpPublicKeyRingBundle the key ring is to be removed from. /// The key ring to be removed. /// A new PgpPublicKeyRingBundle not containing the passed in key ring. /// If the keyId for the passed in key ring is not present. public static PgpPublicKeyRingBundle RemovePublicKeyRing( PgpPublicKeyRingBundle bundle, PgpPublicKeyRing publicKeyRing) { long key = publicKeyRing.GetPublicKey().KeyId; if (!bundle.pubRings.Contains(key)) { throw new ArgumentException("Bundle does not contain a key with a keyId for the passed in ring."); } IDictionary newPubRings = Platform.CreateHashtable(bundle.pubRings); IList newOrder = Platform.CreateArrayList(bundle.order); newPubRings.Remove(key); newOrder.Remove(key); return new PgpPublicKeyRingBundle(newPubRings, newOrder); } } }