views:

312

answers:

2

We have an existing certificate issuing application (C#, ASP.NET, JavaScript) that issues certificates to Windows XP users using XenRoll in IE. I need to extend this to support Windows Vista and Windows 7 users, also using IE.

For Vista and 7, Microsoft replaced the XenRoll ActiveX controll with the new CertEnroll control. I've got this working in Vista SP2, but in 7 I get this error at the installation step:

CertEnroll::CX509Enrollment::InstallResponse: A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider. 0x800b0109 (-2146762487)

Here's a snippet of the relevant HTML & JavaScript:

<object id="classFactoryObj" classid="clsid:884e2049-217d-11da-b2a4-000e7bbb2b09"></object>

<script type="text/javascript">
    function InstallCert() 
    {  
        try
        {
            var classFactory = document.getElementById("classFactoryObj");
            var objEnroll = classFactory.CreateObject("X509Enrollment.CX509Enrollment");

            var signedCert = '-----BEGIN CERTIFICATE-----' + 
                'REMOVED FOR BREVITY' + 
                '-----END CERTIFICATE-----';

            objEnroll.Initialize(1); // User context
            objEnroll.InstallResponse(4, signedCert, 6, ""); // AllowUntrustedRoot = 4

            alert('Certificate installed');
        }
        catch (ex)
        {
            alert('Unable to install certificate: ' + ex.description);
        }
     }

    InstallCert();
</script>

Now, it's true that the root certificate is not trusted, but I'm calling InstallResponse with the first parameter set to 4, which is supposed to allow installation even if the root certificate is not trusted. This works as advertised in Vista, but doesn't seem to in Windows 7.

I tested, and it does work if the root certificate is trusted. I'm sure some one will say it, so I'll pre-empt it - having the clients trust the root certificate is not really an option for us (we want to distribute client authentication certificates to customers, as a part of authenticating them on our network).

Am I doing something wrong here? Has anyone else got this working in Windows 7?

+1  A: 

I wrote this script a while ago for a demo (in conjunction with this page). It supports <keygen/> and replaces it with XEnroll or CertEnroll calls on Internet Explorer. The project has evolved a bit since, but I've just tested this branch with IE8 on Windows 7 and it worked. The CA certificate wasn't on the client machine at all. I had to lower the security settings to 'low' for it to run the ActiveX (otherwise, it wouldn't even submit the request, so even less install the certificate in the response).

If that helps, I do this:

try {
    enrollObj.InstallResponse(4, xmlHttpRequest.responseText,
            0, "");
    window.alert("A certificate has been installed.");
} catch (e1) {
    try {
        enrollObj.InstallResponse(0,
                xmlHttpRequest.responseText, 0, "");
        window.alert("A certificate has been installed.");
    } catch (e2) {
        window
                .alert("You're probably using Vista without SP1 or above, in which case you need to add the certificate of this authority as a trusted root certificate.");
    }
}

I must admit I didn't test which of these two cases was used (as it's the same alert message).

Bruno
I have no issue with the ActiveX settings - if you run CertEnroll over HTTPS you won't get that.
Cocowalla
Indeed, I was just running this on a test plain HTTP server today.
Bruno
I've just checked, it's the first one that works for me `enrollObj.InstallResponse(4, xmlHttpRequest.responseText, 0, "");` Maybe there's something else in the rest of the initialization. (Sorry, I can't remember all the details right now.)
Bruno
Looking at your code it seems we are doing the same thing (I actually decide upon XenRoll, CertEnroll or keygen server-side instead of client-side). Did you test on Windows 7 with or without SP1 installed?
Cocowalla
Win7 Pro, without SP1.
Bruno
I'm using the same :(I see you are using the same object to both make the request and install the certificate. Any idea if that would make any difference?
Cocowalla
What you're missing is some form of initialization. See: http://msdn.microsoft.com/en-us/library/aa378051%28VS.85%29.aspx"Before calling the InstallResponse method, you must initialize the IX509Enrollment object by calling one of the following methods: Initialize, InitializeFromRequest, InitializeFromTemplateName"I'm not sure which one is appropriate for you.
Bruno
I'm already doing that; see the line:`objEnroll.Initialize(1); // User context`
Cocowalla
Sorry, I've just noticed indeed.
Bruno
I've just tried on my script to create a new object when getting the ajax response (before install response). This works:`enrollObj = enrollFactObj.CreateObject("X509Enrollment.CX509Enrollment");``enrollObj.Initialize(1);``enrollObj.InstallResponse(4, xmlHttpRequest.responseText, 0, "");`The main difference is the 3rd parameter, I'm using `XCN_CRYPT_STRING_BASE64HEADER` and you're using `XCN_CRYPT_STRING_BASE64_ANY`. Since you have the header, you could try with 0. It might also be worth checking if there are some CR/LF issues in the PEM text just in case.
Bruno
The other difference is this: `enrollFactObj = new ActiveXObject("X509Enrollment.CX509EnrollmentWebClassFactory");`
Bruno
If I do that I need to lower the ActiveX security settings or the control isn't created. When I lower the setting, the control loads, but I get the same result as before :(
Cocowalla
Re your point about the data type parameter, I tried using different values, but got the same result.
Cocowalla
This blog http://blogs.msdn.com/b/alejacma/archive/2009/01/28/how-to-create-a-certificate-request-with-certenroll-javascript.aspx?PageIndex=3#comments also suggests to use `<object id="objCertEnrollClassFactory" classid="clsid:884e2049-217d-11da-b2a4-000e7bbb2b09"></object>` instead of instantiating the factory explicitly. Not sure if it makes a difference.
Bruno
That's what I'm already doing (thought I've tried both ways). It's one of the things you have to do to run the control without lowering security settings. The other is that it must be delivered over HTTPS.
Cocowalla
A: 

The solution was to install hotfix KB 2078942.

Note that this hotfix does not claim to fix this issue, but it does! Rather annonying, or I would have come across it much earlier :-/

I believe this is a regression bug from Vista, as there was a hotfix for Vista to fix the exact problem I was experiencing.

A thankyou and upvote goes out to Bruno for spending time yesterday trying to help me resolve this.

Cocowalla