I found a way to create the link between the certificate in the store and the provider dll (via the name under which the provider dll is registered in the system). The relevant system API functions are CertSetCertificateContextProperty and CertGetCertificateContextProperty in Crypt32.dll. I was able to verify that this works for signing hlkx files (from with Hardware Lab Kit software or via C# code using PackageDigitalSignatureManager) but I still have problems using this way to sign e.g. executables using Microsoft's signtool.exe which complains about the private key not be available for the certificate.
I was using the system API functions from within C# so I have extracted the relevant code fragments from my project on how to link the certificate with the provider and how to read information on the linked provider from a certificate.
class Program
{
private const UInt32 CERT_SET_KEY_CONTEXT_PROP_ID = 0x00000001;
private const UInt32 CERT_SET_KEY_PROV_HANDLE_PROP_ID = 0x00000001;
private const UInt32 CERT_KEY_PROV_INFO_PROP_ID = 2;
static void Main(string[] args)
{
// Reading certificate from file
X509Certificate2 certificate = new X509Certificate2("C:\\MyCert.crt");
// Adding certificate to store
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
store.Close();
// Linking certificate with provider
// ProviderName is the name under which the provider is registered in the system
// ContainerName is a string that will be passed to the DLL when calls are made it can be used to
// additional information to the DLL that can be set when linking the certificate with the provider
SetCertificateProviderInformation("My Provider Name", "MyContainerName", certificate);
// Read provider information
GetCertificateProviderInformation(certificate);
}
private static void SetCertificateProviderInformation(string providerName, string containerName, X509Certificate2 certificate)
{
Crypt32Dll.CRYPT_KEY_PROV_INFO cryptKeyProvInfo = new Crypt32Dll.CRYPT_KEY_PROV_INFO
{
pwszProvName = providerName,
pwszContainerName = containerName,
dwProvType = 24,
dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID | CERT_SET_KEY_PROV_HANDLE_PROP_ID,
cProvParam = 0,
rgProvParam = IntPtr.Zero,
dwKeySpec = 2
};
IntPtr pvData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Crypt32Dll.CRYPT_KEY_PROV_INFO)));
Marshal.StructureToPtr(cryptKeyProvInfo, pvData, false);
if (Crypt32Dll.CertSetCertificateContextProperty(certificate.Handle, CERT_KEY_PROV_INFO_PROP_ID, 0, pvData))
{
// succeeded
}
else
{
Int32 lastError = Marshal.GetLastWin32Error();
// failed
}
if (pvData != IntPtr.Zero)
{
Marshal.FreeHGlobal(pvData);
}
}
private static void GetCertificateProviderInformation(X509Certificate2 certificate)
{
UInt32 dataSize = 0;
// Get required size for struct
if (Crypt32Dll.CertGetCertificateContextProperty(certificate.Handle, CERT_KEY_PROV_INFO_PROP_ID, IntPtr.Zero, ref dataSize))
{
// Allocate unmanaged struct memory of required size and query the information
IntPtr pvData = Marshal.AllocHGlobal((int)dataSize);
if (Crypt32Dll.CertGetCertificateContextProperty(certificate.Handle, CERT_KEY_PROV_INFO_PROP_ID, pvData, ref dataSize))
{
// succeeded
Crypt32Dll.CRYPT_KEY_PROV_INFO keyProviderInformation = (Crypt32Dll.CRYPT_KEY_PROV_INFO)Marshal.PtrToStructure(pvData, typeof(Crypt32Dll.CRYPT_KEY_PROV_INFO));
Console.Out.WriteLine("Provider Name: " + keyProviderInformation.pwszProvName);
Console.Out.WriteLine("Container Name: " + keyProviderInformation.pwszContainerName);
}
else
{
int lastError = Marshal.GetLastWin32Error();
// failed
}
// Free unmanaged struct memory
Marshal.FreeHGlobal(pvData);
}
else
{
// failed
}
}
}
With the code for using the Crypt32.dll being:
class Crypt32Dll
{
private const string DLL_NAME = "Crypt32.dll";
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct CRYPT_KEY_PROV_INFO
{
[MarshalAs(UnmanagedType.LPWStr)]
internal string pwszContainerName;
[MarshalAs(UnmanagedType.LPWStr)]
internal string pwszProvName;
internal UInt32 dwProvType;
internal UInt32 dwFlags;
internal UInt32 cProvParam;
internal IntPtr rgProvParam;
internal UInt32 dwKeySpec;
}
[DllImport(DLL_NAME, EntryPoint = "CertSetCertificateContextProperty", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool CertSetCertificateContextProperty(
IntPtr pCertContext,
UInt32 dwPropId,
UInt32 dwFlags,
IntPtr pvData
);
[DllImport(DLL_NAME, EntryPoint = "CertGetCertificateContextProperty", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool CertGetCertificateContextProperty(
IntPtr pCertContext,
UInt32 dwPropId,
IntPtr pvData,
ref UInt32 pcbData
);
}