context: using System;
  using System.Collections;
  using System.IO;
  
  using Org.BouncyCastle.Security;
  using Org.BouncyCastle.Utilities;
  using Org.BouncyCastle.Utilities.Collections;
  
  namespace Org.BouncyCastle.Bcpg.OpenPgp
  {
      /// <remarks>
      /// Class to hold a single master secret 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>PgpSecretKeyRingBundle</c> class.
      /// </p>
      /// </remarks>
      public class PgpSecretKeyRing
          : PgpKeyRing
      {
          private readonly IList keys;
          private readonly IList extraPubKeys;
  
          internal PgpSecretKeyRing(
              IList keys)
              : this(keys, Platform.CreateArrayList())
          {
          }
  
          private PgpSecretKeyRing(
              IList	keys,
              IList	extraPubKeys)
          {
              this.keys = keys;
              this.extraPubKeys = extraPubKeys;
          }
  
          public PgpSecretKeyRing(
              byte[] encoding)
              : this(new MemoryStream(encoding))
          {
          }
  
          public PgpSecretKeyRing(
              Stream inputStream)
          {
              this.keys = Platform.CreateArrayList();
              this.extraPubKeys = Platform.CreateArrayList();
  
              BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream);
  
              PacketTag initialTag = bcpgInput.NextPacketTag();
              if (initialTag != PacketTag.SecretKey && initialTag != PacketTag.SecretSubkey)
              {
                  throw new IOException("secret key ring doesn't start with secret key tag: "
                      + "tag 0x" + ((int)initialTag).ToString("X"));
              }
  
              SecretKeyPacket secret = (SecretKeyPacket) bcpgInput.ReadPacket();
  
              //
              // ignore GPG comment packets if found.
              //
              while (bcpgInput.NextPacketTag() == PacketTag.Experimental2)
              {
                  bcpgInput.ReadPacket();
              }
  
              TrustPacket trust = ReadOptionalTrustPacket(bcpgInput);
  
              // revocation and direct signatures
              IList keySigs = ReadSignaturesAndTrust(bcpgInput);
  
              IList ids, idTrusts, idSigs;
              ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs);
  
              keys.Add(new PgpSecretKey(secret, new PgpPublicKey(secret.PublicKeyPacket, trust, keySigs, ids, idTrusts, idSigs)));
  
  
              // Read subkeys
              while (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey
                  || bcpgInput.NextPacketTag() == PacketTag.PublicSubkey)
              {
                  if (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey)
                  {
                      SecretSubkeyPacket sub = (SecretSubkeyPacket) bcpgInput.ReadPacket();
  
                      //
                      // ignore GPG comment packets if found.
                      //
                      while (bcpgInput.NextPacketTag() == PacketTag.Experimental2)
                      {
                          bcpgInput.ReadPacket();
                      }
  
                      TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput);
                      IList sigList = ReadSignaturesAndTrust(bcpgInput);
  
                      keys.Add(new PgpSecretKey(sub, new PgpPublicKey(sub.PublicKeyPacket, subTrust, sigList)));
                  }
                  else
                  {
                      PublicSubkeyPacket sub = (PublicSubkeyPacket) bcpgInput.ReadPacket();
  
                      TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput);
                      IList sigList = ReadSignaturesAndTrust(bcpgInput);
  
                      extraPubKeys.Add(new PgpPublicKey(sub, subTrust, sigList));
                  }
              }
          }
  
          /// <summary>Return the public key for the master key.</summary>
          public PgpPublicKey GetPublicKey()
          {
              return ((PgpSecretKey) keys[0]).PublicKey;
          }
  
          /// <summary>Return the master private key.</summary>
          public PgpSecretKey GetSecretKey()
          {
              return (PgpSecretKey) keys[0];
          }
  
          /// <summary>Allows enumeration of the secret keys.</summary>
          /// <returns>An <c>IEnumerable</c> of <c>PgpSecretKey</c> objects.</returns>
          public IEnumerable GetSecretKeys()
          {
              return new EnumerableProxy(keys);
          }
  
          public PgpSecretKey GetSecretKey(
              long keyId)
          {
              foreach (PgpSecretKey k in keys)
              {
                  if (keyId == k.KeyId)
                  {
                      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
          /// appears in this fashion.
          /// </summary>
          /// <returns>An <c>IEnumerable</c> of unattached, or extra, public keys.</returns>
          public IEnumerable GetExtraPublicKeys()
          {
              return new EnumerableProxy(extraPubKeys);
          }
  
          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 (PgpSecretKey key in keys)
              {
                  key.Encode(outStr);
              }
              foreach (PgpPublicKey extraPubKey in extraPubKeys)
              {
                  extraPubKey.Encode(outStr);
              }
          }
  
          /// <summary>
          /// Replace the public key set on the secret ring with the corresponding key off the public ring.
          /// </summary>
          /// <param name="secretRing">Secret ring to be changed.</param>
          /// <param name="publicRing">Public ring containing the new public key set.</param>
          public static PgpSecretKeyRing ReplacePublicKeys(
              PgpSecretKeyRing	secretRing,
              PgpPublicKeyRing	publicRing)
          {
              IList newList = Platform.CreateArrayList(secretRing.keys.Count);
  
              foreach (PgpSecretKey sk in secretRing.keys)
              {
                  PgpPublicKey pk = publicRing.GetPublicKey(sk.KeyId);
  
                  newList.Add(PgpSecretKey.ReplacePublicKey(sk, pk));
              }
  
              return new PgpSecretKeyRing(newList);
          }
  
          /// <summary>
          /// Return a copy of the passed in secret key ring, with the master key and sub keys encrypted
          /// using a new password and the passed in algorithm.
          /// </summary>
          /// <param name="ring">The <c>PgpSecretKeyRing</c> to be copied.</param>
          /// <param name="oldPassPhrase">The current password for key.</param>
          /// <param name="newPassPhrase">The new password for the key.</param>
          /// <param name="newEncAlgorithm">The algorithm to be used for the encryption.</param>
          /// <param name="rand">Source of randomness.</param>
          public static PgpSecretKeyRing CopyWithNewPassword(
              PgpSecretKeyRing			ring,
              char[]						oldPassPhrase,
              char[]						newPassPhrase,
              SymmetricKeyAlgorithmTag	newEncAlgorithm,
              SecureRandom				rand)
          {
              IList newKeys = Platform.CreateArrayList(ring.keys.Count);
              foreach (PgpSecretKey secretKey in ring.GetSecretKeys())
              {
                  if (secretKey.IsPrivateKeyEmpty)
                  {
                      newKeys.Add(secretKey);
                  }
                  else
                  {
                      newKeys.Add(PgpSecretKey.CopyWithNewPassword(secretKey, oldPassPhrase, newPassPhrase, newEncAlgorithm, rand));
                  }
              }
  
              return new PgpSecretKeyRing(newKeys, ring.extraPubKeys);
          }
  
          /// <summary>
          /// Returns a new key ring with the secret key passed in either added or
          /// replacing an existing one with the same key ID.
          /// </summary>
          /// <param name="secRing">The secret key ring to be modified.</param>
          /// <param name="secKey">The secret key to be inserted.</param>
          /// <returns>A new <c>PgpSecretKeyRing</c></returns>
          public static PgpSecretKeyRing InsertSecretKey(
              PgpSecretKeyRing  secRing,
              PgpSecretKey      secKey)
          {
              IList keys = Platform.CreateArrayList(secRing.keys);
              bool found = false;
              bool masterFound = false;
  
              for (int i = 0; i != keys.Count; i++)
              {
                  PgpSecretKey key = (PgpSecretKey) keys[i];
  
  
gt:                 if (key.KeyId == secKey.KeyId)