context: //**********************************************************************************
  //
  //OpenSSLKey
  // .NET 2.0  OpenSSL Public & Private Key Parser
  //
  // Copyright (C) 2008  	JavaScience Consulting
  //
  //***********************************************************************************
  //
  //  opensslkey.cs
  //
  //  Reads and parses:
  //    (1) OpenSSL PEM or DER public keys
  //    (2) OpenSSL PEM or DER traditional SSLeay private keys (encrypted and unencrypted)
  //    (3) PKCS #8 PEM or DER encoded private keys (encrypted and unencrypted)
  //  Keys in PEM format must have headers/footers .
  //  Encrypted Private Key in SSLEay format not supported in DER
  //  Removes header/footer lines.
  //  For traditional SSLEAY PEM private keys, checks for encrypted format and
  //  uses PBE to extract 3DES key.
  //  For SSLEAY format, only supports encryption format: DES-EDE3-CBC
  //  For PKCS #8, only supports PKCS#5 v2.0  3des.
  //  Parses private and public key components and returns .NET RSA object.
  //  Creates dummy unsigned certificate linked to private keypair and
  //  optionally exports to pkcs #12
  //
  // See also: 
  //  http://www.openssl.org/docs/crypto/pem.html#PEM_ENCRYPTION_FORMAT 
  //**************************************************************************************
  
  using System;
  using System.IO;
  using System.Text;
  using System.Security.Cryptography;
  using System.Security.Cryptography.X509Certificates;
  using System.Runtime.InteropServices;
  using System.Security;
  using System.Diagnostics;
  using System.ComponentModel;
  
  
  namespace JavaScience {
  
  
  
  public class opensslkey {
  
   const  String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----" ;
   const  String pemprivfooter   = "-----END RSA PRIVATE KEY-----" ;
   const  String pempubheader = "-----BEGIN PUBLIC KEY-----" ;
   const  String pempubfooter   = "-----END PUBLIC KEY-----" ;
   const  String pemp8header = "-----BEGIN PRIVATE KEY-----" ;
   const  String pemp8footer   = "-----END PRIVATE KEY-----" ;
   const  String pemp8encheader = "-----BEGIN ENCRYPTED PRIVATE KEY-----" ;
   const  String pemp8encfooter   = "-----END ENCRYPTED PRIVATE KEY-----" ;
  
  
  
  
  //--------   Get the binary PKCS #8 PRIVATE key   --------
  public static byte[] DecodePkcs8PrivateKey(String instr) 
    {
   const  String pemp8header = "-----BEGIN PRIVATE KEY-----" ;
   const  String pemp8footer   = "-----END PRIVATE KEY-----" ;
    String pemstr = instr.Trim() ;
    byte[] binkey;
         if(!pemstr.StartsWith(pemp8header) || !pemstr.EndsWith(pemp8footer))
  	return null;
         StringBuilder sb = new StringBuilder(pemstr) ;
         sb.Replace(pemp8header, "") ;  //remove headers/footers, if present
         sb.Replace(pemp8footer, "") ;
  
  String pubstr = sb.ToString().Trim();	//get string after removing leading/trailing whitespace
  
     try{  
       binkey = Convert.FromBase64String(pubstr) ;
      }
     catch(System.FormatException) {		//if can't b64 decode, data is not valid
  	return null;
      }
    return binkey;
   }
  
  
  //------- Parses binary asn.1 PKCS #8 PrivateKeyInfo; returns RSACryptoServiceProvider ---
  public static RSACryptoServiceProvider DecodePrivateKeyInfo(byte[] pkcs8)
   {
   // encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
   // this byte[] includes the sequence byte and terminal encoded null 
     byte[] SeqOID = {0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00} ;
     byte[] seq = new byte[15];
   // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
    MemoryStream  mem = new MemoryStream(pkcs8) ;
    int lenstream = (int) mem.Length;
    BinaryReader binr = new BinaryReader(mem) ;    //wrap Memory Stream with BinaryReader for easy reading
    byte bt = 0;
    ushort twobytes = 0;
  
  try{
  
  twobytes = binr.ReadUInt16();
  if(twobytes == 0x8130)	//data read as little endian order (actual data order for Sequence is 30 81)
  	binr.ReadByte();	//advance 1 byte
  else if(twobytes == 0x8230)
  	binr.ReadInt16();	//advance 2 bytes
  else
  	return null;
  
  
  bt = binr.ReadByte();
  if(bt != 0x02)
  	return null;
  
  twobytes = binr.ReadUInt16();
  
  if(twobytes != 0x0001)
  	return null;
  
  seq = binr.ReadBytes(15);		//read the Sequence OID
  if(!CompareBytearrays(seq, SeqOID))	//make sure Sequence for OID is correct
  	return null;
  
  bt = binr.ReadByte();
  if(bt != 0x04)	//expect an Octet string 
  	return null;
  
  bt = binr.ReadByte();		//read next byte, or next 2 bytes is  0x81 or 0x82; otherwise bt is the byte count
  if(bt == 0x81)
  	binr.ReadByte();
  else
   if(bt == 0x82)
  	binr.ReadUInt16();
  //------ at this stage, the remaining sequence should be the RSA private key
  
    byte[] rsaprivkey = binr.ReadBytes((int)(lenstream -mem.Position)) ;
      RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey(rsaprivkey);
    return rsacsp;
  }
  
   catch(Exception){
      return null; 
    }
  
   finally { binr.Close(); }
  
   }
  
  
  
  
  
  
  
  
  //--------   Get the binary PKCS #8 Encrypted PRIVATE key   --------
  public static byte[] DecodePkcs8EncPrivateKey(String instr) 
    {
   const  String pemp8encheader = "-----BEGIN ENCRYPTED PRIVATE KEY-----" ;
   const  String pemp8encfooter   = "-----END ENCRYPTED PRIVATE KEY-----" ;
    String pemstr = instr.Trim() ;
    byte[] binkey;
         if(!pemstr.StartsWith(pemp8encheader) || !pemstr.EndsWith(pemp8encfooter))
  	return null;
         StringBuilder sb = new StringBuilder(pemstr) ;
         sb.Replace(pemp8encheader, "") ;  //remove headers/footers, if present
         sb.Replace(pemp8encfooter, "") ;
  
  String pubstr = sb.ToString().Trim();	//get string after removing leading/trailing whitespace
  
     try{  
       binkey = Convert.FromBase64String(pubstr) ;
      }
     catch(System.FormatException) {		//if can't b64 decode, data is not valid
  	return null;
      }
    return binkey;
   }
  
  
  
  
  
  
  //------- Parses binary asn.1 EncryptedPrivateKeyInfo; returns RSACryptoServiceProvider ---
  public static RSACryptoServiceProvider DecodeEncryptedPrivateKeyInfo(byte[] encpkcs8)
   {
   // encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
   // this byte[] includes the sequence byte and terminal encoded null 
     byte[] OIDpkcs5PBES2 = {0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05,  0x0D } ;
     byte[] OIDpkcs5PBKDF2  = {0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05,  0x0C } ;
     byte[] OIDdesEDE3CBC = {0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x03, 0x07} ;
     byte[] seqdes = new byte[10] ;
     byte[] seq = new byte[11];
     byte[] salt ;
     byte[] IV;
     byte[] encryptedpkcs8;
     byte[] pkcs8;
  
     int saltsize, ivsize, encblobsize;
     int iterations;
  
   // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
    MemoryStream  mem = new MemoryStream(encpkcs8) ;
    int lenstream = (int) mem.Length;
    BinaryReader binr = new BinaryReader(mem) ;    //wrap Memory Stream with BinaryReader for easy reading
    byte bt = 0;
    ushort twobytes = 0;
  
  try{
  
  twobytes = binr.ReadUInt16();
  if(twobytes == 0x8130)	//data read as little endian order (actual data order for Sequence is 30 81)
  	binr.ReadByte();	//advance 1 byte
  else if(twobytes == 0x8230)
  	binr.ReadInt16();	//advance 2 bytes
  else
  	return null;
  
  twobytes = binr.ReadUInt16();	//inner sequence
  if(twobytes == 0x8130)
  	binr.ReadByte();
  else if(twobytes == 0x8230)
  	binr.ReadInt16();
  
  
  seq = binr.ReadBytes(11);		//read the Sequence OID
  if(!CompareBytearrays(seq, OIDpkcs5PBES2))	//is it a OIDpkcs5PBES2 ?
  	return null;
  
  twobytes = binr.ReadUInt16();	//inner sequence for pswd salt
  if(twobytes == 0x8130)
  	binr.ReadByte();
  else if(twobytes == 0x8230)
  	binr.ReadInt16();
  
  twobytes = binr.ReadUInt16();	//inner sequence for pswd salt
  if(twobytes == 0x8130)
  	binr.ReadByte();
  else if(twobytes == 0x8230)
  	binr.ReadInt16();
  
  seq = binr.ReadBytes(11);		//read the Sequence OID
  if(!CompareBytearrays(seq, OIDpkcs5PBKDF2))	//is it a OIDpkcs5PBKDF2 ?
  	return null;
  
  twobytes = binr.ReadUInt16();
  if(twobytes == 0x8130)
  	binr.ReadByte();
  else if(twobytes == 0x8230)
  	binr.ReadInt16();
  
  bt = binr.ReadByte();
  if(bt != 0x04)		//expect octet string for salt
  	return null;
  saltsize = binr.ReadByte();
  salt = binr.ReadBytes(saltsize);
  
  
  bt=binr.ReadByte();
  if (bt != 0x02) 	//expect an integer for PBKF2 interation count
  	return null;
  
  int itbytes = binr.ReadByte();	//PBKD2 iterations should fit in 2 bytes.
  if(itbytes ==1)
  	iterations = binr.ReadByte();
  else if(itbytes == 2)
  	iterations = 256*binr.ReadByte() + binr.ReadByte();
  else
  	return null;
  
  
  twobytes = binr.ReadUInt16();
  if(twobytes == 0x8130)
  	binr.ReadByte();
  else if(twobytes == 0x8230)
  	binr.ReadInt16();
  
  
  seqdes = binr.ReadBytes(10);		//read the Sequence OID
  if(!CompareBytearrays(seqdes, OIDdesEDE3CBC))	//is it a OIDdes-EDE3-CBC ?
  	return null;
  
  bt = binr.ReadByte();
  if(bt != 0x04)		//expect octet string for IV
  	return null;
  ivsize = binr.ReadByte();	// IV byte size should fit in one byte (24 expected for 3DES)
  IV= binr.ReadBytes(ivsize);
  
  
  bt=binr.ReadByte();
  if(bt != 0x04)		// expect octet string for encrypted PKCS8 data
  	return null;
  
  
  bt = binr.ReadByte();
  
  if(bt == 0x81)
  	encblobsize = binr.ReadByte();	// data size in next byte
  else if(bt == 0x82)
  	encblobsize = 256*binr.ReadByte() + binr.ReadByte() ;
  else
  	encblobsize = bt;		// we already have the data size
  
  
  encryptedpkcs8 = binr.ReadBytes(encblobsize) ;
  //if(verbose)
  //	showBytes("Encrypted PKCS8 blob", encryptedpkcs8) ;
  
  
  SecureString secpswd = GetSecPswd("Enter password for Encrypted PKCS #8 ==>") ;
  pkcs8 = DecryptPBDK2(encryptedpkcs8, salt, IV, secpswd, iterations) ;
  if(pkcs8 == null)	// probably a bad pswd entered.
  	return null;
  
  //if(verbose)
  //	showBytes("Decrypted PKCS #8", pkcs8) ;
   //----- With a decrypted pkcs #8 PrivateKeyInfo blob, decode it to an RSA ---
    RSACryptoServiceProvider rsa =  DecodePrivateKeyInfo(pkcs8) ;
    return rsa;
  }
  
   catch(Exception){
      return null; 
    }
  
   finally { binr.Close(); }
  
  
   }
  
  
  
  
  
  //  ------  Uses PBKD2 to derive a 3DES key and decrypts data --------
  public static byte[] DecryptPBDK2(byte[] edata, byte[] salt, byte[]IV, SecureString secpswd, int iterations)
  {
  	CryptoStream decrypt = null;
  
  	IntPtr unmanagedPswd = IntPtr.Zero;
  	byte[] psbytes = new byte[secpswd.Length] ;
  	unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi(secpswd);
  	Marshal.Copy(unmanagedPswd, psbytes, 0, psbytes.Length) ;
  	Marshal.ZeroFreeGlobalAllocAnsi(unmanagedPswd);
  
    try
  	{
  	Rfc2898DeriveBytes kd = new Rfc2898DeriveBytes(psbytes, salt, iterations);
  	TripleDES decAlg = TripleDES.Create();
  	decAlg.Key = kd.GetBytes(24);
  	decAlg.IV = IV;
  	MemoryStream memstr = new MemoryStream();
  	decrypt = new CryptoStream(memstr,decAlg.CreateDecryptor(), CryptoStreamMode.Write);
  	decrypt.Write(edata, 0, edata.Length);
  	decrypt.Flush();
  	decrypt.Close() ;	// this is REQUIRED.
  
gt: 	byte[] cleartext = memstr.ToArray();