locked
Using Client Side Certificates in Windows Metro

    Question

  • Hi,

    Do client side certificates work on windows metro? My application needs to use a client side certificate when connecting to a remote url. To this end I've tried the following approaches (which none worked)...

    1) I've used certificate extensions in app manifest (also checked shared certificates capability)

    http://msdn.microsoft.com/en-us/library/windows/apps/hh464981.aspx#certificates_extension_content

    2) I've manually imported the certificate in the application by using CertificateEnrollmentManager.ImportPfxDataAsync 

    Also, for connecting to the remote url I'm using a HttpClient with a HttpClientHandler having the  ClientCertificateOption set to automatic (as I understand, there is no way to manually set the certificate to be used).

    According to http://msdn.microsoft.com/en-us/library/windows/apps/windows.security.cryptography.certificates.certificateenrollmentmanager.installcertificateasync.aspx by ussing the CertificateEnrollmentManager, the certificates are installed in the isolated app container MY store (and therefore are /app based) and I can't see them in Microsoft Management Console so I can't even be sure that the installation went ok (even if no exception were thrown).

    If I install the certificate manually in windows (.pfx file) then I can access the url from browser but still can't access it from application.

    The error I get is :  [System.Net.WebException] = {"The underlying connection was closed: An unexpected error occurred on a send."}

    So, is there a way to make a win metro application use a client certificate when accessing an url? Supposedly  this was not possible in Windows Customer Preview but my version of windows is Release Preview.

    Can I install (from code) the certificate outside app container? (for example in User PERSONAL store) ?

    Any help is highly apreciated.

    Thank you,

    Darius

    Tuesday, June 26, 2012 2:44 PM

Answers

  • Correct,

    Think of it as two different options.  Using shared client certs the app will pick the first Client auth cert in your store and send it.  If it is a smart card you will prompted for the PIN (if it has one).  Using xhr, the system will prompt you to select the client certificate if there are multiple certs available (yes the functionality is different when using xhr).  The second method is to use InstallCertificateAsync.

    -Jeff


    Jeff Sanders (MSFT)

    Thursday, July 19, 2012 12:03 PM
  • Darius,

    If you don't want to provide a System.Net trace, you can put together a simple app to prove the client side certs are working.

    Here would be the cs code behind a simple page:

                try
                {
    
                    HttpClientHandler aHandler = new HttpClientHandler();
                    aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
    
                    HttpClient aClient = new HttpClient(aHandler);
                    HttpResponseMessage aResp = await aClient.GetAsync("https://jsanders4.northamerica.corp.microsoft.com/");
                    txtBlock.Text = aResp.StatusCode.ToString();
                }
                catch (Exception theEx)
                {
                    txtBlock.Text = theEx.Message;
                }

    and the entries you should have in your manifest:

      <Capabilities>
        <Capability Name="sharedUserCertificates" />
        <Capability Name="enterpriseAuthentication" />
        <Capability Name="privateNetworkClientServer" />
        <Capability Name="internetClient" />
      </Capabilities>
      <Extensions>
        <Extension Category="windows.certificates">
          <Certificates>
            <SelectionCriteria HardwareOnly="false" AutoSelect="true"/>
          </Certificates>
        </Extension>
      </Extensions>
    -Jeff

    Jeff Sanders (MSFT)

    Thursday, July 12, 2012 8:04 PM

