context: using System;
  using System.Collections;
  using System.IO;
  using Org.BouncyCastle.Asn1;
  using Org.BouncyCastle.Asn1.X509;
  using Org.BouncyCastle.Asn1.Pkcs;
  using Org.BouncyCastle.X509;
  using Org.BouncyCastle.OpenSsl;
  using Org.BouncyCastle.Security.Certificates;
  using Org.BouncyCastle.Utilities;
  using Org.BouncyCastle.Utilities.Collections;
  
  namespace Org.BouncyCastle.Pkix
  {
  	/**
  	 * An immutable sequence of certificates (a certification path).<br />
  	 * <br />
  	 * This is an abstract class that defines the methods common to all CertPaths.
  	 * Subclasses can handle different kinds of certificates (X.509, PGP, etc.).<br />
  	 * <br />
  	 * All CertPath objects have a type, a list of Certificates, and one or more
  	 * supported encodings. Because the CertPath class is immutable, a CertPath
  	 * cannot change in any externally visible way after being constructed. This
  	 * stipulation applies to all public fields and methods of this class and any
  	 * added or overridden by subclasses.<br />
  	 * <br />
  	 * The type is a string that identifies the type of Certificates in the
  	 * certification path. For each certificate cert in a certification path
  	 * certPath, cert.getType().equals(certPath.getType()) must be true.<br />
  	 * <br />
  	 * The list of Certificates is an ordered List of zero or more Certificates.
  	 * This List and all of the Certificates contained in it must be immutable.<br />
  	 * <br />
  	 * Each CertPath object must support one or more encodings so that the object
  	 * can be translated into a byte array for storage or transmission to other
  	 * parties. Preferably, these encodings should be well-documented standards
  	 * (such as PKCS#7). One of the encodings supported by a CertPath is considered
  	 * the default encoding. This encoding is used if no encoding is explicitly
  	 * requested (for the {@link #getEncoded()} method, for instance).<br />
  	 * <br />
  	 * All CertPath objects are also Serializable. CertPath objects are resolved
  	 * into an alternate {@link CertPathRep} object during serialization. This
  	 * allows a CertPath object to be serialized into an equivalent representation
  	 * regardless of its underlying implementation.<br />
  	 * <br />
  	 * CertPath objects can be created with a CertificateFactory or they can be
  	 * returned by other classes, such as a CertPathBuilder.<br />
  	 * <br />
  	 * By convention, X.509 CertPaths (consisting of X509Certificates), are ordered
  	 * starting with the target certificate and ending with a certificate issued by
  	 * the trust anchor. That is, the issuer of one certificate is the subject of
  	 * the following one. The certificate representing the
  	 * {@link TrustAnchor TrustAnchor} should not be included in the certification
  	 * path. Unvalidated X.509 CertPaths may not follow these conventions. PKIX
  	 * CertPathValidators will detect any departure from these conventions that
  	 * cause the certification path to be invalid and throw a
  	 * CertPathValidatorException.<br />
  	 * <br />
  	 * <strong>Concurrent Access</strong><br />
  	 * <br />
  	 * All CertPath objects must be thread-safe. That is, multiple threads may
  	 * concurrently invoke the methods defined in this class on a single CertPath
  	 * object (or more than one) with no ill effects. This is also true for the List
  	 * returned by CertPath.getCertificates.<br />
  	 * <br />
  	 * Requiring CertPath objects to be immutable and thread-safe allows them to be
  	 * passed around to various pieces of code without worrying about coordinating
  	 * access. Providing this thread-safety is generally not difficult, since the
  	 * CertPath and List objects in question are immutable.
  	 *
  	 * @see CertificateFactory
  	 * @see CertPathBuilder
  	 */
  	/// <summary>
  	/// CertPath implementation for X.509 certificates.
  	/// </summary>
  	public class PkixCertPath
  //		: CertPath
  	{
  		internal static readonly IList certPathEncodings;
  
          static PkixCertPath()
          {
              IList encodings = Platform.CreateArrayList();
              encodings.Add("PkiPath");
              encodings.Add("PEM");
              encodings.Add("PKCS7");
              certPathEncodings = CollectionUtilities.ReadOnly(encodings);
          }
  
          private readonly IList certificates;
  
  		/**
  		 * @param certs
  		 */
  		private static IList SortCerts(
  			IList certs)
  		{
  			if (certs.Count < 2)
  				return certs;
  
  			X509Name issuer = ((X509Certificate)certs[0]).IssuerDN;
  			bool okay = true;
  
  			for (int i = 1; i != certs.Count; i++)
  			{
  				X509Certificate cert = (X509Certificate)certs[i];
  
  				if (issuer.Equivalent(cert.SubjectDN, true))
  				{
  					issuer = ((X509Certificate)certs[i]).IssuerDN;
  				}
  				else
  				{
  					okay = false;
  					break;
  				}
  			}
  
  			if (okay)
  				return certs;
  
  			// find end-entity cert
              IList retList = Platform.CreateArrayList(certs.Count);
              IList orig = Platform.CreateArrayList(certs);
  
  			for (int i = 0; i < certs.Count; i++)
  			{
  				X509Certificate cert = (X509Certificate)certs[i];
  				bool           found = false;
  
  				X509Name subject = cert.SubjectDN;
  				foreach (X509Certificate c in certs)
  				{
  					if (c.IssuerDN.Equivalent(subject, true))
  					{
  						found = true;
  						break;
  					}
  				}
  
  				if (!found)
  				{
  					retList.Add(cert);
  					certs.RemoveAt(i);
  				}
  			}
  
  			// can only have one end entity cert - something's wrong, give up.
  			if (retList.Count > 1)
  				return orig;
  
  			for (int i = 0; i != retList.Count; i++)
  			{
  				issuer = ((X509Certificate)retList[i]).IssuerDN;
  
  				for (int j = 0; j < certs.Count; j++)
  				{
  					X509Certificate c = (X509Certificate)certs[j];
  					if (issuer.Equivalent(c.SubjectDN, true))
  					{
  						retList.Add(c);
  						certs.RemoveAt(j);
  						break;
  					}
  				}
  			}
  
  			// make sure all certificates are accounted for.
  			if (certs.Count > 0)
  				return orig;
  
  			return retList;
  		}
  
  		/**
  		 * Creates a CertPath of the specified type.
  		 * This constructor is protected because most users should use
  		 * a CertificateFactory to create CertPaths.
  		 * @param type the standard name of the type of Certificatesin this path
  		 **/
  		public PkixCertPath(
  			ICollection certificates)
  //			: base("X.509")
  		{
  			this.certificates = SortCerts(Platform.CreateArrayList(certificates));
  		}
  
  		public PkixCertPath(
  			Stream inStream)
  			: this(inStream, "PkiPath")
  		{
  		}
  
  		/**
  		 * Creates a CertPath of the specified type.
  		 * This constructor is protected because most users should use
  		 * a CertificateFactory to create CertPaths.
  		 *
  		 * @param type the standard name of the type of Certificatesin this path
  		 **/
  		public PkixCertPath(
  			Stream	inStream,
  			string	encoding)
  //			: base("X.509")
  		{
              string upper = encoding.ToUpper();
  
              IList certs;
  			try
  			{
  				if (upper.Equals("PkiPath".ToUpper()))
  				{
  					Asn1InputStream derInStream = new Asn1InputStream(inStream);
  					Asn1Object derObject = derInStream.ReadObject();
  					if (!(derObject is Asn1Sequence))
  					{
  						throw new CertificateException(
  							"input stream does not contain a ASN1 SEQUENCE while reading PkiPath encoded data to load CertPath");
  					}
  
                      certs = Platform.CreateArrayList();
  
                      foreach (Asn1Encodable ae in (Asn1Sequence)derObject)
                      {
                          byte[] derBytes = ae.GetEncoded(Asn1Encodable.Der);
                          Stream certInStream = new MemoryStream(derBytes, false);
  
                          // TODO Is inserting at the front important (list will be sorted later anyway)?
                          certs.Insert(0, new X509CertificateParser().ReadCertificate(certInStream));
  					}
  				}
                  else if (upper.Equals("PKCS7") || upper.Equals("PEM"))
  				{
                      certs = Platform.CreateArrayList(new X509CertificateParser().ReadCertificates(inStream));
  				}
  				else
  				{
  					throw new CertificateException("unsupported encoding: " + encoding);
  				}
  			}
  			catch (IOException ex)
  			{
  				throw new CertificateException(
  					"IOException throw while decoding CertPath:\n"
  					+ ex.ToString());
  			}
  
  			this.certificates = SortCerts(certs);
  		}
  
  		/**
  		 * Returns an iteration of the encodings supported by this
  		 * certification path, with the default encoding
  		 * first. Attempts to modify the returned Iterator via its
  		 * remove method result in an UnsupportedOperationException.
  		 *
  		 * @return an Iterator over the names of the supported encodings (as Strings)
  		 **/
  		public virtual IEnumerable Encodings
  		{
              get { return new EnumerableProxy(certPathEncodings); }
  		}
  
  		/**
  		* Compares this certification path for equality with the specified object.
  		* Two CertPaths are equal if and only if their types are equal and their
  		* certificate Lists (and by implication the Certificates in those Lists)
  		* are equal. A CertPath is never equal to an object that is not a CertPath.<br />
  		* <br />
  		* This algorithm is implemented by this method. If it is overridden, the
  		* behavior specified here must be maintained.
  		*
  		* @param other
  		*            the object to test for equality with this certification path
  		*
  		* @return true if the specified object is equal to this certification path,
  		*         false otherwise
  		*
  		* @see Object#hashCode() Object.hashCode()
  		*/
  		public override bool Equals(
  			object obj)
  		{
  			if (this == obj)
  				return true;
  
  			PkixCertPath other = obj as PkixCertPath;
  			if (other == null)
  				return false;
  
  //			if (!this.Type.Equals(other.Type))
  //				return false;
  
  			//return this.Certificates.Equals(other.Certificates);
  
  			// TODO Extract this to a utility class
  			IList thisCerts = this.Certificates;
  			IList otherCerts = other.Certificates;
  
  			if (thisCerts.Count != otherCerts.Count)
  				return false;
  
  			IEnumerator e1 = thisCerts.GetEnumerator();
  			IEnumerator e2 = thisCerts.GetEnumerator();
  
  			while (e1.MoveNext())
  			{
  				e2.MoveNext();
  
  				if (!Platform.Equals(e1.Current, e2.Current))
  					return false;
  			}
  
  			return true;
  		}
  
  		public override int GetHashCode()
  		{
  			// FIXME?
  			return this.Certificates.GetHashCode();
  		}
  
  		/**
  		 * Returns the encoded form of this certification path, using
  		 * the default encoding.
  		 *
  		 * @return the encoded bytes
  		 * @exception CertificateEncodingException if an encoding error occurs
  		 **/
  		public virtual byte[] GetEncoded()
  		{
  			foreach (object enc in Encodings)
  			{
  				if (enc is string)
  				{
  					return GetEncoded((string)enc);
  				}
  			}
  			return null;
  		}
  
  		/**
  		 * Returns the encoded form of this certification path, using
  		 * the specified encoding.
  		 *
  		 * @param encoding the name of the encoding to use
  		 * @return the encoded bytes
  		 * @exception CertificateEncodingException if an encoding error
  		 * occurs or the encoding requested is not supported
  		 *
  		 */
  		public virtual byte[] GetEncoded(
  			string encoding)
  		{
  			if (Platform.CompareIgnoreCase(encoding, "PkiPath") == 0)
  			{
  				Asn1EncodableVector v = new Asn1EncodableVector();
  
  				for (int i = certificates.Count - 1; i >= 0; i--)
  				{
  					v.Add(ToAsn1Object((X509Certificate) certificates[i]));
  				}
  
  				return ToDerEncoded(new DerSequence(v));
  			}
              else if (Platform.CompareIgnoreCase(encoding, "PKCS7") == 0)
  			{
  				Asn1.Pkcs.ContentInfo encInfo = new Asn1.Pkcs.ContentInfo(
  					PkcsObjectIdentifiers.Data, null);
  
  				Asn1EncodableVector v = new Asn1EncodableVector();
  				for (int i = 0; i != certificates.Count; i++)
  				{
  					v.Add(ToAsn1Object((X509Certificate)certificates[i]));
  				}
  
  				Asn1.Pkcs.SignedData sd = new Asn1.Pkcs.SignedData(
  					new DerInteger(1),
  					new DerSet(),
  					encInfo,
  					new DerSet(v),
  					null,
  					new DerSet());
  
  				return ToDerEncoded(new Asn1.Pkcs.ContentInfo(PkcsObjectIdentifiers.SignedData, sd));
  			}
              else if (Platform.CompareIgnoreCase(encoding, "PEM") == 0)
  			{
  				MemoryStream bOut = new MemoryStream();
  				PemWriter pWrt = new PemWriter(new StreamWriter(bOut));
  
  				try
  				{
  
gt: 					for (int i = 0; i != certificates.Count; i++)