Sei sulla pagina 1di 10

//******************************************************************************* *** // //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 unen crypted) // (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 using using using using using using using using System; System.IO; System.Text; System.Security.Cryptography; System.Security.Cryptography.X509Certificates; System.Runtime.InteropServices; System.Security; System.Diagnostics; System.ComponentModel;

namespace JavaScience { public class Win32 { [DllImport("crypt32.dll", SetLastError = true)] public static extern IntPtr CertCreateSelfSignCertificate( IntPtr hProv, ref CERT_NAME_BLOB pSubjectIssuerBlob, uint dwFlagsm, ref CRYPT_KEY_PROV_INFO pKeyProvInfo, IntPtr pSignatureAlgorithm, IntPtr pStartTime, IntPtr pEndTime, IntPtr other);

[DllImport("crypt32.dll", SetLastError = true)] public static extern bool CertStrToName( uint dwCertEncodingType, String pszX500, uint dwStrType, IntPtr pvReserved, [In, Out] byte[] pbEncoded, ref uint pcbEncoded, IntPtr other); [DllImport("crypt32.dll", SetLastError = true)] public static extern bool CertFreeCertificateContext( IntPtr hCertStore); } [StructLayout(LayoutKind.Sequential)] public struct CRYPT_KEY_PROV_INFO { [MarshalAs(UnmanagedType.LPWStr)] public String pwszContainerName; [MarshalAs(UnmanagedType.LPWStr)] public String pwszProvName; public uint dwProvType; public uint dwFlags; public uint cProvParam; public IntPtr rgProvParam; public uint dwKeySpec; } [StructLayout(LayoutKind.Sequential)] public struct CERT_NAME_BLOB { public int cbData; public IntPtr pbData; } public class opensslkey { const const const const const const const const ) { // 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, 0x F7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; byte[] seq = new byte[15]; // --------- Set up stream to read the asn.1 encoded SubjectPublicK String String String String String String String String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----"; pemprivfooter = "-----END RSA PRIVATE KEY-----"; pempubheader = "-----BEGIN PUBLIC KEY-----"; pempubfooter = "-----END PUBLIC KEY-----"; pemp8header = "-----BEGIN PRIVATE KEY-----"; pemp8footer = "-----END PRIVATE KEY-----"; pemp8encheader = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; pemp8encfooter = "-----END ENCRYPTED PRIVATE KEY-----";

public static RSACryptoServiceProvider DecodePrivateKeyInfo(byte[] pkcs8

eyInfo blob -----MemoryStream mem = new MemoryStream(pkcs8); int lenstream = (int)mem.Length; BinaryReader binr = new BinaryReader(mem); ith BinaryReader for easy reading byte bt = 0; ushort twobytes = 0; try {

//wrap Memory Stream w

twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) //data read as little endian order (actu al 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); if (!CompareBytearrays(seq, SeqOID)) 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 byte s 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.Positio n)); RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey(rsaprivkey ); return rsacsp; } catch (Exception) { return null; } //read the Sequence OID //make sure Sequence for

finally { binr.Close(); } } public static RSACryptoServiceProvider DecodeEncryptedPrivateKeyInfo(byt e[] encpkcs8, SecureString securePassword) { // 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, 0 x0D, 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, 0 x0D, 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 SubjectPublicK eyInfo blob -----MemoryStream mem = new MemoryStream(encpkcs8); int lenstream = (int)mem.Length; BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream w ith BinaryReader for easy reading byte bt = 0; ushort twobytes = 0; try { twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) //data read as little endian order (actu al 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(); if (twobytes == 0x8130) binr.ReadByte(); else if (twobytes == 0x8230) binr.ReadInt16(); //inner sequence

seq = binr.ReadBytes(11); //read the Sequence OID if (!CompareBytearrays(seq, OIDpkcs5PBES2)) //is it a OIDpkc s5PBES2 ? return null;

twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) binr.ReadByte(); else if (twobytes == 0x8230) binr.ReadInt16(); twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) binr.ReadByte(); else if (twobytes == 0x8230) binr.ReadInt16();

//inner sequence for pswd salt

//inner sequence for pswd salt

seq = binr.ReadBytes(11); //read the Sequence OID if (!CompareBytearrays(seq, OIDpkcs5PBKDF2)) //is it a OIDpkc s5PBKDF2 ? 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) 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 on e byte (24 expected for 3DES) //expect an integer for PBKF2 interation

IV = binr.ReadBytes(ivsize); bt = binr.ReadByte(); if (bt != 0x04) S8 data return null; bt = binr.ReadByte(); if (bt == 0x81) encblobsize = binr.ReadByte(); e else if (bt == 0x82) encblobsize = 256 * binr.ReadByte() + binr.ReadByte(); else encblobsize = bt; // we already have the data size encryptedpkcs8 = binr.ReadBytes(encblobsize); SecureString secpswd = securePassword; pkcs8 = DecryptPBDK2(encryptedpkcs8, salt, IV, secpswd, iteratio ns); if (pkcs8 == null) return null; to an RSA --RSACryptoServiceProvider rsa = DecodePrivateKeyInfo(pkcs8); return rsa; } catch (Exception) { return null; } finally { binr.Close(); } } 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, it erations); TripleDES decAlg = TripleDES.Create(); // probably a bad pswd entered. // data size in next byt // expect octet string for encrypted PKC

//----- With a decrypted pkcs #8 PrivateKeyInfo blob, decode it

decAlg.Key = kd.GetBytes(24); decAlg.IV = IV; MemoryStream memstr = new MemoryStream(); decrypt = new CryptoStream(memstr, decAlg.CreateDecryptor(), Cry ptoStreamMode.Write); decrypt.Write(edata, 0, edata.Length); decrypt.Flush(); decrypt.Close(); // this is REQUIRED. byte[] cleartext = memstr.ToArray(); return cleartext; } catch (Exception e) { Console.WriteLine("Problem decrypting: {0}", e.Message); return null; } } public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privke y) { byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; // --------- Set up stream to decode the asn.1 encoded RSA private key -----MemoryStream mem = new MemoryStream(privkey); BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream w ith BinaryReader for easy reading byte bt = 0; ushort twobytes = 0; int elems = 0; try { twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) //data read as little endian order (actu al 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(); if (twobytes != 0x0102) //version number return null; bt = binr.ReadByte(); if (bt != 0x00) return null; //------ all private key components are Integer sequences ---elems = GetIntegerSize(binr); MODULUS = binr.ReadBytes(elems); elems = GetIntegerSize(binr); E = binr.ReadBytes(elems); elems = GetIntegerSize(binr); D = binr.ReadBytes(elems);

elems = GetIntegerSize(binr); P = binr.ReadBytes(elems); elems = GetIntegerSize(binr); Q = binr.ReadBytes(elems); elems = GetIntegerSize(binr); DP = binr.ReadBytes(elems); elems = GetIntegerSize(binr); DQ = binr.ReadBytes(elems); elems = GetIntegerSize(binr); IQ = binr.ReadBytes(elems); // ------- create RSACryptoServiceProvider instance and initiali ze with public key ----RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); RSAParameters RSAparams = new RSAParameters(); RSAparams.Modulus = MODULUS; RSAparams.Exponent = E; RSAparams.D = D; RSAparams.P = P; RSAparams.Q = Q; RSAparams.DP = DP; RSAparams.DQ = DQ; RSAparams.InverseQ = IQ; RSA.ImportParameters(RSAparams); return RSA; } catch (Exception) { return null; } finally { binr.Close(); } } private static int GetIntegerSize(BinaryReader binr) { byte bt = 0; byte lowbyte = 0x00; byte highbyte = 0x00; int count = 0; bt = binr.ReadByte(); if (bt != 0x02) //expect integer return 0; bt = binr.ReadByte(); if (bt == 0x81) count = binr.ReadByte(); // data size in next byte else if (bt == 0x82) { highbyte = binr.ReadByte(); // data size in next 2 bytes lowbyte = binr.ReadByte(); byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; count = BitConverter.ToInt32(modint, 0); } else {

count = bt; }

