using System; using System.Collections; using System.IO; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Bcpg.OpenPgp { /// /// Class to hold a single master public key and its subkeys. ///

/// Often PGP keyring files consist of multiple master keys, if you are trying to process /// or construct one of these you should use the PgpPublicKeyRingBundle class. ///

///
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)); } } /// Return the first public key in the ring. public PgpPublicKey GetPublicKey() { return (PgpPublicKey) keys[0]; } /// Return the public key referred to by the passed in key ID if it is present. public PgpPublicKey GetPublicKey( long keyId) { foreach (PgpPublicKey k in keys) { if (keyId == k.KeyId) { return k; } } return null; } /// Allows enumeration of all the public keys. /// An IEnumerable of PgpPublicKey objects. 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); } } /// /// Returns a new key ring with the public key passed in either added or /// replacing an existing one. /// /// The public key ring to be modified. /// The public key to be inserted. /// A new PgpPublicKeyRing 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); } /// Returns a new key ring with the public key passed in removed from the key ring. /// The public key ring to be modified. /// The public key to be removed. /// A new PgpPublicKeyRing, or null if pubKey is not found. 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); } } }