context: using System;
  using System.Collections;
  using System.IO;
  using System.Text;
  
  using Org.BouncyCastle.Asn1;
  using Org.BouncyCastle.Asn1.Misc;
  using Org.BouncyCastle.Asn1.Utilities;
  using Org.BouncyCastle.Asn1.X509;
  using Org.BouncyCastle.Crypto;
  using Org.BouncyCastle.Math;
  using Org.BouncyCastle.Security;
  using Org.BouncyCastle.Security.Certificates;
  using Org.BouncyCastle.Utilities.Encoders;
  using Org.BouncyCastle.X509.Extension;
  
  namespace Org.BouncyCastle.X509
  {
      /// <summary>
      /// An Object representing an X509 Certificate.
      /// Has static methods for loading Certificates encoded in many forms that return X509Certificate Objects.
      /// </summary>
      public class X509Certificate
  		:	X509ExtensionBase
  //		, PKCS12BagAttributeCarrier
      {
          private readonly X509CertificateStructure c;
  //        private Hashtable pkcs12Attributes = new Hashtable();
  //        private ArrayList pkcs12Ordering = new ArrayList();
  		private readonly BasicConstraints basicConstraints;
  		private readonly bool[] keyUsage;
  
  		protected X509Certificate()
  		{
  		}
  
  		public X509Certificate(
  			X509CertificateStructure c)
  		{
  			this.c = c;
  
  			try
  			{
  				Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.19"));
  
  				if (str != null)
  				{
  					basicConstraints = BasicConstraints.GetInstance(
  						X509ExtensionUtilities.FromExtensionValue(str));
  				}
  			}
  			catch (Exception e)
  			{
  				throw new CertificateParsingException("cannot construct BasicConstraints: " + e);
  			}
  
  			try
  			{
  				Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.15"));
  
  				if (str != null)
  				{
  					DerBitString bits = DerBitString.GetInstance(
  						X509ExtensionUtilities.FromExtensionValue(str));
  
  					byte[] bytes = bits.GetBytes();
  					int length = (bytes.Length * 8) - bits.PadBits;
  
  					keyUsage = new bool[(length < 9) ? 9 : length];
  
  					for (int i = 0; i != length; i++)
  					{
  //						keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
  						keyUsage[i] = (bytes[i / 8] & (0x80 >> (i % 8))) != 0;
  					}
  				}
  				else
  				{
  					keyUsage = null;
  				}
  			}
  			catch (Exception e)
  			{
  				throw new CertificateParsingException("cannot construct KeyUsage: " + e);
  			}
  		}
  
  //		internal X509Certificate(
  //			Asn1Sequence seq)
  //        {
  //            this.c = X509CertificateStructure.GetInstance(seq);
  //        }
  
  //		/// <summary>
  //        /// Load certificate from byte array.
  //        /// </summary>
  //        /// <param name="encoded">Byte array containing encoded X509Certificate.</param>
  //        public X509Certificate(
  //            byte[] encoded)
  //			: this((Asn1Sequence) new Asn1InputStream(encoded).ReadObject())
  //		{
  //        }
  //
  //        /// <summary>
  //        /// Load certificate from Stream.
  //        /// Must be positioned at start of certificate.
  //        /// </summary>
  //        /// <param name="input"></param>
  //        public X509Certificate(
  //            Stream input)
  //			: this((Asn1Sequence) new Asn1InputStream(input).ReadObject())
  //        {
  //        }
  
  		/// <summary>
          /// Return true if the current time is within the start and end times nominated on the certificate.
          /// </summary>
          /// <returns>true id certificate is valid for the current time.</returns>
          public virtual bool IsValidNow
          {
  			get { return IsValid(DateTime.UtcNow); }
          }
  
  		/// <summary>
          /// Return true if the nominated time is within the start and end times nominated on the certificate.
          /// </summary>
          /// <param name="time">The time to test validity against.</param>
          /// <returns>True if certificate is valid for nominated time.</returns>
          public virtual bool IsValid(
  			DateTime time)
          {
              return time.CompareTo(NotBefore) >= 0 && time.CompareTo(NotAfter) <= 0;
          }
  
  		/// <summary>
  		/// Checks if the current date is within certificate's validity period.
  		/// </summary>
  		public virtual void CheckValidity()
  		{
  			this.CheckValidity(DateTime.UtcNow);
  		}
  
  		/// <summary>
  		/// Checks if the given date is within certificate's validity period.
  		/// </summary>
  		/// <exception cref="CertificateExpiredException">if the certificate is expired by given date</exception>
  		/// <exception cref="CertificateNotYetValidException">if the certificate is not yet valid on given date</exception>
  		public virtual void CheckValidity(
  			DateTime time)
  		{
  			if (time.CompareTo(NotAfter) > 0)
  				throw new CertificateExpiredException("certificate expired on " + c.EndDate.GetTime());
  			if (time.CompareTo(NotBefore) < 0)
  				throw new CertificateNotYetValidException("certificate not valid until " + c.StartDate.GetTime());
  		}
  
  		/// <summary>
          /// Return the certificate's version.
          /// </summary>
          /// <returns>An integer whose value Equals the version of the cerficate.</returns>
          public virtual int Version
          {
              get { return c.Version; }
          }
  
  		/// <summary>
          /// Return a <see cref="Org.BouncyCastle.Math.BigInteger">BigInteger</see> containing the serial number.
          /// </summary>
          /// <returns>The Serial number.</returns>
          public virtual BigInteger SerialNumber
          {
              get { return c.SerialNumber.Value; }
          }
  
  		/// <summary>
          /// Get the Issuer Distinguished Name. (Who signed the certificate.)
          /// </summary>
          /// <returns>And X509Object containing name and value pairs.</returns>
  //        public IPrincipal IssuerDN
  		public virtual X509Name IssuerDN
  		{
              get { return c.Issuer; }
          }
  
  		/// <summary>
          /// Get the subject of this certificate.
          /// </summary>
          /// <returns>An X509Name object containing name and value pairs.</returns>
  //        public IPrincipal SubjectDN
  		public virtual X509Name SubjectDN
  		{
              get { return c.Subject; }
          }
  
  		/// <summary>
  		/// The time that this certificate is valid from.
  		/// </summary>
  		/// <returns>A DateTime object representing that time in the local time zone.</returns>
  		public virtual DateTime NotBefore
  		{
  			get { return c.StartDate.ToDateTime(); }
  		}
  
  		/// <summary>
          /// The time that this certificate is valid up to.
          /// </summary>
          /// <returns>A DateTime object representing that time in the local time zone.</returns>
          public virtual DateTime NotAfter
          {
  			get { return c.EndDate.ToDateTime(); }
          }
  
  		/// <summary>
  		/// Return the Der encoded TbsCertificate data.
  		/// This is the certificate component less the signature.
  		/// To Get the whole certificate call the GetEncoded() member.
  		/// </summary>
  		/// <returns>A byte array containing the Der encoded Certificate component.</returns>
  		public virtual byte[] GetTbsCertificate()
  		{
  			return c.TbsCertificate.GetDerEncoded();
  		}
  
  		/// <summary>
  		/// The signature.
  		/// </summary>
  		/// <returns>A byte array containg the signature of the certificate.</returns>
  		public virtual byte[] GetSignature()
  		{
  			return c.Signature.GetBytes();
  		}
  
  		/// <summary>
  		/// A meaningful version of the Signature Algorithm. (EG SHA1WITHRSA)
  		/// </summary>
  		/// <returns>A sting representing the signature algorithm.</returns>
  		public virtual string SigAlgName
  		{
  			get { return SignerUtilities.GetEncodingName(c.SignatureAlgorithm.ObjectID); }
  		}
  
  		/// <summary>
  		/// Get the Signature Algorithms Object ID.
  		/// </summary>
  		/// <returns>A string containg a '.' separated object id.</returns>
  		public virtual string SigAlgOid
  		{
  			get { return c.SignatureAlgorithm.ObjectID.Id; }
  		}
  
  		/// <summary>
  		/// Get the signature algorithms parameters. (EG DSA Parameters)
  		/// </summary>
  		/// <returns>A byte array containing the Der encoded version of the parameters or null if there are none.</returns>
  		public virtual byte[] GetSigAlgParams()
  		{
  			if (c.SignatureAlgorithm.Parameters != null)
  			{
  				return c.SignatureAlgorithm.Parameters.GetDerEncoded();
  			}
  
  			return null;
  		}
  
  		/// <summary>
  		/// Get the issuers UID.
  		/// </summary>
  		/// <returns>A DerBitString.</returns>
  		public virtual DerBitString IssuerUniqueID
  		{
  			get { return c.TbsCertificate.IssuerUniqueID; }
  		}
  
  		/// <summary>
  		/// Get the subjects UID.
  		/// </summary>
  		/// <returns>A DerBitString.</returns>
  		public virtual DerBitString SubjectUniqueID
  		{
  			get { return c.TbsCertificate.SubjectUniqueID; }
  		}
  
  		/// <summary>
  		/// Get a key usage guidlines.
  		/// </summary>
  		public virtual bool[] GetKeyUsage()
  		{
  			return keyUsage == null ? null : (bool[]) keyUsage.Clone();
  		}
  
  		// TODO Replace with something that returns a list of DerObjectIdentifier
  		public virtual IList GetExtendedKeyUsage()
  		{
  			Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.37"));
  
  			if (str == null)
  				return null;
  
  			try
  			{
  				Asn1Sequence seq = Asn1Sequence.GetInstance(
  					X509ExtensionUtilities.FromExtensionValue(str));
  
  				ArrayList list = new ArrayList();
  
  				foreach (DerObjectIdentifier oid in seq)
  				{
  					list.Add(oid.Id);
  				}
  
  				return list;
  			}
  			catch (Exception e)
  			{
  				throw new CertificateParsingException("error processing extended key usage extension", e);
  			}
  		}
  
  		public virtual int GetBasicConstraints()
  		{
  			if (basicConstraints != null && basicConstraints.IsCA())
  			{
  				if (basicConstraints.PathLenConstraint == null)
  				{
  					return int.MaxValue;
  				}
  
  				return basicConstraints.PathLenConstraint.IntValue;
  			}
  
  			return -1;
  		}
  
  		public virtual ICollection GetSubjectAlternativeNames()
  		{
  			return GetAlternativeNames("2.5.29.17");
  		}
  
  		public virtual ICollection GetIssuerAlternativeNames()
  		{
  			return GetAlternativeNames("2.5.29.18");
  		}
  
  		protected virtual ICollection GetAlternativeNames(
  			string oid)
  		{
  			Asn1OctetString altNames = GetExtensionValue(new DerObjectIdentifier(oid));
  
  			if (altNames == null)
  				return null;
  
  			Asn1Object asn1Object = X509ExtensionUtilities.FromExtensionValue(altNames);
  
  			GeneralNames gns = GeneralNames.GetInstance(asn1Object);
  
  			ArrayList result = new ArrayList();
  			foreach (GeneralName gn in gns.GetNames())
  			{
  				ArrayList entry = new ArrayList();
  				entry.Add(gn.TagNo);
  				entry.Add(gn.Name.ToString());
  				result.Add(entry);
  			}
  // MASC20070308. CF compatibility patch
  #if !NETCF
  			return ArrayList.ReadOnly(result);
  #else
  			return result;
  #endif
  		}
  
  		protected override X509Extensions GetX509Extensions()
  		{
  			return c.Version == 3
  				?	c.TbsCertificate.Extensions
  				:	null;
  		}
  
  		/// <summary>
  		/// Get the public key of the subject of the certificate.
  		/// </summary>
  		/// <returns>The public key parameters.</returns>
  		public virtual AsymmetricKeyParameter GetPublicKey()
  		{
  			return PublicKeyFactory.CreateKey(c.SubjectPublicKeyInfo);
  		}
  
  		/// <summary>
  		/// Return a Der encoded version of this certificate.
  		/// </summary>
  		/// <returns>A byte array.</returns>
  		public virtual byte[] GetEncoded()
  		{
  			return c.GetDerEncoded();
  		}
  
  		public override bool Equals(
  			object obj)
  		{
  			if (obj == this)
  				return true;
  
  			X509Certificate other = obj as X509Certificate;
  
  			if (other == null)
  				return false;
  
  			return c.Equals(other.c);
  
  			// NB: May prefer this implementation of Equals if more than one certificate implementation in play
  //			return Arrays.AreEqual(this.GetEncoded(), other.GetEncoded());
  		}
  
  		public override int GetHashCode()
  		{
  			return c.GetHashCode();
  		}
  
  //		public void setBagAttribute(
  //			DERObjectIdentifier oid,
  //			DEREncodable        attribute)
  //		{
  //			pkcs12Attributes.put(oid, attribute);
  //			pkcs12Ordering.addElement(oid);
  //		}
  //
  //		public DEREncodable getBagAttribute(
  //			DERObjectIdentifier oid)
  //		{
  //			return (DEREncodable)pkcs12Attributes.get(oid);
  //		}
  //
  //		public Enumeration getBagAttributeKeys()
  //		{
  //			return pkcs12Ordering.elements();
  //		}
  
  		public override string ToString()
  		{
  			StringBuilder buf = new StringBuilder();
  			string nl = 
  // MASC 20070308. CF compatibility patch
  #if !NETCF
  				Environment.NewLine
  #else
  				EnvironmentEx.NewLine
  #endif					
  				;
  
  			buf.Append("  [0]         Version: ").Append(this.Version).Append(nl);
  			buf.Append("         SerialNumber: ").Append(this.SerialNumber).Append(nl);
  			buf.Append("             IssuerDN: ").Append(this.IssuerDN).Append(nl);
  			buf.Append("           Start Date: ").Append(this.NotBefore).Append(nl);
  			buf.Append("           Final Date: ").Append(this.NotAfter).Append(nl);
  			buf.Append("            SubjectDN: ").Append(this.SubjectDN).Append(nl);
  			buf.Append("           Public Key: ").Append(this.GetPublicKey()).Append(nl);
  			buf.Append("  Signature Algorithm: ").Append(this.SigAlgName).Append(nl);
  
  			byte[] sig = this.GetSignature();
  
  			// MASC 20070308. CF compatibility patch
  			{
  				byte[] hex = Hex.Encode(sig, 0, 20);
  				buf.Append("            Signature: ").Append(Encoding.ASCII.GetString(hex,0,hex.Length)).Append(nl);
  			}
  			for (int i = 20; i < sig.Length; i += 20)
  			{
  				if (i < sig.Length - 20)
  				{
  					// MASC 20070308. CF compatibility patch
  					byte[] hex = Hex.Encode(sig, i, 20);
  					buf.Append("                       ")
  						.Append(Encoding.ASCII.GetString(hex,0,hex.Length)).Append(nl);
  				}
  				else
  				{
  					// MASC 20070308. CF compatibility patch
  					byte[] hex = Hex.Encode(sig, i, sig.Length - i);
  					buf.Append("                       ")
  						.Append(Encoding.ASCII.GetString(hex,0,hex.Length)).Append(nl);
  				}
  			}
  
  			X509Extensions extensions = c.TbsCertificate.Extensions;
  
  			if (extensions != null)
  			{
  				IEnumerator e = extensions.ExtensionOids.GetEnumerator();
  
  				if (e.MoveNext())
  				{
  					buf.Append("       Extensions: \n");
  				}
  
  				do
  				{
  
gt: 					DerObjectIdentifier oid = (DerObjectIdentifier)e.Current;