context: using System;
  using System.Collections;
  using System.Diagnostics;
  using System.IO;
  using Org.BouncyCastle.Asn1;
  using Org.BouncyCastle.Asn1.Nist;
  using Org.BouncyCastle.Asn1.Pkcs;
  using Org.BouncyCastle.Asn1.Sec;
  using Org.BouncyCastle.Asn1.TeleTrust;
  using Org.BouncyCastle.Asn1.X509;
  using Org.BouncyCastle.Asn1.X9;
  using Org.BouncyCastle.Crypto;
  using Org.BouncyCastle.Crypto.Generators;
  using Org.BouncyCastle.Crypto.Parameters;
  using Org.BouncyCastle.Pkcs;
  using Org.BouncyCastle.Security;
  using Org.BouncyCastle.Utilities;
  using Org.BouncyCastle.Utilities.Encoders;
  using Org.BouncyCastle.Utilities.IO.Pem;
  using Org.BouncyCastle.X509;
  
  namespace Org.BouncyCastle.OpenSsl
  {
  	/**
  	* Class for reading OpenSSL PEM encoded streams containing 
  	* X509 certificates, PKCS8 encoded keys and PKCS7 objects.
  	* <p>
  	* In the case of PKCS7 objects the reader will return a CMS ContentInfo object. Keys and
  	* Certificates will be returned using the appropriate java.security type.</p>
  	*/
  	public class PemReader
  		: Org.BouncyCastle.Utilities.IO.Pem.PemReader
  	{
  //		private static readonly IDictionary parsers = new Hashtable();
  
  		static PemReader()
  		{
  //			parsers.Add("CERTIFICATE REQUEST", new PKCS10CertificationRequestParser());
  //			parsers.Add("NEW CERTIFICATE REQUEST", new PKCS10CertificationRequestParser());
  //			parsers.Add("CERTIFICATE", new X509CertificateParser(provider));
  //			parsers.Add("X509 CERTIFICATE", new X509CertificateParser(provider));
  //			parsers.Add("X509 CRL", new X509CRLParser(provider));
  //			parsers.Add("PKCS7", new PKCS7Parser());
  //			parsers.Add("ATTRIBUTE CERTIFICATE", new X509AttributeCertificateParser());
  //			parsers.Add("EC PARAMETERS", new ECNamedCurveSpecParser());
  //			parsers.Add("PUBLIC KEY", new PublicKeyParser(provider));
  //			parsers.Add("RSA PUBLIC KEY", new RSAPublicKeyParser(provider));
  //			parsers.Add("RSA PRIVATE KEY", new RSAKeyPairParser(provider));
  //			parsers.Add("DSA PRIVATE KEY", new DSAKeyPairParser(provider));
  //			parsers.Add("EC PRIVATE KEY", new ECDSAKeyPairParser(provider));
  //			parsers.Add("ENCRYPTED PRIVATE KEY", new EncryptedPrivateKeyParser(provider));
  //			parsers.Add("PRIVATE KEY", new PrivateKeyParser(provider));
  		}
  
  		private readonly IPasswordFinder pFinder;
  
  		/**
  		* Create a new PemReader
  		*
  		* @param reader the Reader
  		*/
  		public PemReader(
  			TextReader reader)
  			: this(reader, null)
  		{
  		}
  
  		/**
  		* Create a new PemReader with a password finder
  		*
  		* @param reader the Reader
  		* @param pFinder the password finder
  		*/
  		public PemReader(
  			TextReader		reader,
  			IPasswordFinder	pFinder)
  			: base(reader)
  		{
  			this.pFinder = pFinder;
  		}
  
  		public object ReadObject()
  		{
  			PemObject obj = ReadPemObject();
  
  			if (obj == null)
  				return null;
  
  			// TODO Follow Java build and map to parser objects?
  //			if (parsers.Contains(obj.Type))
  //				return ((PemObjectParser)parsers[obj.Type]).ParseObject(obj);
  
  			if (obj.Type.EndsWith("PRIVATE KEY"))
  				return ReadPrivateKey(obj);
  
  			switch (obj.Type)
  			{
  				case "PUBLIC KEY":
  					return ReadPublicKey(obj);
  				case "RSA PUBLIC KEY":
  					return ReadRsaPublicKey(obj);
  				case "CERTIFICATE REQUEST":
  				case "NEW CERTIFICATE REQUEST":
  					return ReadCertificateRequest(obj);
  				case "CERTIFICATE":
  				case "X509 CERTIFICATE":
  					return ReadCertificate(obj);
  				case "PKCS7":
  					return ReadPkcs7(obj);
  				case "X509 CRL":
  					return ReadCrl(obj);
  				case "ATTRIBUTE CERTIFICATE":
  					return ReadAttributeCertificate(obj);
  				// TODO Add back in when tests done, and return type issue resolved
  				//case "EC PARAMETERS":
  				//	return ReadECParameters(obj);
  				default:
  					throw new IOException("unrecognised object: " + obj.Type);
  			}
  		}
  
  		private AsymmetricKeyParameter ReadRsaPublicKey(PemObject pemObject)
  		{
  			RsaPublicKeyStructure rsaPubStructure = RsaPublicKeyStructure.GetInstance(
  				Asn1Object.FromByteArray(pemObject.Content));
  
  			return new RsaKeyParameters(
  				false, // not private
  				rsaPubStructure.Modulus, 
  				rsaPubStructure.PublicExponent);
  		}
  
  		private AsymmetricKeyParameter ReadPublicKey(PemObject pemObject)
  		{
  			return PublicKeyFactory.CreateKey(pemObject.Content);
  		}
  
  		/**
  		* Reads in a X509Certificate.
  		*
  		* @return the X509Certificate
  		* @throws IOException if an I/O error occured
  		*/
  		private X509Certificate ReadCertificate(PemObject pemObject)
  		{
  			try
  			{
  				return new X509CertificateParser().ReadCertificate(pemObject.Content);
  			}
  			catch (Exception e)
  			{
  				throw new PemException("problem parsing cert: " + e.ToString());
  			}
  		}
  
  		/**
  		* Reads in a X509CRL.
  		*
  		* @return the X509Certificate
  		* @throws IOException if an I/O error occured
  		*/
  		private X509Crl ReadCrl(PemObject pemObject)
  		{
  			try
  			{
  				return new X509CrlParser().ReadCrl(pemObject.Content);
  			}
  			catch (Exception e)
  			{
  				throw new PemException("problem parsing cert: " + e.ToString());
  			}
  		}
  
  		/**
  		* Reads in a PKCS10 certification request.
  		*
  		* @return the certificate request.
  		* @throws IOException if an I/O error occured
  		*/
  		private Pkcs10CertificationRequest ReadCertificateRequest(PemObject pemObject)
  		{
  			try
  			{
  				return new Pkcs10CertificationRequest(pemObject.Content);
  			}
  			catch (Exception e)
  			{
  				throw new PemException("problem parsing cert: " + e.ToString());
  			}
  		}
  
  		/**
  		* Reads in a X509 Attribute Certificate.
  		*
  		* @return the X509 Attribute Certificate
  		* @throws IOException if an I/O error occured
  		*/
  		private IX509AttributeCertificate ReadAttributeCertificate(PemObject pemObject)
  		{
  			return new X509V2AttributeCertificate(pemObject.Content);
  		}
  
  		/**
  		* Reads in a PKCS7 object. This returns a ContentInfo object suitable for use with the CMS
  		* API.
  		*
  		* @return the X509Certificate
  		* @throws IOException if an I/O error occured
  		*/
  		// TODO Consider returning Asn1.Pkcs.ContentInfo
  		private Asn1.Cms.ContentInfo ReadPkcs7(PemObject pemObject)
  		{
  			try
  			{
  				return Asn1.Cms.ContentInfo.GetInstance(
  					Asn1Object.FromByteArray(pemObject.Content));
  			}
  			catch (Exception e)
  			{
  				throw new PemException("problem parsing PKCS7 object: " + e.ToString());
  			}
  		}
  
  		/**
  		* Read a Key Pair
  		*/
  		private object ReadPrivateKey(PemObject pemObject)
  		{
  			//
  			// extract the key
  			//
  			Debug.Assert(pemObject.Type.EndsWith("PRIVATE KEY"));
  
  			string type = pemObject.Type.Substring(0, pemObject.Type.Length - "PRIVATE KEY".Length).Trim();
  			byte[] keyBytes = pemObject.Content;
  
  			IDictionary fields = Platform.CreateHashtable();
  			foreach (PemHeader header in pemObject.Headers)
  			{
  				fields[header.Name] = header.Value;
  			}
  
  			string procType = (string) fields["Proc-Type"];
  
  			if (procType == "4,ENCRYPTED")
  			{
  				if (pFinder == null)
  					throw new PasswordException("No password finder specified, but a password is required");
  
  				char[] password = pFinder.GetPassword();
  
  				if (password == null)
  					throw new PasswordException("Password is null, but a password is required");
  
  				string dekInfo = (string) fields["DEK-Info"];
  				string[] tknz = dekInfo.Split(',');
  
  				string dekAlgName = tknz[0].Trim();
  				byte[] iv = Hex.Decode(tknz[1].Trim());
  
  				keyBytes = PemUtilities.Crypt(false, keyBytes, password, dekAlgName, iv);
  			}
  
  			try
  			{
  				AsymmetricKeyParameter pubSpec, privSpec;
  				Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(keyBytes);
  
  				switch (type)
  				{
  					case "RSA":
  					{
  
gt: 						if (seq.Count != 9)