FindPrivateKey X509


namespace Microsoft.ChannelSDK.Tools.FindPrivateKey
{
    using System;
    using System.IO;
    using System.Security.Cryptography.X509Certificates;
    using System.Runtime.InteropServices;
    class FindPrivateKey
    {
        static void PrintHelp()
        {
            Console.WriteLine("FindPrivateKey helps user to find the location of the Private Key file of a X.509 Certificate.");
            Console.WriteLine("Usage: FindPrivateKey <storeName> <storeLocation> [{ {-n <subjectName>} | {-t <thumbprint>} } [-f | -d | -a]]");
            Console.WriteLine("       <subjectName> subject name of the certificate");
            Console.WriteLine("       <thumbprint>  thumbprint of the certificate (use certmgr.exe to get it)");
            Console.WriteLine("       -f            output file name only");
            Console.WriteLine("       -d            output directory only");
            Console.WriteLine("       -a            output absolute file name");
            Console.WriteLine("e.g. FindPrivateKey My CurrentUser -n \"CN=John Doe\"");
            Console.WriteLine("e.g. FindPrivateKey My LocalMachine -t \"03 33 98 63 d0 47 e7 48 71 33 62 64 76 5c 4c 9d 42 1d 6b 52\" -a");
        }
        static void Main(string[] args)
        {            
            if (args.Length < 2 || args.Length == 3 || args.Length > 5 
                || (args.Length > 2 && args[2] != "-n" && args[2] != "-t")
                || (args.Length == 5 && args[4] != "-f" && args[4] != "-d" && args[4] != "-a"))
            {
                PrintHelp();
                return;
            }
            try
            {
                StoreName storeName = (StoreName)Enum.Parse(typeof(StoreName), args[0], true);
                StoreLocation storeLocation = (StoreLocation)Enum.Parse(typeof(StoreLocation), args[1], true);
                X509Certificate2 cert;
                if (args.Length > 2)
                {
                    // insert a comma followed by a space for store.Certificates.Find(findType, key, false) 
                    // to successful find the certificate
                    string key = args[3];
                    string[] keys = key.Split(',');
                    key = string.Empty;
                    for (int i = 0; i < keys.Length; i++)
                    {
                        key += keys[i];
                        if ( i != keys.Length -1 )
                         key += ", ";
                    }
                    if (args[2] == "-n")
                        cert = LoadCertificate(storeName, storeLocation, key, X509FindType.FindBySubjectDistinguishedName);
                    else
                        cert = LoadCertificate(storeName, storeLocation, key, X509FindType.FindByThumbprint);
                }
                else
                {
                    cert = SelectCertificate(storeName, storeLocation);
                    if (cert == null)
                        return;
                }
                string privateKeyFile = GetKeyFileName(cert);
                string privateKeyDirectory = GetKeyFileDirectory(privateKeyFile);
                if (args.Length == 5)
                {
                    if (args[4] == "-f")
                        Console.WriteLine(privateKeyFile);
                    else if (args[4] == "-d")
                        Console.WriteLine(privateKeyDirectory);
                    else
                        Console.WriteLine("a:{0}\\{1}", privateKeyDirectory, privateKeyFile);
                }
                else
                {
                    Console.WriteLine("Private key directory:");
                    Console.WriteLine(privateKeyDirectory);
                    Console.WriteLine("Private key file name:");
                    Console.WriteLine(privateKeyFile);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("FindPrivateKey failed for the following reason:");
                Console.WriteLine(ex.Message);
                Console.WriteLine("\nUse /? option for help");
            }
        }
        static X509Certificate2 SelectCertificate(StoreName storeName, StoreLocation storeLocation)
        {
            X509Certificate2 result;
            X509Store store = new X509Store(storeName, storeLocation);
            store.Open(OpenFlags.ReadOnly);
            try
            {
                X509Certificate2Collection matches;
                matches = X509Certificate2UI.SelectFromCollection(store.Certificates, "Select certificate", "Select the certificate to find the location of the associated private key file:", X509SelectionFlag.SingleSelection);
                if (matches.Count != 1)
                    result = null;
                else
                    result = matches[0];
            }
            finally
            {
                store.Close();
            }
            return result;
        }
        static X509Certificate2 LoadCertificate(StoreName storeName, StoreLocation storeLocation, string key, X509FindType findType)
        {
            X509Certificate2 result;
            X509Store store = new X509Store(storeName, storeLocation);
            store.Open(OpenFlags.ReadOnly);
            try
            {
                X509Certificate2Collection matches;
                matches = store.Certificates.Find(findType, key, false);
                if (matches.Count > 1)
                    throw new InvalidOperationException(String.Format("More than one certificate with key '{0}' found in the store.", key));
                if (matches.Count == 0)
                    throw new InvalidOperationException(String.Format("No certificates with key '{0}' found in the store.", key));
                result = matches[0];
            }
            finally
            {
                store.Close();
            }
            return result;
        }
        static string GetKeyFileName(X509Certificate2 cert)
        {
            IntPtr            hProvider     = IntPtr.Zero; // CSP handle
            bool              freeProvider  = false;       // Do we need to free the CSP ?
            uint               acquireFlags  = 0;
            int                  _keyNumber = 0;
            string              keyFileName = null;
            byte[]              keyFileBytes = null;
            //
            // Determine whether there is private key information available for this certificate in the key store
            //
            if ( CryptAcquireCertificatePrivateKey(cert.Handle,
                acquireFlags,
                IntPtr.Zero,
                ref hProvider,
                ref _keyNumber,
                ref freeProvider) )
            {
                IntPtr pBytes  = IntPtr.Zero; // Native Memory for the CRYPT_KEY_PROV_INFO structure
                int    cbBytes = 0;           // Native Memory size
                try
                {
                    if ( CryptGetProvParam(hProvider, CryptGetProvParamType.PP_UNIQUE_CONTAINER, IntPtr.Zero, ref cbBytes, 0) )
                    {
                        pBytes = Marshal.AllocHGlobal(cbBytes);
                        if ( CryptGetProvParam(hProvider, CryptGetProvParamType.PP_UNIQUE_CONTAINER, pBytes, ref cbBytes, 0) )
                        {
                            keyFileBytes = new byte[cbBytes];
                            Marshal.Copy(pBytes,keyFileBytes,0,cbBytes);
                            // Copy eveything except tailing null byte
                            keyFileName = System.Text.Encoding.ASCII.GetString(keyFileBytes, 0, keyFileBytes.Length-1);
                        }
                    }        
                }
                finally
                {
                    if ( freeProvider )
                        CryptReleaseContext(hProvider,0);
                    //
                    // Free our native memory
                    //
                    if ( pBytes != IntPtr.Zero )
                        Marshal.FreeHGlobal(pBytes);
                }
            }
            if (keyFileName == null)
                throw new InvalidOperationException("Unable to obtain private key file name");
            return keyFileName;
        }
        static string GetKeyFileDirectory(string keyFileName)
        {
            // Look up All User profile from environment variable
            string allUserProfile = System.Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
            // set up searching directory
            string machineKeyDir = allUserProfile + "\\Microsoft\\Crypto\\RSA\\MachineKeys";
            // Seach the key file
            string[] fs = System.IO.Directory.GetFiles(machineKeyDir, keyFileName);
            // If found
            if (fs.Length > 0)
                return machineKeyDir;
            // Next try current user profile
            string currentUserProfile = System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            // seach all sub directory
            string userKeyDir = currentUserProfile + "\\Microsoft\\Crypto\\RSA\\";
            fs = System.IO.Directory.GetDirectories(userKeyDir);
            if (fs.Length > 0)
            {
                // for each sub directory
                foreach (string keyDir in fs)
                {
                    fs = System.IO.Directory.GetFiles(keyDir, keyFileName);
                    if (fs.Length == 0)
                        continue;
                    else
                        // found
                        return keyDir;
                }
            }
            throw new InvalidOperationException("Unable to locate private key file directory");
        }
        [DllImport("crypt32", CharSet = CharSet.Unicode, SetLastError = true)]
        internal extern static bool CryptAcquireCertificatePrivateKey(IntPtr pCert, uint dwFlags, IntPtr pvReserved, ref IntPtr phCryptProv, ref int pdwKeySpec, ref bool pfCallerFreeProv);
        [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
        internal extern static bool CryptGetProvParam(IntPtr hCryptProv, CryptGetProvParamType dwParam, IntPtr pvData, ref int pcbData, uint dwFlags);
        [DllImport("advapi32", SetLastError = true)]
        internal extern static bool CryptReleaseContext(IntPtr hProv, uint dwFlags);
    }
    enum CryptGetProvParamType
    {
        PP_ENUMALGS = 1,
        PP_ENUMCONTAINERS = 2,
        PP_IMPTYPE = 3,
        PP_NAME = 4,
        PP_VERSION = 5,
        PP_CONTAINER = 6,
        PP_CHANGE_PASSWORD = 7,
        PP_KEYSET_SEC_DESCR = 8,       // get/set security descriptor of keyset
        PP_CERTCHAIN = 9,      // for retrieving certificates from tokens
        PP_KEY_TYPE_SUBTYPE = 10,
        PP_PROVTYPE = 16,
        PP_KEYSTORAGE = 17,
        PP_APPLI_CERT = 18,
        PP_SYM_KEYSIZE = 19,
        PP_SESSION_KEYSIZE = 20,
        PP_UI_PROMPT = 21,
        PP_ENUMALGS_EX = 22,
        PP_ENUMMANDROOTS = 25,
        PP_ENUMELECTROOTS = 26,
        PP_KEYSET_TYPE = 27,
        PP_ADMIN_PIN = 31,
        PP_KEYEXCHANGE_PIN = 32,
        PP_SIGNATURE_PIN = 33,
        PP_SIG_KEYSIZE_INC = 34,
        PP_KEYX_KEYSIZE_INC = 35,
        PP_UNIQUE_CONTAINER = 36,
        PP_SGC_INFO = 37,
        PP_USE_HARDWARE_RNG = 38,
        PP_KEYSPEC = 39,
        PP_ENUMEX_SIGNING_PROT = 40,
        PP_CRYPT_COUNT_KEY_USE = 41,
    }
}

posted @ 2009-05-19 23:02  于斯人也  阅读(3511)  评论(0编辑  收藏  举报