All replies

  • Hi Darius,

    I'll have our certificate guy take a look at this. I'm not sure if he's in tomorrow, so it may take a couple of days.

    In the mean time, take a look at the Metro style banking app with strong authentication sample which demonstrates the Certificates extension and using the certificate API to import certs into the app container (look in enrollment.js). The sample is written in JavaScript, but the relevant calls are all into the Windows Runtime and use the same classes as you will from C#.

    --Rob

    Wednesday, June 27, 2012 1:12 AM
  • Any update on this? I still didn't managed to make this work...

    Thursday, July 05, 2012 7:43 AM
  • Hi Darius,

    Can you take a system.net trace when reproducing the problem...  following these instructions and send it to me?

    Instructions:http://blogs.msdn.com/b/jpsanders/archive/2011/12/16/how-to-take-a-system-net-trace-from-metro-style-applications-developer-preview.aspx

    Contact:  http://blogs.msdn.com/jpsanders/contact.aspx

    Thanks!


    Jeff Sanders (MSFT)

    Wednesday, July 11, 2012 7:18 PM
  • Darius,

    If you don't want to provide a System.Net trace, you can put together a simple app to prove the client side certs are working.

    Here would be the cs code behind a simple page:

                try
                {
    
                    HttpClientHandler aHandler = new HttpClientHandler();
                    aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
    
                    HttpClient aClient = new HttpClient(aHandler);
                    HttpResponseMessage aResp = await aClient.GetAsync("https://jsanders4.northamerica.corp.microsoft.com/");
                    txtBlock.Text = aResp.StatusCode.ToString();
                }
                catch (Exception theEx)
                {
                    txtBlock.Text = theEx.Message;
                }

    and the entries you should have in your manifest:

      <Capabilities>
        <Capability Name="sharedUserCertificates" />
        <Capability Name="enterpriseAuthentication" />
        <Capability Name="privateNetworkClientServer" />
        <Capability Name="internetClient" />
      </Capabilities>
      <Extensions>
        <Extension Category="windows.certificates">
          <Certificates>
            <SelectionCriteria HardwareOnly="false" AutoSelect="true"/>
          </Certificates>
        </Extension>
      </Extensions>
    -Jeff

    Jeff Sanders (MSFT)

    Thursday, July 12, 2012 8:04 PM
  • >> Also, for connecting to the remote url I'm using a HttpClient with a HttpClientHandler having

    >> the  ClientCertificateOption set to automatic (as I understand, there is no way to manually set the certificate to be used).

    Can someone confirm that this is true?  Is the only way to use client certificates to set it to automatic?

    Thursday, July 19, 2012 11:23 AM
  • Hi Adam,

    You can download and install the client certificate for your app.  The banking sample shows how to do that.  You can also select the certificate based on the issuer (I have yet to get that to work but am working on a sample with the product team for that).

    Banking sample:

    http://code.msdn.microsoft.com/windowsapps/Metro-style-banking-app-7d963c00

    -Jeff


    Jeff Sanders (MSFT)

    Thursday, July 19, 2012 11:34 AM
  • Ok, I think I'm understanding this more but it's still pretty confusing.  In a normal smart card authentication the certificates are already on your machine (in Internet Options > Content > Certificates > Personal).  Are you saying that "downloading and installing" the client certificate makes it available to my Metro application even if it was already in the Personal certificate store?  As in, even if it is already on my machine I am able to "choose" the certificate for the user by finding the 1 certificate I want them to use in Personal store then calling InstallCertificateAsync using that data and then xhr or HttpClient will magically start using it?  That seems to be what the banking application does but the documentation is so sparse that I can't tell.

    Thursday, July 19, 2012 11:59 AM
  • Correct,

    Think of it as two different options.  Using shared client certs the app will pick the first Client auth cert in your store and send it.  If it is a smart card you will prompted for the PIN (if it has one).  Using xhr, the system will prompt you to select the client certificate if there are multiple certs available (yes the functionality is different when using xhr).  The second method is to use InstallCertificateAsync.

    -Jeff


    Jeff Sanders (MSFT)

    Thursday, July 19, 2012 12:03 PM
  • Cool, thanks!  I confirmed that both XmlHttpRequest (in a Javascript app) and IXMLHTTPRequest2 (in a C++ app) prompt you to select the client certificate if there are multiple certs available.  So if the certificate is already in the store (e.g. the smart card middleware copied the cert from the card to the Personal certificate store) I didn't need to install anything in my code.  I just needed to make sure I had the "Shared User-Certificates" capability in each app.
    Thursday, July 19, 2012 6:48 PM
  • Hi!

    Thanks to this thread I've managed to get my solution to work with a xhr call, but I get two dialogs. The first one asks me for the certificate if there are many user certs available for the site. The second one is asking me for the permission for using the crypto key, it does not show info on which key it is using. When the user has chosen a certificate the message is clear enough, but sometimes the certificate is automatically selected by the system (one there's only one option) and the only permission the user gets is this one:

    Should I fill anything into the xhr call to provide info for this dialog?

    Thank you,

    Juanma


    Juan Manuel Servera
    twitter: @jmservera
    mi blog: http://jmservera.wordpress.com
    Únete al grupo de WP7 en LinkedIn
    MCPD WP7 Developer - MCTS Sharepoint 2010 Application Development

    Sunday, July 22, 2012 9:51 PM
  • Hi Juan,

    No you do not fill in any additional info.  You will get this prompt once for the app so the user knows the key is being used once.

    -Jeff


    Jeff Sanders (MSFT)

    Monday, July 23, 2012 11:44 AM
  • Darius,

    If you don't want to provide a System.Net trace, you can put together a simple app to prove the client side certs are working.

    Here would be the cs code behind a simple page:

                try
                {
    
                    HttpClientHandler aHandler = new HttpClientHandler();
                    aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
    
                    HttpClient aClient = new HttpClient(aHandler);
                    HttpResponseMessage aResp = await aClient.GetAsync("https://jsanders4.northamerica.corp.microsoft.com/");
                    txtBlock.Text = aResp.StatusCode.ToString();
                }
                catch (Exception theEx)
                {
                    txtBlock.Text = theEx.Message;
                }

    and the entries you should have in your manifest:

      <Capabilities>
        <Capability Name="sharedUserCertificates" />
        <Capability Name="enterpriseAuthentication" />
        <Capability Name="privateNetworkClientServer" />
        <Capability Name="internetClient" />
      </Capabilities>
      <Extensions>
        <Extension Category="windows.certificates">
          <Certificates>
            <SelectionCriteria HardwareOnly="false" AutoSelect="true"/>
          </Certificates>
        </Extension>
      </Extensions>
    -Jeff

    Jeff Sanders (MSFT)

    Unfortunately I'm not able to get it working. I followed the same steps but still getting a 403 error. I'm first installing the certificate using "ImportPfxDataAsync" function and I can see the certificate file being created in my local store ("My") [the name of the file is the certificate thumbprint and for some reason the extension of that file is "sys"].

    This is the code for installing the certificate:

    Task t = Task.Factory.StartNew(() => CertificateEnrollmentManager.ImportPfxDataAsync(certificateData, "test", ExportOption.Exportable, KeyProtectionLevel.NoConsent, InstallOptions.None,
                    "My Test Certificate").AsTask());
                t.Wait();

    and this is the code for sending the request:

            private async Task LetTheFunBegin()
            {
                try
                {
                    HttpClientHandler aHandler = new HttpClientHandler();
                    aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
    
                    HttpClient aClient = new HttpClient(aHandler);
                    HttpResponseMessage aResp = await aClient.GetAsync("https://myuri.com");
    
                    string a = await aResp.Content.ReadAsStringAsync();
                    var abc = aResp.StatusCode.ToString();
                }
                catch (Exception theEx)
                {
                    var def = theEx.Message;
                }
            }

    The manifest file looks like following:

    <?xml version="1.0" encoding="utf-8"?>
    <Package xmlns="http://schemas.microsoft.com/appx/2010/manifest">
    ....
      <Capabilities>
        <Capability Name="sharedUserCertificates" />
        <Capability Name="enterpriseAuthentication" />
        <Capability Name="privateNetworkClientServer" />
        <Capability Name="internetClient" />
      </Capabilities>
      <Extensions>
        <Extension Category="windows.certificates">
          <Certificates>
            <SelectionCriteria HardwareOnly="false" AutoSelect="true"/>
          </Certificates>
        </Extension>
      </Extensions>
    </Package>

    However I am always getting 403 error. It seems the certificate is not being picked up at all.

    Any inputs will be highly appreciated.

    Thanks

    Gaurav

    Wednesday, September 05, 2012 8:54 PM
  • Hi Gaurav,

    You have to ensure you client cert has the Client Auth OID as well or it will not be picked up.

    -Jeff


    Jeff Sanders (MSFT)

    Thursday, September 06, 2012 11:45 AM
  • Here is what your cert should have:


    Jeff Sanders (MSFT)

    Thursday, September 06, 2012 12:09 PM
  • Thanks Jeff. I had 2 certificates and after this I checked and found out the one I have been using does not have this property. The other one has this property and when I use that, I am able to run my application successfully.

    Now I have another problem. When I wrote my 1st question, I was building the application in XAML/C#. This morning I tried to build the same application using HTML 5/JS. The weird issue I am having is that with the correct certificate, the application built using HTML 5/JS works perfectly fine but I am still getting 403 error in the application built using XAML/C#.

    This is my code for XAML/C# application which is failing:

                    await CertificateEnrollmentManager.ImportPfxDataAsync(certificateData2, "", ExportOption.Exportable, KeyProtectionLevel.NoConsent, InstallOptions.None, "My Test Certificate 3");
                    HttpClientHandler aHandler = new HttpClientHandler();
                    
                    aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
                    
                    HttpClient aClient = new HttpClient(aHandler);
                    aClient.DefaultRequestHeaders.Add("x-ms-version", "2012-03-01");
                    
                    HttpResponseMessage aResp = await aClient.GetAsync("https://myurl.com");
                    
                    string a = await aResp.Content.ReadAsStringAsync();
                    var abc = aResp.StatusCode.ToString();
    

    and this is my code for HTML 5/JS which is working:

                Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager.importPfxDataAsync(certificateData2, "", Windows.Security.Cryptography.Certificates.ExportOption.exportable,
        Windows.Security.Cryptography.Certificates.KeyProtectionLevel.noConsent, Windows.Security.Cryptography.Certificates.InstallOptions.none, "My Test Certificate 1").done(
                             function () {
                                 var uri = "https://myurl.com";
                                 WinJS.xhr({ url: uri, headers: { "x-ms-version": "2012-03-01" } })
                                     .done(
                                        function (/*@override*/ request) {
                                            var responseData = request.responseText;
                                        },
                                        function (error) {
                                            var abc = error.message;
                                        });
                             }
                    );
    

    I'm absolutely clueless as to why this is happening!!

    Thanks again for all your help.

    Thursday, September 06, 2012 1:15 PM
  • As an aside, are these certificates static?  If so, you should include them in the app package.

    The code is different between your .net and javascript.  You are using different options.

    Finally, a system.net trace should tell you if a client certificate is found and being sent for your .NET problem.  Also make sure you don't use 'Shared Client Certificates' in the manifest since you are installing it in the application's My store.

    -Jeff


    Jeff Sanders (MSFT)

    Thursday, September 06, 2012 1:41 PM
  • Thanks Jeff. The certificates are not static. A user should be able to specify these certificates. Can you tell me the difference in options you found in my code? To me I'm using same options (Exportable, NoConsent, and None).

    Good tip on Shared Client Certificates!! I see that it is defined in my manifest file for XAML/C# application. Let me remove it and give it a try. Will revert back soon.

    Regards

    Gaurav

    Thursday, September 06, 2012 1:48 PM
  • Hi Jeff,

    So I commented "Shared Client Certificates" capability in both applications (It was there in HTML 5/JS app as well). Still no luck with XAML/C# application. It still gives me 403 error. I even played with <Extensions> (commenting/uncommenting) but that did not make any difference in the app.

    <Capabilities>
        <!--<Capability Name="sharedUserCertificates" />-->
        <Capability Name="enterpriseAuthentication" />
        <Capability Name="privateNetworkClientServer" />
        <Capability Name="internetClient" />
      </Capabilities>
      <!--<Extensions>
        <Extension Category="windows.certificates">
          <Certificates>
            <SelectionCriteria HardwareOnly="false" AutoSelect="true"/>
          </Certificates>
        </Extension>
      </Extensions>-->

    Thanks

    Gaurav

     

    Thursday, September 06, 2012 3:04 PM
  • Jeff,

    I was experiencing the same issue, reached out to Gaurav, and he pointed me here. 

    After generating a client side certificate with the correct OID for client side authentication, I found I was able to connect from the javascript app, but not a reference winmd assembly that I wrote in C#.  The interesting thing I found is that when I actually made the call to install the pfx from within the winmd C# assembly, then it worked from the C# side as well.  Is the certificate store that is scoped to the application restricted or scoped by assembly?

    On a side note, I'm curious as to why the OID for client side authentication would be needed.  I don't believe the management certificates generated by Visual Studio are client auth certs, and at the very least this requirement should be documented somewhere. 


    Harin

    Thursday, September 06, 2012 4:51 PM
  • Hello Harin,

    The cert store is per Application.

    The OID requirement is due to a problem on our side.  There is a bug filed on this and to update the documentation temporarily!

    -Jeff


    Jeff Sanders (MSFT)

    Thursday, September 06, 2012 6:08 PM
  • Hi Jeff,

    is there any ETA on the OID bug fix?

    We're currently writing a Win8 App that makes use of the Azure Management API and it adds a bit of a barrier to entry for users.

    Many thanks,

    Howard


    http://blogs.conchango.com/howardvanrooijen

    Tuesday, October 16, 2012 2:17 PM
  • Hi Howard,

    There is no ETA on the fix.  Sorry!

    -Jeff


    Jeff Sanders (MSFT)

    Tuesday, October 16, 2012 2:20 PM
  • #sadtrombone :(

    Is there a connect issue open that I could use to track progress?

    Many thanks,

    Howard


    http://blogs.conchango.com/howardvanrooijen

    Sunday, October 21, 2012 8:23 PM
  • Hi Howard,

    Sorry, there is no external way for you to track this.

    -Jeff


    Jeff Sanders (MSFT)

    Monday, October 22, 2012 12:02 PM
  • Hello Harin,

    The cert store is per Application.

    The OID requirement is due to a problem on our side.  There is a bug filed on this and to update the documentation temporarily!

    -Jeff


    Jeff Sanders (MSFT)

    Hi Jeff,

    I have had many of the same problems as others here and I have now encountered a new sticking point.

    Does this OID requirement extend to servers as well? I have succeeded in setting up a client cert and can select it in my GUI, but the server logs say the client is cancelling the SSL handshake immediately after the server certificate is sent to the client and before the server receives the client cert.

    I guess this is a server problem, and I have the servers hierarchy in my trusted root CA list. Could the client close the connection because the server cert is missing "Server Authentication" in enhanced key usage?

    [Edit]

    Going through Internet Explorer I was guided a bit more to add the servers root CA and server cert to my cert store as trusted certs. This improved matters a bit, and I can now see that IE reports a problem because the address and the cert don't match. This is not a problem in IE because it gives me the option to continue, but no such option appears in my Metro App. Is there a way to add this option, or if not can I, like in Wininet, allow my App to inherit IE internet settings and use IEs trusted sites settings to skip this issue?

    I was thinking that an IXMLHTTPRequest2Callback object would allow me the option to handle this step, but no such luck. And setting the XHR object property XHR_PROP_NO_AUTH to TRUE has had no effect. 


    • Edited by Warren Gavin Wednesday, October 24, 2012 1:53 PM More detail
    Wednesday, October 24, 2012 9:55 AM
  • Hi Gaurav,

    Could you confirm if you or anyone in this thread were able to make successful API calls with the client cert imported via ImportPfxDataAsync in c# ?

    I tried all the methods and mentioned above and still couldn't get past 403. Any help is much appreciated.

    Interesting thing is when I ran fiddler and placed the client cert for fiddler to pick it up, the call was successful. However without fiddler the same code doesn't work and results in 403 forbidden. I have enabled system.net tracing in machine.config but nothing shows up there.

    C# Code:

     await CertificateEnrollmentManager.ImportPfxDataAsync(pfxContent, "",
                                                        ExportOption.Exportable, KeyProtectionLevel.NoConsent, InstallOptions.None,
                    "Client Auth");
                // Following succeeds
                var r2 = await CertificateEnrollmentManager.CreateRequestAsync(new CertificateRequestProperties() { FriendlyName = "Client Auth", Exportable = ExportOption.Exportable });
                var uri = new Uri("https://myServiceUrl");
                try
                {
                    HttpClientHandler httpClientHandler = new HttpClientHandler();
                    httpClientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
                    httpClientHandler.UseProxy = false;
                    HttpClient httpClient = new HttpClient(httpClientHandler);
                    httpClient.DefaultRequestHeaders.Add("x-ms-version", "2010-10-28");
                    HttpResponseMessage response = await httpClient.GetAsync(uri);
                    string content = await response.Content.ReadAsStringAsync();
                    var statusCode = response.StatusCode.ToString();
                }
                catch (Exception ex)
                {
                    
                }

    manifest:

     <Capabilities>
        <Capability Name="documentsLibrary" ></Capability>
        <!--<Capability Name="sharedUserCertificates" />-->
        <Capability Name="internetClient" />
      </Capabilities>
    <!--
    <Extensions>
        <Extension Category="windows.certificates">
          <Certificates>
           <SelectionCriteria AutoSelect="true" />
          </Certificates>
        </Extension>
      </Extensions>
    -->

    Thanks,

    Ravi chandhiran (MSFT)

    Wednesday, October 31, 2012 6:13 PM
  • Hi Ravi,

    I was able to do so. Please take a look at this blog post I wrote about the same: http://gauravmantri.com/2012/09/08/consuming-windows-azure-service-management-api-in-a-windows-8-application/

    Hope this helps.

    Wednesday, October 31, 2012 6:38 PM
  • Hi Ravi,

    I was able to do so. Please take a look at this blog post I wrote about the same: http://gauravmantri.com/2012/09/08/consuming-windows-azure-service-management-api-in-a-windows-8-application/

    Hope this helps.

    Gaurav, this is an awesome detailed blog post. The key being that you so correctly had the insight to mention the exact makecert command that worked for you - a great example of blogging it right. It worked for me from C# by having that right cert. Keep up the good work!

    Wednesday, October 31, 2012 7:32 PM
  • Hello,

    How can I clear application state in order to the system (C++ IXMLHttpRequest) prompt user to select the client certificate if there are multiple certs available in store again (without relaunching application)?

    Thanks.
    • Edited by k Mike Monday, May 06, 2013 5:20 PM
    Monday, May 06, 2013 5:16 PM
  • Please create a new thread in the appropriate forum.  I'm locking this post.

    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Monday, May 06, 2013 5:40 PM