I am trying to determine the name of a private key file for a certificate stored on a remote Windows machine (2k3 / 2k8), and I have some difficulties. I am also not familiar with Microsoft CryptAPI, so I'm looking for any help you can provide.
The goal of this exercise is to find certificates with private keys installed on a remote computer that meet certain criteria and provide the correct rights to their personal key files. Although I could assign permissions at the folder level, I would prefer only assign permissions at the file level of the private key, where necessary (for obvious reasons).
In this scenario, suppose that a service account with administrative-like permissions accesses a certificate store:
I am returning a remote certificate store using the following call from C # using p / invoke:
[DllImport ("CRYPT32", EntryPoint = "CertOpenStore", CharSet = CharSet.Unicode, SetLastError = true)] public static extern IntPtr CertOpenStore (int storeProvider, int encodingType, int hcryptProv, int flags, string pvPara);
IntPtr storeHandle = CertOpenStore (CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, string.Format (@ "\ {0} {1}", server_name, name));
Then I use CertEnumCertificatesInStore to restore the certificates I want to evaluate.
[DllImport ("CRYPT32", EntryPoint = "CertEnumCertificatesInStore", CharSet = CharSet.Unicode, SetLastError = true)] public static extern IntPtr CertEnumCertificatesInStore (IntPtr storeProvider, IntPtr prevCertContext); IntPtr certCtx = IntPtr.Zero; certCtx = CertEnumCertificatesInStore (storeHandle, certCtx);
If the certificate meets my criteria, I create an X509Certificate2 instance from IntPtr returned from the CertEnumCertificatesInStore call, for example:
X509Certificate2 current = new X509Certificate2 (certCtx);
Once I have X509Certificate2 instances for the certificates that interest me, I call CryptAcquireCertificatePrivateKey to get the private key provider:
[DllImport ("crypt32", CharSet = CharSet.Unicode, SetLastError = true)] internal extern static bool CryptAcquireCertificatePrivateKey (IntPtr pCert, uint dwFlags, IntPtr pvReserved, ref IntPtr phCryptProv, ref int pcwrl;
// cert is X509Certificate2
CryptAcquireCertificatePrivateKey (cert.Handle, 0, IntPtr.Zero, ref hProvider, ref_keyNumber, ref freeProvider);
To restore the private key file name, I am trying to request a unique container name from hProvider as pData, for example:
[DllImport ("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] internal extern static bool CryptGetProvParam (IntPtr hCryptProv, CryptGetProvParamType dwParam, IntPtr pvData, ref int pcbData, uint;
IntPtr pData = IntPtr.Zero; CryptGetProvParam (hProvider, PP_UNIQUE_CONTAINER, pData, ref cbBytes, 0));
So far, all of the above steps have worked fine locally (server_name == local machine name); however, the unique name of the container (the name of the private key file) that is returned for the certificate stored in the certificate store of the local computer of the remote computer does not appear as the actual name of the private key file that I see under:
w2k3: \ Documents and Settings \ All Users \ Application Data \ Microsoft \ Crypto \ RSA \ MachineKeys
ws08: \ ProgramData \ Microsoft \ Crypto \ RSA \ MachineKeys
For example, if I run the above steps directly on a remote machine, I get the private key file name AAAAAAA-111111, but if I run them remotely, I get the private key BBBBBBBBB-2222222. Also, if I install the remote certificate locally and follow the steps on my local computer, I get the same BBBBBBBBB-2222222 private key name.
Most likely, I feel that in step 4 I can skip the disclaimer by calling CryptAcquireCertificatePrivateKey. Perhaps this call is based on the local machine identifier to generate the name of a unique container that will be used to store the private key block.
Update
After some further research, I found a blog that describes in detail how file names are created for containers with a private key here .
Instead of using CryptAcquireCertificatePrivateKey, you can use the methods described in this blog to get the name of the private key container on any computer, as soon as you have the name of the container received by CertGetCertificateContextProperty. This code shows how to get the name of the private key container so that you can generate the name of the private key file. * disclaimer - I'm sure this can be changed and may not even be complete, but I am sending it in case it helps someone else in the future *
Structures and P / Challenge:
[StructLayout(LayoutKind.Sequential)] public struct CryptKeyProviderInfo { [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; } public const uint CERT_KEY_PROV_INFO_PROP_ID = 0x00000002; [DllImport("crypt32.dll", SetLastError = true)] internal extern static bool CertGetCertificateContextProperty(IntPtr pCertContext, uint dwPropId, IntPtr pvData, ref uint pcbData); IntPtr providerInfo = IntPtr.Zero; string containerName = string.Empty; try {