I have identified an issue related to building apps that use C:\Windows\System32\CertEnroll.dll as a reference.
The following code works fine when compiled using VS 2015 on Windows 7 and then ran on a Windows 7 machine.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CERTENROLLLib;
namespace CertTest
{
class Program
{
static void Main(string[] args)
{
try
{
CX509PrivateKey key = new CX509PrivateKey();
key.ContainerName = Guid.NewGuid().ToString();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
When you try and compile this in Windows 10 and then try and run it on a Windows 7 machine, it throws the following error.
"Unable to cast COM object of type 'System.__ComObject' to interface type 'CERTENROLLLib.CX509PrivateKey'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{728AB362-217D-11DA-B2A4-000E7BBB2B09}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))."
I have had several people here replicate it and I'd like to get more input before contacting Microsoft on what's going on here.
I guess my question is: Can anybody else confirm this or if it's confirmed they broke backward compatibilty?
I had the same problem, my development machine is running on windows 10 and the build server windows 8.1.
But since c# has the ability of reflection and dynamic types, i now first analyze which types the InitializeFromPrivateKey method takes as parameters(I separated it from the actually certificate code by creating a method).
And then use a dynamic type dependent on which type the second parameter is.
Somehow the Interface Implementation on CertEnroll.dll changed between "vanilla" Windows 2008 and Windows 2008 R2. I guess it is the same with some Windows 7 builds. To get it (halfway) working, you have to instantiate the classes with
Activator.CreateInstance(Type.GetTypeFromProgID(<TypeName>)
; This will cause the system to look up the references in HKLM:\SOFTWARE\Classes\Interface\ to get the right class for you.Working Example:
(Part of this code was used from https://stackoverflow.com/a/13806300/5243037)
Remarks
So why did I write "halfway"? There is a problem with certenroll.dll V. 6, which causes the build to fail on cert.InitializeFromPrivateKey. In certenroll.dll V 6.0, the second parameter must be of type "CX509PrivateKey", whereas on Win10 machines with Certenroll.dll V 10, it's IX509PrivateKey:
So you would think: Yea, simply "cast" the privateKey in the above example to CX509PrivateKey on Activator.CreateInstance. The Problem here is, it will compile, but on vanilla Win2k8 it will not give you the class (CX509...) but the Interface (IX509...), and thus the cast fails and returns null.
We've solved this issue by compiling the certenrollment function in a seperate project on a machine with certenroll.dll V 10. It compiles fine and works in Win2k8, too. It's never-the-less a bit annoying to have it in a seperate project, since the build will fail on our buildserver with certenroll.dll V 6.
These are the steps from Microsoft to resolve this issue
If you use Windows 10 solely as your build environment then the executable would run on downlevel OSes, however if you really just want to have a project that you can compile anywhere and run anywhere then the only solution is to create your own interop DLL that you include in the project folder. You would have to generate it on Windows 7 first and reference that DLL.
Tlbimp.exe CertEnroll_Interop c:\Windows\System32\CertEnroll.dll
This generates a CertEnroll_Interop.dll file that you can copy to your project folder and then browse to in your project. Of course you would need the using “using CertEnroll_Interop;” statement.
You can build the project on Windows 10 and have it run on Windows 7 and Windows 8.1 and any other combination.