VariableLibrary.ps1
[System.Collections.ArrayList]$script:FunctionsForSBUse = @( ${Function:AddWinRMTrustLocalHost}.Ast.Extent.Text ${Function:DecryptFile}.Ast.Extent.Text ${Function:EncryptFile}.Ast.Extent.Text ${Function:GetModuleDependencies}.Ast.Extent.Text ${Function:InvokeModuleDependencies}.Ast.Extent.Text ${Function:InvokePSCompatibility}.Ast.Extent.Text ${Function:ManualPSGalleryModuleInstall}.Ast.Extent.Text ${Function:NewCryptographyKey}.Ast.Extent.Text ${Function:NewUniqueString}.Ast.Extent.Text ${Function:UnzipFile}.Ast.Extent.Text ${Function:Extract-PfxCerts}.Ast.Extent.Text ${Function:Get-DecryptedContent}.Ast.Extent.Text ${Function:Get-EncryptionCert}.Ast.Extent.Text ${Function:Get-PfxCertificateBetter}.Ast.Extent.Text ${Function:Get-PrivatekeyProperty}.Ast.Extent.Text ${Function:New-EncryptedFile}.Ast.Extent.Text ${Function:New-SelfSignedCertificateEx}.Ast.Extent.Text ) # Below $opensslkeysource from http://www.jensign.com/opensslkey/index.html $script:opensslkeysource = @' //********************************************************************************** // // OpenSSLKey // .NET 2.0 OpenSSL Public & Private Key Parser // // Copyright (c) 2008 JavaScience Consulting, Michel Gallant // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // //*********************************************************************************** // // 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 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 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-----" ; // static byte[] pempublickey; // static byte[] pemprivatekey; // static byte[] pkcs8privatekey; // static byte[] pkcs8encprivatekey; static bool verbose = false; public static void Main(String[] args) { if(args.Length == 1) if(args[0].ToUpper() == "V") verbose = true; Console.ForegroundColor = ConsoleColor.Gray; Console.Write("\nRSA public, private or PKCS #8 key file to decode: "); String filename = Console.ReadLine().Trim(); if (filename == "") //exit while(true) loop return; if (!File.Exists(filename)) { Console.WriteLine("File \"{0}\" does not exist!\n", filename); return; } StreamReader sr = File.OpenText(filename); String pemstr = sr.ReadToEnd().Trim(); sr.Close(); if(pemstr.StartsWith("-----BEGIN")) DecodePEMKey(pemstr); else DecodeDERKey(filename); } // ------- Decode PEM pubic, private or pkcs8 key ---------------- public static void DecodePEMKey(String pemstr) { byte[] pempublickey; byte[] pemprivatekey; byte[] pkcs8privatekey; byte[] pkcs8encprivatekey; if(pemstr.StartsWith(pempubheader) && pemstr.EndsWith(pempubfooter)) { Console.WriteLine("Trying to decode and parse a PEM public key .."); pempublickey = DecodeOpenSSLPublicKey(pemstr); if(pempublickey != null) { if(verbose) showBytes("\nRSA public key", pempublickey) ; //PutFileBytes("rsapubkey.pem", pempublickey, pempublickey.Length) ; RSACryptoServiceProvider rsa = DecodeX509PublicKey(pempublickey); Console.WriteLine("\nCreated an RSACryptoServiceProvider instance\n") ; String xmlpublickey =rsa.ToXmlString(false) ; Console.WriteLine("\nXML RSA public key: {0} bits\n{1}\n", rsa.KeySize, xmlpublickey) ; } } else if(pemstr.StartsWith(pemprivheader) && pemstr.EndsWith(pemprivfooter)) { Console.WriteLine("Trying to decrypt and parse a PEM private key .."); pemprivatekey = DecodeOpenSSLPrivateKey(pemstr); if(pemprivatekey != null) { if(verbose) showBytes("\nRSA private key", pemprivatekey) ; //PutFileBytes("rsaprivkey.pem", pemprivatekey, pemprivatekey.Length) ; RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(pemprivatekey); Console.WriteLine("\nCreated an RSACryptoServiceProvider instance\n") ; String xmlprivatekey =rsa.ToXmlString(true) ; Console.WriteLine("\nXML RSA private key: {0} bits\n{1}\n", rsa.KeySize, xmlprivatekey) ; ProcessRSA(rsa); } } else if(pemstr.StartsWith(pemp8header) && pemstr.EndsWith(pemp8footer)) { Console.WriteLine("Trying to decode and parse as PEM PKCS #8 PrivateKeyInfo .."); pkcs8privatekey = DecodePkcs8PrivateKey(pemstr); if(pkcs8privatekey != null) { if(verbose) showBytes("\nPKCS #8 PrivateKeyInfo", pkcs8privatekey) ; //PutFileBytes("PrivateKeyInfo", pkcs8privatekey, pkcs8privatekey.Length) ; RSACryptoServiceProvider rsa = DecodePrivateKeyInfo(pkcs8privatekey); if(rsa !=null) { Console.WriteLine("\nCreated an RSACryptoServiceProvider instance\n") ; String xmlprivatekey =rsa.ToXmlString(true) ; Console.WriteLine("\nXML RSA private key: {0} bits\n{1}\n", rsa.KeySize, xmlprivatekey) ; ProcessRSA(rsa) ; } else Console.WriteLine("\nFailed to create an RSACryptoServiceProvider"); } } else if(pemstr.StartsWith(pemp8encheader) && pemstr.EndsWith(pemp8encfooter)) { Console.WriteLine("Trying to decode and parse as PEM PKCS #8 EncryptedPrivateKeyInfo .."); pkcs8encprivatekey = DecodePkcs8EncPrivateKey(pemstr); if(pkcs8encprivatekey != null) { if(verbose) showBytes("\nPKCS #8 EncryptedPrivateKeyInfo", pkcs8encprivatekey) ; //PutFileBytes("EncryptedPrivateKeyInfo", pkcs8encprivatekey, pkcs8encprivatekey.Length) ; RSACryptoServiceProvider rsa = DecodeEncryptedPrivateKeyInfo(pkcs8encprivatekey); if(rsa !=null) { Console.WriteLine("\nCreated an RSACryptoServiceProvider instance\n") ; String xmlprivatekey =rsa.ToXmlString(true) ; Console.WriteLine("\nXML RSA private key: {0} bits\n{1}\n", rsa.KeySize, xmlprivatekey) ; ProcessRSA(rsa) ; } else Console.WriteLine("\nFailed to create an RSACryptoServiceProvider"); } } else { Console.WriteLine("Not a PEM public, private key or a PKCS #8"); return; } } // ------- Decode PEM pubic, private or pkcs8 key ---------------- public static void DecodeDERKey(String filename) { RSACryptoServiceProvider rsa = null ; byte[] keyblob = GetFileBytes(filename); if(keyblob == null) return; rsa = DecodeX509PublicKey(keyblob); if (rsa !=null) { Console.WriteLine("\nA valid SubjectPublicKeyInfo\n") ; Console.WriteLine("\nCreated an RSACryptoServiceProvider instance\n") ; String xmlpublickey =rsa.ToXmlString(false) ; Console.WriteLine("\nXML RSA public key: {0} bits\n{1}\n", rsa.KeySize, xmlpublickey) ; return; } rsa = DecodeRSAPrivateKey(keyblob); if (rsa != null) { Console.WriteLine("\nA valid RSAPrivateKey\n") ; Console.WriteLine("\nCreated an RSACryptoServiceProvider instance\n") ; String xmlprivatekey =rsa.ToXmlString(true) ; Console.WriteLine("\nXML RSA private key: {0} bits\n{1}\n", rsa.KeySize, xmlprivatekey) ; ProcessRSA(rsa) ; return; } rsa = DecodePrivateKeyInfo(keyblob); //PKCS #8 unencrypted if(rsa !=null) { Console.WriteLine("\nA valid PKCS #8 PrivateKeyInfo\n") ; Console.WriteLine("\nCreated an RSACryptoServiceProvider instance\n") ; String xmlprivatekey =rsa.ToXmlString(true) ; Console.WriteLine("\nXML RSA private key: {0} bits\n{1}\n", rsa.KeySize, xmlprivatekey) ; ProcessRSA(rsa); return; } rsa = DecodeEncryptedPrivateKeyInfo(keyblob); //PKCS #8 encrypted if(rsa !=null) { Console.WriteLine("\nA valid PKCS #8 EncryptedPrivateKeyInfo\n") ; Console.WriteLine("\nCreated an RSACryptoServiceProvider instance\n") ; String xmlprivatekey =rsa.ToXmlString(true) ; Console.WriteLine("\nXML RSA private key: {0} bits\n{1}\n", rsa.KeySize, xmlprivatekey) ; ProcessRSA(rsa); return; } Console.WriteLine("Not a binary DER public, private or PKCS #8 key"); return; } public static void ProcessRSA(RSACryptoServiceProvider rsa) { if(verbose) showRSAProps(rsa); Console.Write("\n\nExport RSA private key to PKCS #12 file? (Y or N) "); String resp = Console.ReadLine().ToUpper() ; if (resp == "Y" || resp == "YES") RSAtoPKCS12(rsa) ; } //-------- Generate pkcs #12 from an RSACryptoServiceProvider --------- public static void RSAtoPKCS12(RSACryptoServiceProvider rsa) { CspKeyContainerInfo keyInfo = rsa.CspKeyContainerInfo; String keycontainer = keyInfo.KeyContainerName; uint keyspec = (uint) keyInfo.KeyNumber; String provider = keyInfo.ProviderName; uint cspflags = 0; //CryptoAPI Current User store; LM would be CRYPT_MACHINE_KEYSET = 0x00000020 String fname = keycontainer + ".p12" ; //---- need to pass in rsa since underlying keycontainer is not persisted and might be deleted too quickly --- byte[] pkcs12 = GetPkcs12(rsa, keycontainer, provider, keyspec , cspflags) ; if ( (pkcs12 !=null) && verbose) showBytes("\npkcs #12", pkcs12); if(pkcs12 !=null){ PutFileBytes(fname, pkcs12, pkcs12.Length) ; Console.WriteLine("\nWrote pkc #12 file '{0}'\n", fname) ; } else Console.WriteLine("\nProblem getting pkcs#12") ; } //-------- 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); if(verbose) showBytes("Salt for pbkd", salt); 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; if(verbose) Console.WriteLine("PBKD2 iterations {0}", iterations); 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); if(verbose) showBytes("IV for des-EDE3-CBC", IV); 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. byte[] cleartext = memstr.ToArray(); return cleartext; } catch (Exception e) { Console.WriteLine("Problem decrypting: {0}", e.Message) ; return null; } } //-------- Get the binary RSA PUBLIC key -------- public static byte[] DecodeOpenSSLPublicKey(String instr) { const String pempubheader = "-----BEGIN PUBLIC KEY-----" ; const String pempubfooter = "-----END PUBLIC KEY-----" ; String pemstr = instr.Trim() ; byte[] binkey; if (!pemstr.StartsWith(pempubheader) || !pemstr.EndsWith(pempubfooter)) return null; StringBuilder sb = new StringBuilder(pemstr) ; sb.Replace(pempubheader, "") ; //remove headers/footers, if present sb.Replace(pempubfooter, "") ; 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 X509 SubjectPublicKeyInfo; returns RSACryptoServiceProvider --- public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key) { // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" 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(x509key) ; 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; seq = binr.ReadBytes(15); //read the Sequence OID if(!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct return null; twobytes = binr.ReadUInt16(); if(twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) binr.ReadByte(); //advance 1 byte else if(twobytes == 0x8203) binr.ReadInt16(); //advance 2 bytes else return null; bt = binr.ReadByte(); if(bt != 0x00) //expect null byte next return null; 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(); byte lowbyte = 0x00; byte highbyte = 0x00; if(twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81) lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus else if(twobytes == 0x8202) { highbyte = binr.ReadByte(); //advance 2 bytes lowbyte = binr.ReadByte(); } else return null; byte[] modint = {lowbyte, highbyte, 0x00, 0x00} ; //reverse byte order since asn.1 key uses big endian order int modsize = BitConverter.ToInt32(modint, 0) ; byte firstbyte = binr.ReadByte(); binr.BaseStream.Seek(-1, SeekOrigin.Current); if(firstbyte == 0x00) { //if first byte (highest order) of modulus is zero, don't include it binr.ReadByte(); //skip this null byte modsize -=1 ; //reduce modulus buffer size by 1 } byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes if(binr.ReadByte() != 0x02) //expect an Integer for the exponent data return null; int expbytes = (int) binr.ReadByte() ; // should only need one byte for actual exponent data (for all useful values) byte[] exponent = binr.ReadBytes(expbytes); showBytes("\nExponent", exponent); showBytes("\nModulus", modulus) ; // ------- create RSACryptoServiceProvider instance and initialize with public key ----- RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); RSAParameters RSAKeyInfo = new RSAParameters(); RSAKeyInfo.Modulus = modulus; RSAKeyInfo.Exponent = exponent; RSA.ImportParameters(RSAKeyInfo); return RSA; } catch(Exception){ return null; } finally { binr.Close(); } } //------- Parses binary ans.1 RSA private key; returns RSACryptoServiceProvider --- public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey) { 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 with 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 (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(); 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) ; if(verbose) { showBytes("\nModulus", MODULUS) ; showBytes("\nExponent", E); showBytes("\nD", D); showBytes("\nP", P); showBytes("\nQ", Q); showBytes("\nDP", DP); showBytes("\nDQ", DQ); showBytes("\nIQ", IQ); } // ------- create RSACryptoServiceProvider instance and initialize 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); //last ReadByte wasn't a removed zero, so back up a byte return count; } //----- Get the binary RSA PRIVATE key, decrypting if necessary ---- public static byte[] DecodeOpenSSLPrivateKey(String instr) { const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----" ; const String pemprivfooter = "-----END RSA PRIVATE KEY-----" ; String pemstr = instr.Trim() ; byte[] binkey; if(!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter)) return null; StringBuilder sb = new StringBuilder(pemstr) ; sb.Replace(pemprivheader, "") ; //remove headers/footers, if present sb.Replace(pemprivfooter, "") ; String pvkstr = sb.ToString().Trim(); //get string after removing leading/trailing whitespace try{ // if there are no PEM encryption info lines, this is an UNencrypted PEM private key binkey = Convert.FromBase64String(pvkstr) ; return binkey; } catch(System.FormatException) { //if can't b64 decode, it must be an encrypted private key //Console.WriteLine("Not an unencrypted OpenSSL PEM private key"); } StringReader str = new StringReader(pvkstr); //-------- read PEM encryption info. lines and extract salt ----- if(!str.ReadLine().StartsWith("Proc-Type: 4,ENCRYPTED")) return null; String saltline = str.ReadLine(); if(!saltline.StartsWith("DEK-Info: DES-EDE3-CBC,") ) return null; String saltstr = saltline.Substring(saltline.IndexOf(",") + 1).Trim() ; byte[] salt = new byte[saltstr.Length/2]; for (int i=0; i <salt.Length; i++) salt[i] = Convert.ToByte(saltstr.Substring (i*2, 2), 16); if(! (str.ReadLine() == "")) return null; //------ remaining b64 data is encrypted RSA key ---- String encryptedstr = str.ReadToEnd() ; try{ //should have b64 encrypted RSA key now binkey = Convert.FromBase64String(encryptedstr) ; } catch(System.FormatException) { // bad b64 data. return null; } //------ Get the 3DES 24 byte key using PDK used by OpenSSL ---- SecureString despswd = GetSecPswd("Enter password to derive 3DES key==>") ; //Console.Write("\nEnter password to derive 3DES key: "); //String pswd = Console.ReadLine(); byte[] deskey = GetOpenSSL3deskey(salt, despswd, 1, 2); // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes if(deskey == null) return null; //showBytes("3DES key", deskey) ; //------ Decrypt the encrypted 3des-encrypted RSA private key ------ byte[] rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV if(rsakey !=null) return rsakey; //we have a decrypted RSA private key else { Console.WriteLine("Failed to decrypt RSA private key; probably wrong password."); return null; } } // ----- Decrypt the 3DES encrypted RSA private key ---------- public static byte[] DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV) { MemoryStream memst = new MemoryStream(); TripleDES alg = TripleDES.Create(); alg.Key = desKey; alg.IV = IV; try { CryptoStream cs = new CryptoStream(memst, alg.CreateDecryptor(), CryptoStreamMode.Write); cs.Write(cipherData, 0, cipherData.Length); cs.Close(); } catch(Exception exc) { Console.WriteLine(exc.Message); return null; } byte[] decryptedData = memst.ToArray(); return decryptedData; } //----- OpenSSL PBKD uses only one hash cycle (count); miter is number of iterations required to build sufficient bytes --- private static byte[] GetOpenSSL3deskey(byte[] salt, SecureString secpswd, int count, int miter ) { IntPtr unmanagedPswd = IntPtr.Zero; int HASHLENGTH = 16; //MD5 bytes byte[] keymaterial = new byte[HASHLENGTH*miter] ; //to store contatenated Mi hashed results byte[] psbytes = new byte[secpswd.Length] ; unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi(secpswd); Marshal.Copy(unmanagedPswd, psbytes, 0, psbytes.Length) ; Marshal.ZeroFreeGlobalAllocAnsi(unmanagedPswd); //UTF8Encoding utf8 = new UTF8Encoding(); //byte[] psbytes = utf8.GetBytes(pswd); // --- contatenate salt and pswd bytes into fixed data array --- byte[] data00 = new byte[psbytes.Length + salt.Length] ; Array.Copy(psbytes, data00, psbytes.Length); //copy the pswd bytes Array.Copy(salt, 0, data00, psbytes.Length, salt.Length) ; //concatenate the salt bytes // ---- do multi-hashing and contatenate results D1, D2 ... into keymaterial bytes ---- MD5 md5 = new MD5CryptoServiceProvider(); byte[] result = null; byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget for(int j=0; j<miter; j++) { // ---- Now hash consecutively for count times ------ if(j == 0) result = data00; //initialize else { Array.Copy(result, hashtarget, result.Length); Array.Copy(data00, 0, hashtarget, result.Length, data00.Length) ; result = hashtarget; //Console.WriteLine("Updated new initial hash target:") ; //showBytes(result) ; } for(int i=0; i<count; i++) result = md5.ComputeHash(result); Array.Copy(result, 0, keymaterial, j*HASHLENGTH, result.Length); //contatenate to keymaterial } //showBytes("Final key material", keymaterial); byte[] deskey = new byte[24]; Array.Copy(keymaterial, deskey, deskey.Length) ; Array.Clear(psbytes, 0, psbytes.Length); Array.Clear(data00, 0, data00.Length) ; Array.Clear(result, 0, result.Length) ; Array.Clear(hashtarget, 0, hashtarget.Length) ; Array.Clear(keymaterial, 0, keymaterial.Length) ; return deskey; } //------ Since we are using an RSA with nonpersisted keycontainer, must pass it in to ensure it isn't colledted ----- private static byte[] GetPkcs12(RSA rsa, String keycontainer, String cspprovider, uint KEYSPEC, uint cspflags) { byte[] pfxblob = null; IntPtr hCertCntxt = IntPtr.Zero; String DN = "CN=Opensslkey Unsigned Certificate"; hCertCntxt = CreateUnsignedCertCntxt(keycontainer, cspprovider, KEYSPEC, cspflags, DN) ; if(hCertCntxt == IntPtr.Zero){ Console.WriteLine("Couldn't create an unsigned-cert\n") ; return null; } try{ X509Certificate cert = new X509Certificate(hCertCntxt) ; //create certificate object from cert context. //X509Certificate2UI.DisplayCertificate(new X509Certificate2(cert)) ; // display it, showing linked private key SecureString pswd = GetSecPswd("Set PFX Password ==>") ; pfxblob = cert.Export(X509ContentType.Pkcs12, pswd); } catch(Exception exc) { Console.WriteLine( "BAD RESULT" + exc.Message); pfxblob = null; } rsa.Clear() ; if(hCertCntxt != IntPtr.Zero) Win32.CertFreeCertificateContext(hCertCntxt) ; return pfxblob; } private static IntPtr CreateUnsignedCertCntxt(String keycontainer, String provider, uint KEYSPEC, uint cspflags, String DN) { const uint AT_KEYEXCHANGE = 0x00000001; const uint AT_SIGNATURE = 0x00000002; const uint CRYPT_MACHINE_KEYSET = 0x00000020; const uint PROV_RSA_FULL = 0x00000001; const String MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0"; const String MS_STRONG_PROV = "Microsoft Strong Cryptographic Provider"; const String MS_ENHANCED_PROV = "Microsoft Enhanced Cryptographic Provider v1.0"; const uint CERT_CREATE_SELFSIGN_NO_SIGN = 1 ; const uint X509_ASN_ENCODING = 0x00000001; const uint CERT_X500_NAME_STR = 3; IntPtr hCertCntxt = IntPtr.Zero; byte[] encodedName = null; uint cbName = 0; if( provider != MS_DEF_PROV && provider != MS_STRONG_PROV && provider != MS_ENHANCED_PROV) return IntPtr.Zero; if(keycontainer == "") return IntPtr.Zero; if( KEYSPEC != AT_SIGNATURE && KEYSPEC != AT_KEYEXCHANGE) return IntPtr.Zero; if(cspflags != 0 && cspflags != CRYPT_MACHINE_KEYSET) //only 0 (Current User) keyset is currently used. return IntPtr.Zero; if (DN == "") return IntPtr.Zero; if(Win32.CertStrToName(X509_ASN_ENCODING, DN, CERT_X500_NAME_STR, IntPtr.Zero, null, ref cbName, IntPtr.Zero)) { encodedName = new byte[cbName] ; Win32.CertStrToName(X509_ASN_ENCODING, DN, CERT_X500_NAME_STR, IntPtr.Zero, encodedName, ref cbName, IntPtr.Zero); } CERT_NAME_BLOB subjectblob = new CERT_NAME_BLOB(); subjectblob.pbData = Marshal.AllocHGlobal(encodedName.Length); Marshal.Copy(encodedName, 0, subjectblob.pbData, encodedName.Length); subjectblob.cbData = encodedName.Length; CRYPT_KEY_PROV_INFO pInfo = new CRYPT_KEY_PROV_INFO(); pInfo.pwszContainerName = keycontainer; pInfo.pwszProvName = provider; pInfo.dwProvType = PROV_RSA_FULL; pInfo.dwFlags = cspflags; pInfo.cProvParam = 0; pInfo.rgProvParam = IntPtr.Zero; pInfo.dwKeySpec = KEYSPEC; hCertCntxt = Win32.CertCreateSelfSignCertificate(IntPtr.Zero, ref subjectblob, CERT_CREATE_SELFSIGN_NO_SIGN, ref pInfo, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); if(hCertCntxt == IntPtr.Zero) showWin32Error(Marshal.GetLastWin32Error()); Marshal.FreeHGlobal(subjectblob.pbData); return hCertCntxt ; } private static SecureString GetSecPswd(String prompt) { SecureString password = new SecureString(); Console.ForegroundColor = ConsoleColor.Gray; Console.Write(prompt); Console.ForegroundColor = ConsoleColor.Magenta; 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, Console.CursorTop); Console.Write(" "); Console.SetCursorPosition(Console.CursorLeft - 1, Console.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; } private static void showRSAProps(RSACryptoServiceProvider rsa) { Console.WriteLine("RSA CSP key information:"); CspKeyContainerInfo keyInfo = rsa.CspKeyContainerInfo; Console.WriteLine("Accessible property: " + keyInfo.Accessible); Console.WriteLine("Exportable property: " + keyInfo.Exportable); Console.WriteLine("HardwareDevice property: " + keyInfo.HardwareDevice); Console.WriteLine("KeyContainerName property: " + keyInfo.KeyContainerName); Console.WriteLine("KeyNumber property: " + keyInfo.KeyNumber.ToString()); Console.WriteLine("MachineKeyStore property: " + keyInfo.MachineKeyStore); Console.WriteLine("Protected property: " + keyInfo.Protected); Console.WriteLine("ProviderName property: " + keyInfo.ProviderName); Console.WriteLine("ProviderType property: " + keyInfo.ProviderType); Console.WriteLine("RandomlyGenerated property: " + keyInfo.RandomlyGenerated); Console.WriteLine("Removable property: " + keyInfo.Removable); Console.WriteLine("UniqueKeyContainerName property: " + keyInfo.UniqueKeyContainerName); } private static void showBytes(String info, byte[] data){ Console.WriteLine("{0} [{1} bytes]", info, data.Length); for(int i=1; i<=data.Length; i++){ Console.Write("{0:X2} ", data[i-1]) ; if(i%16 == 0) Console.WriteLine(); } Console.WriteLine("\n\n"); } private static byte[] GetFileBytes(String filename) { if(!File.Exists(filename)) return null; Stream stream=new FileStream(filename,FileMode.Open); int datalen = (int)stream.Length; byte[] filebytes =new byte[datalen]; stream.Seek(0,SeekOrigin.Begin); stream.Read(filebytes,0,datalen); stream.Close(); return filebytes; } private static void PutFileBytes(String outfile, byte[] data, int bytes) { FileStream fs = null; if(bytes > data.Length) { Console.WriteLine("Too many bytes"); return; } try { fs = new FileStream(outfile, FileMode.Create); fs.Write(data, 0, bytes); } catch(Exception e) { Console.WriteLine(e.Message) ; } finally { fs.Close(); } } private static void showWin32Error(int errorcode) { Win32Exception myEx=new Win32Exception(errorcode); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Error code:\t 0x{0:X}", myEx.ErrorCode); Console.WriteLine("Error message:\t {0}\n", myEx.Message); Console.ForegroundColor = ConsoleColor.Gray; } } } '@ # SIG # Begin signature block # MIIMiAYJKoZIhvcNAQcCoIIMeTCCDHUCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUhJNiHyofvAUz9pPc1cSZqxpF # LDygggn9MIIEJjCCAw6gAwIBAgITawAAAB/Nnq77QGja+wAAAAAAHzANBgkqhkiG # 9w0BAQsFADAwMQwwCgYDVQQGEwNMQUIxDTALBgNVBAoTBFpFUk8xETAPBgNVBAMT # CFplcm9EQzAxMB4XDTE3MDkyMDIxMDM1OFoXDTE5MDkyMDIxMTM1OFowPTETMBEG # CgmSJomT8ixkARkWA0xBQjEUMBIGCgmSJomT8ixkARkWBFpFUk8xEDAOBgNVBAMT # B1plcm9TQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCwqv+ROc1 # bpJmKx+8rPUUfT3kPSUYeDxY8GXU2RrWcL5TSZ6AVJsvNpj+7d94OEmPZate7h4d # gJnhCSyh2/3v0BHBdgPzLcveLpxPiSWpTnqSWlLUW2NMFRRojZRscdA+e+9QotOB # aZmnLDrlePQe5W7S1CxbVu+W0H5/ukte5h6gsKa0ktNJ6X9nOPiGBMn1LcZV/Ksl # lUyuTc7KKYydYjbSSv2rQ4qmZCQHqxyNWVub1IiEP7ClqCYqeCdsTtfw4Y3WKxDI # JaPmWzlHNs0nkEjvnAJhsRdLFbvY5C2KJIenxR0gA79U8Xd6+cZanrBUNbUC8GCN # wYkYp4A4Jx+9AgMBAAGjggEqMIIBJjASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsG # AQQBgjcVAgQWBBQ/0jsn2LS8aZiDw0omqt9+KWpj3DAdBgNVHQ4EFgQUicLX4r2C # Kn0Zf5NYut8n7bkyhf4wGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDgYDVR0P # AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUdpW6phL2RQNF # 7AZBgQV4tgr7OE0wMQYDVR0fBCowKDAmoCSgIoYgaHR0cDovL3BraS9jZXJ0ZGF0 # YS9aZXJvREMwMS5jcmwwPAYIKwYBBQUHAQEEMDAuMCwGCCsGAQUFBzAChiBodHRw # Oi8vcGtpL2NlcnRkYXRhL1plcm9EQzAxLmNydDANBgkqhkiG9w0BAQsFAAOCAQEA # tyX7aHk8vUM2WTQKINtrHKJJi29HaxhPaHrNZ0c32H70YZoFFaryM0GMowEaDbj0 # a3ShBuQWfW7bD7Z4DmNc5Q6cp7JeDKSZHwe5JWFGrl7DlSFSab/+a0GQgtG05dXW # YVQsrwgfTDRXkmpLQxvSxAbxKiGrnuS+kaYmzRVDYWSZHwHFNgxeZ/La9/8FdCir # MXdJEAGzG+9TwO9JvJSyoGTzu7n93IQp6QteRlaYVemd5/fYqBhtskk1zDiv9edk # mHHpRWf9Xo94ZPEy7BqmDuixm4LdmmzIcFWqGGMo51hvzz0EaE8K5HuNvNaUB/hq # MTOIB5145K8bFOoKHO4LkTCCBc8wggS3oAMCAQICE1gAAAH5oOvjAv3166MAAQAA # AfkwDQYJKoZIhvcNAQELBQAwPTETMBEGCgmSJomT8ixkARkWA0xBQjEUMBIGCgmS # JomT8ixkARkWBFpFUk8xEDAOBgNVBAMTB1plcm9TQ0EwHhcNMTcwOTIwMjE0MTIy # WhcNMTkwOTIwMjExMzU4WjBpMQswCQYDVQQGEwJVUzELMAkGA1UECBMCUEExFTAT # BgNVBAcTDFBoaWxhZGVscGhpYTEVMBMGA1UEChMMRGlNYWdnaW8gSW5jMQswCQYD # VQQLEwJJVDESMBAGA1UEAxMJWmVyb0NvZGUyMIIBIjANBgkqhkiG9w0BAQEFAAOC # AQ8AMIIBCgKCAQEAxX0+4yas6xfiaNVVVZJB2aRK+gS3iEMLx8wMF3kLJYLJyR+l # rcGF/x3gMxcvkKJQouLuChjh2+i7Ra1aO37ch3X3KDMZIoWrSzbbvqdBlwax7Gsm # BdLH9HZimSMCVgux0IfkClvnOlrc7Wpv1jqgvseRku5YKnNm1JD+91JDp/hBWRxR # 3Qg2OR667FJd1Q/5FWwAdrzoQbFUuvAyeVl7TNW0n1XUHRgq9+ZYawb+fxl1ruTj # 3MoktaLVzFKWqeHPKvgUTTnXvEbLh9RzX1eApZfTJmnUjBcl1tCQbSzLYkfJlJO6 # eRUHZwojUK+TkidfklU2SpgvyJm2DhCtssFWiQIDAQABo4ICmjCCApYwDgYDVR0P # AQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBS5d2bhatXq # eUDFo9KltQWHthbPKzAfBgNVHSMEGDAWgBSJwtfivYIqfRl/k1i63yftuTKF/jCB # 6QYDVR0fBIHhMIHeMIHboIHYoIHVhoGubGRhcDovLy9DTj1aZXJvU0NBKDEpLENO # PVplcm9TQ0EsQ049Q0RQLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNl # cnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9emVybyxEQz1sYWI/Y2VydGlmaWNh # dGVSZXZvY2F0aW9uTGlzdD9iYXNlP29iamVjdENsYXNzPWNSTERpc3RyaWJ1dGlv # blBvaW50hiJodHRwOi8vcGtpL2NlcnRkYXRhL1plcm9TQ0EoMSkuY3JsMIHmBggr # BgEFBQcBAQSB2TCB1jCBowYIKwYBBQUHMAKGgZZsZGFwOi8vL0NOPVplcm9TQ0Es # Q049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENO # PUNvbmZpZ3VyYXRpb24sREM9emVybyxEQz1sYWI/Y0FDZXJ0aWZpY2F0ZT9iYXNl # P29iamVjdENsYXNzPWNlcnRpZmljYXRpb25BdXRob3JpdHkwLgYIKwYBBQUHMAKG # Imh0dHA6Ly9wa2kvY2VydGRhdGEvWmVyb1NDQSgxKS5jcnQwPQYJKwYBBAGCNxUH # BDAwLgYmKwYBBAGCNxUIg7j0P4Sb8nmD8Y84g7C3MobRzXiBJ6HzzB+P2VUCAWQC # AQUwGwYJKwYBBAGCNxUKBA4wDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOC # AQEAszRRF+YTPhd9UbkJZy/pZQIqTjpXLpbhxWzs1ECTwtIbJPiI4dhAVAjrzkGj # DyXYWmpnNsyk19qE82AX75G9FLESfHbtesUXnrhbnsov4/D/qmXk/1KD9CE0lQHF # Lu2DvOsdf2mp2pjdeBgKMRuy4cZ0VCc/myO7uy7dq0CvVdXRsQC6Fqtr7yob9NbE # OdUYDBAGrt5ZAkw5YeL8H9E3JLGXtE7ir3ksT6Ki1mont2epJfHkO5JkmOI6XVtg # anuOGbo62885BOiXLu5+H2Fg+8ueTP40zFhfLh3e3Kj6Lm/NdovqqTBAsk04tFW9 # Hp4gWfVc0gTDwok3rHOrfIY35TGCAfUwggHxAgEBMFQwPTETMBEGCgmSJomT8ixk # ARkWA0xBQjEUMBIGCgmSJomT8ixkARkWBFpFUk8xEDAOBgNVBAMTB1plcm9TQ0EC # E1gAAAH5oOvjAv3166MAAQAAAfkwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwx # CjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGC # NwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFL82zy19q30w2oHH # 7e6t5qTs9SWTMA0GCSqGSIb3DQEBAQUABIIBALE/gY//SxPUAlWIZD2WZCR4Rywj # CtZa0lXdrpigDxrXrhytR+7zsNJw8JppGAOe9ZTXlwRsAnfefD4RJ68gr93B8Sd4 # 4OCrf1Z4LQ+tv22T6lzZ5zEmHSGmbhMB/8yRiVbye+UdkVAQFctJMkvkx6xSlfpk # +w8FTwAtZuFLh/acgiASjfsPTV+gvHxkd8D+cPDOLmOIUgHE1XX6tvT8tA6V9V0b # 4OcFofxMmkH5DzXzud9XY52nmwN38SroK37HYkcRQrhP7TcF+BRmkdyy1tiJWn8o # f3cZokFOv97M4AD9gw5GPHLxhat7cvirTeNRPjdKbbOwz+weKAiPZkCFD3Q= # SIG # End signature block |