//******************************************************************************** // // DecodeStoreKey // X.509 Store Certificate to Public Key Decoder // // Copyright (C) 2003. Michel I. Gallant // //********************************************************************************* // // DecodeStoreKey.cs // // This C# utility for .NET Framework 1.0/1.1 // Searches CryptoAPI certificate store(s) for substring match to SubjectName // Displays the ASN.1 encoded public key and CryptoAPI PUBLICKEYBLOB for a matched certificate. // Allows saving both encoded and decoded public keys to file. // Decodes the PUBLICKEYBLOB into fields and displays exponent and modulus bytes // in big-endian form suitable for RSAParameters fields. // // Handles either binary DER or BASE64 encoded X.509 v3 certificates. // //********************************************************************************** using System; using System.IO; using System.Text; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Runtime.InteropServices; namespace JavaScience { public class Win32 { [DllImport("crypt32.dll")] public static extern bool CryptDecodeObject( uint CertEncodingType, uint lpszStructType, byte[] pbEncoded, uint cbEncoded, uint flags, [In, Out] byte[] pvStructInfo, ref uint cbStructInfo); [DllImport("crypt32.dll", SetLastError=true)] public static extern IntPtr CertFindCertificateInStore( IntPtr hCertStore, uint dwCertEncodingType, uint dwFindFlags, uint dwFindType, [In, MarshalAs(UnmanagedType.LPWStr)]String pszFindString, IntPtr pPrevCertCntxt) ; [DllImport("crypt32.dll", SetLastError=true)] public static extern bool CertFreeCertificateContext( IntPtr hCertStore) ; [DllImport("crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)] public static extern IntPtr CertOpenStore( IntPtr storeProvider, uint dwMsgAndCertEncodingType, IntPtr hCryptProv, uint dwFlags, String cchNameString) ; [DllImport("crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)] //overloaded public static extern IntPtr CertOpenStore( [MarshalAs(UnmanagedType.LPStr)] String storeProvider, uint dwMsgAndCertEncodingType, IntPtr hCryptProv, uint dwFlags, String cchNameString) ; [DllImport("crypt32.dll", SetLastError=true)] public static extern bool CertCloseStore( IntPtr hCertStore, uint dwFlags) ; } [StructLayout(LayoutKind.Sequential)] public struct PUBKEYBLOBHEADERS { public byte bType; //BLOBHEADER public byte bVersion; //BLOBHEADER public short reserved; //BLOBHEADER public uint aiKeyAlg; //BLOBHEADER public uint magic; //RSAPUBKEY public uint bitlen; //RSAPUBKEY public uint pubexp; //RSAPUBKEY } public class DecodeStoreKey { const uint CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000; const uint CERT_STORE_READONLY_FLAG = 0x00008000; const uint CERT_STORE_OPEN_EXISTING_FLAG = 0x00004000; const uint CERT_FIND_SUBJECT_STR = 0x00080007; const uint X509_ASN_ENCODING = 0x00000001; const uint PKCS_7_ASN_ENCODING = 0x00010000; const uint RSA_CSP_PUBLICKEYBLOB = 19; static uint ENCODING_TYPE = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING ; static string[] CAPIStores = {"MY", "ADDRESSBOOK", "ROOT"}; public static void Main(String[] args) { X509Certificate cert =null; byte[] encodedpubkey = null; byte[] publickeyblob = null; IntPtr hSysStore = IntPtr.Zero; IntPtr hCertCntxt = IntPtr.Zero; string[] searchstores; if(args.Length<1){ usage(); return; } string searchstr = args[0]; if(args.Length==1) searchstores = CAPIStores; //no store name provided so use fixed list. else searchstores = new string[]{args[1]}; //---------- Search store(s) for a matching certificate ------------- uint openflags = CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG; foreach(String store in searchstores) { hSysStore = Win32.CertOpenStore("System", ENCODING_TYPE, IntPtr.Zero, openflags, store ); if(hSysStore == IntPtr.Zero){ Console.WriteLine("Failed to open system store {0}", store); continue; } hCertCntxt=Win32.CertFindCertificateInStore( hSysStore, ENCODING_TYPE, 0, CERT_FIND_SUBJECT_STR, searchstr , IntPtr.Zero) ; if(hCertCntxt != IntPtr.Zero){ //use certcontext from managed code Console.WriteLine("CertContext:\t{0}", hCertCntxt) ; cert = new X509Certificate(hCertCntxt); Console.WriteLine("\nFound certificate with SubjectName string \"{0}\"",searchstr); Console.WriteLine("SubjectName:\t{0}", cert.GetName()); break; } } // end foreach //------- Clean Up ----------- if(hCertCntxt != IntPtr.Zero) Win32.CertFreeCertificateContext(hCertCntxt); if(hSysStore != IntPtr.Zero) Win32.CertCloseStore(hSysStore, 0) ; //----------------------------------------------------------------- if(cert==null){ Console.WriteLine("No certificate found in {0} stores matching substring {1}", "STOREsPLACEHOLDER", searchstr); return; } // Get the asn.1 encoded publickey bytes. encodedpubkey = cert.GetPublicKey(); uint blobbytes=0; // Display the value to the console. Console.WriteLine(); showBytes("Encoded publickey", encodedpubkey); Console.WriteLine(); if(Win32.CryptDecodeObject(ENCODING_TYPE, RSA_CSP_PUBLICKEYBLOB, encodedpubkey, (uint)encodedpubkey.Length, 0, null, ref blobbytes)) { publickeyblob = new byte[blobbytes]; if(Win32.CryptDecodeObject(ENCODING_TYPE, RSA_CSP_PUBLICKEYBLOB, encodedpubkey, (uint)encodedpubkey.Length, 0, publickeyblob, ref blobbytes)) showBytes("CryptoAPI publickeyblob", publickeyblob); } else{ Console.WriteLine("Couldn't decode publickeyblob from certificate publickey") ; return;} PUBKEYBLOBHEADERS pkheaders = new PUBKEYBLOBHEADERS() ; int headerslength = Marshal.SizeOf(pkheaders); IntPtr buffer = Marshal.AllocHGlobal( headerslength); Marshal.Copy( publickeyblob, 0, buffer, headerslength ); pkheaders = (PUBKEYBLOBHEADERS) Marshal.PtrToStructure( buffer, typeof(PUBKEYBLOBHEADERS) ); Marshal.FreeHGlobal( buffer ); Console.WriteLine("\n ---- PUBLICKEYBLOB headers ------"); Console.WriteLine(" btype {0}", pkheaders.bType); Console.WriteLine(" bversion {0}", pkheaders.bVersion); Console.WriteLine(" reserved {0}", pkheaders.reserved); Console.WriteLine(" aiKeyAlg 0x{0}", pkheaders.aiKeyAlg); String magicstring = (new ASCIIEncoding()).GetString(BitConverter.GetBytes(pkheaders.magic)) ; Console.WriteLine(" magic 0x{0:x4} '{1}'", pkheaders.magic, magicstring); Console.WriteLine(" bitlen {0}", pkheaders.bitlen); Console.WriteLine(" pubexp {0}", pkheaders.pubexp); Console.WriteLine(" --------------------------------"); //----- Get public exponent in big-endian byte array, suitable for RSAParameters.Exponent ------------- byte[] exponent = BitConverter.GetBytes(pkheaders.pubexp); //returns bytes in little-endian order Array.Reverse(exponent); //PUBLICKEYBLOB stores in LITTLE-endian order; convert to BIG-endian order showBytes("\nPublic key exponent (big-endian order):", exponent); //----- Get modulus in big-endian byte array, suitable for RSAParameters.Modulus ------------- int modulusbytes = (int)pkheaders.bitlen/8 ; byte[] modulus = new byte[modulusbytes]; try{ Array.Copy(publickeyblob, headerslength, modulus, 0, modulusbytes); Array.Reverse(modulus); //convert from little to big-endian ordering. showBytes("\nPublic key modulus (big-endian order):", modulus); } catch(Exception){ Console.WriteLine("Problem getting modulus from publickeyblob"); } Console.Write("\nWrite public key to encoded-key and PUBLICKEYBLOB files? [y|n] "); string ans = Console.ReadLine(); if(ans.Trim().StartsWith("Y") || ans.Trim().StartsWith("y")){ WriteKeyBlob("encodedpubkey_" + searchstr, encodedpubkey); WriteKeyBlob("publickeyblob_" + searchstr, publickeyblob); } //------- Try to instantiate an RSACryptoServiceProvider --------- //Create a new instance of RSACryptoServiceProvider. RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(); //Create a new instance of RSAParameters. RSAParameters RSAKeyInfo = new RSAParameters(); //Set RSAKeyInfo to the public key values. RSAKeyInfo.Modulus = modulus; RSAKeyInfo.Exponent = exponent; //Import key parameters into RSA. oRSA.ImportParameters(RSAKeyInfo); //------------------------------------------------------------ } 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(); } private static void WriteKeyBlob(String keyblobfile, byte[] keydata) { FileStream fs = null; if (File.Exists(keyblobfile)) { Console.WriteLine("File '{0}' already exists!", keyblobfile); return; } try{ fs = new FileStream(keyblobfile, FileMode.CreateNew); fs.Write(keydata, 0, keydata.Length); Console.WriteLine("Wrote public key file '{0}'", keyblobfile) ; } catch(Exception e) { Console.WriteLine(e.Message) ; } finally { fs.Close(); } } private static void usage() { Console.WriteLine("\nUsage:\nDecodeStoreKey.exe "); } } }