// we already have the data size

while (binr.ReadByte() == 0x00) { //remove high order zeros in data count -= 1; } binr.BaseStream.Seek(-1, SeekOrigin.Current); eadByte wasn't a removed zero, so back up a byte return count; } private static SecureString GetSecPswd(String prompt) { SecureString password = new SecureString(); Console.ForegroundColor = ConsoleColor.Gray; Console.Write(prompt); Console.ForegroundColor = ConsoleColor.Magenta;

//last R

while (true) { ConsoleKeyInfo cki = Console.ReadKey(true); if (cki.Key == ConsoleKey.Enter) { Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(); return password; } else if (cki.Key == ConsoleKey.Backspace) { // remove the last asterisk from the screen... if (password.Length > 0) { Console.SetCursorPosition(Console.CursorLeft - 1, Consol e.CursorTop); Console.Write(" "); Console.SetCursorPosition(Console.CursorLeft - 1, Consol e.CursorTop); password.RemoveAt(password.Length - 1); } } else if (cki.Key == ConsoleKey.Escape) { Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(); return password; } else if (Char.IsLetterOrDigit(cki.KeyChar) || Char.IsSymbol(cki. KeyChar)) { if (password.Length < 20) { password.AppendChar(cki.KeyChar); Console.Write("*"); } else {

Console.Beep(); } } else { Console.Beep(); } } } private static bool CompareBytearrays(byte[] a, byte[] b) { if (a.Length != b.Length) return false; int i = 0; foreach (byte c in a) { if (c != b[i]) return false; i++; } return true; } } }