diff --git a/Crypto/src/openpgp/PgpPublicKeyRing.cs b/Crypto/src/openpgp/PgpPublicKeyRing.cs
new file mode 100644
index 000000000..ecb935e4b
--- /dev/null
+++ b/Crypto/src/openpgp/PgpPublicKeyRing.cs
@@ -0,0 +1,200 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+ /// <remarks>
+ /// Class to hold a single master public key and its subkeys.
+ /// <p>
+ /// Often PGP keyring files consist of multiple master keys, if you are trying to process
+ /// or construct one of these you should use the <c>PgpPublicKeyRingBundle</c> class.
+ /// </p>
+ /// </remarks>
+ public class PgpPublicKeyRing
+ : PgpKeyRing
+ {
+ private readonly IList keys;
+
+ public PgpPublicKeyRing(
+ byte[] encoding)
+ : this(new MemoryStream(encoding, false))
+ {
+ }
+
+ internal PgpPublicKeyRing(
+ IList pubKeys)
+ {
+ this.keys = pubKeys;
+ }
+
+ public PgpPublicKeyRing(
+ Stream inputStream)
+ {
+ this.keys = Platform.CreateArrayList();
+
+ BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream);
+
+ PacketTag initialTag = bcpgInput.NextPacketTag();
+ if (initialTag != PacketTag.PublicKey && initialTag != PacketTag.PublicSubkey)
+ {
+ throw new IOException("public key ring doesn't start with public key tag: "
+ + "tag 0x" + ((int)initialTag).ToString("X"));
+ }
+
+ PublicKeyPacket pubPk = (PublicKeyPacket) bcpgInput.ReadPacket();;
+ TrustPacket trustPk = ReadOptionalTrustPacket(bcpgInput);
+
+ // direct signatures and revocations
+ IList keySigs = ReadSignaturesAndTrust(bcpgInput);
+
+ IList ids, idTrusts, idSigs;
+ ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs);
+
+ keys.Add(new PgpPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs));
+
+
+ // Read subkeys
+ while (bcpgInput.NextPacketTag() == PacketTag.PublicSubkey)
+ {
+ keys.Add(ReadSubkey(bcpgInput));
+ }
+ }
+
+ /// <summary>Return the first public key in the ring.</summary>
+ public PgpPublicKey GetPublicKey()
+ {
+ return (PgpPublicKey) keys[0];
+ }
+
+ /// <summary>Return the public key referred to by the passed in key ID if it is present.</summary>
+ public PgpPublicKey GetPublicKey(
+ long keyId)
+ {
+ foreach (PgpPublicKey k in keys)
+ {
+ if (keyId == k.KeyId)
+ {
+ 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 IEnumerable GetPublicKeys()
+ {
+ return new EnumerableProxy(keys);
+ }
+
+ public byte[] GetEncoded()
+ {
+ MemoryStream bOut = new MemoryStream();
+
+ Encode(bOut);
+
+ return bOut.ToArray();
+ }
+
+ public void Encode(
+ Stream outStr)
+ {
+ if (outStr == null)
+ throw new ArgumentNullException("outStr");
+
+ foreach (PgpPublicKey k in keys)
+ {
+ k.Encode(outStr);
+ }
+ }
+
+ /// <summary>
+ /// Returns a new key ring with the public key passed in either added or
+ /// replacing an existing one.
+ /// </summary>
+ /// <param name="pubRing">The public key ring to be modified.</param>
+ /// <param name="pubKey">The public key to be inserted.</param>
+ /// <returns>A new <c>PgpPublicKeyRing</c></returns>
+ public static PgpPublicKeyRing InsertPublicKey(
+ PgpPublicKeyRing pubRing,
+ PgpPublicKey pubKey)
+ {
+ IList keys = Platform.CreateArrayList(pubRing.keys);
+ bool found = false;
+ bool masterFound = false;
+
+ for (int i = 0; i != keys.Count; i++)
+ {
+ PgpPublicKey key = (PgpPublicKey) keys[i];
+
+ if (key.KeyId == pubKey.KeyId)
+ {
+ found = true;
+ keys[i] = pubKey;
+ }
+ if (key.IsMasterKey)
+ {
+ masterFound = true;
+ }
+ }
+
+ if (!found)
+ {
+ if (pubKey.IsMasterKey)
+ {
+ if (masterFound)
+ throw new ArgumentException("cannot add a master key to a ring that already has one");
+
+ keys.Insert(0, pubKey);
+ }
+ else
+ {
+ keys.Add(pubKey);
+ }
+ }
+
+ return new PgpPublicKeyRing(keys);
+ }
+
+ /// <summary>Returns a new key ring with the public key passed in removed from the key ring.</summary>
+ /// <param name="pubRing">The public key ring to be modified.</param>
+ /// <param name="pubKey">The public key to be removed.</param>
+ /// <returns>A new <c>PgpPublicKeyRing</c>, or null if pubKey is not found.</returns>
+ public static PgpPublicKeyRing RemovePublicKey(
+ PgpPublicKeyRing pubRing,
+ PgpPublicKey pubKey)
+ {
+ IList keys = Platform.CreateArrayList(pubRing.keys);
+ bool found = false;
+
+ for (int i = 0; i < keys.Count; i++)
+ {
+ PgpPublicKey key = (PgpPublicKey) keys[i];
+
+ if (key.KeyId == pubKey.KeyId)
+ {
+ found = true;
+ keys.RemoveAt(i);
+ }
+ }
+
+ return found ? new PgpPublicKeyRing(keys) : null;
+ }
+
+ internal static PgpPublicKey ReadSubkey(BcpgInputStream bcpgInput)
+ {
+ PublicKeyPacket pk = (PublicKeyPacket) bcpgInput.ReadPacket();
+ TrustPacket kTrust = ReadOptionalTrustPacket(bcpgInput);
+
+ // PGP 8 actually leaves out the signature.
+ IList sigList = ReadSignaturesAndTrust(bcpgInput);
+
+ return new PgpPublicKey(pk, kTrust, sigList);
+ }
+ }
+}
|