Search code examples
windows-phone-8.1certificatecordova-pluginssmartcardinappbrowser

Cordova InAppBrowser accessing certificate on virtual smartcard


I have an app running on Windows Phone 8.1 which calls a URL via InAppBrowser plugin. This URL is supposed to ask for the user certificate stored on a virtual smartcard on the phone. When I call the URL via Internet Explorer, I am asked for my PIN to unlock the virtual smartcard but in the InAppBrowser, this doesn't work. No PIN prompt, nothing. Iterating through the Certificates yielded from

IReadOnlyList<Certificate> certStores = await  CertificateStores.FindAllAsync();

I can see the certificate at app runtime but InAppBrowser doesn't seem to query for them. Do I have to copy its reference to another certificate store or is InAppBrowser not capable of establishing SSL with user certificates ?


Solution

  • The issue is with the webview component, x-ms-webview to be more precisely. InAppBrowser plugin uses this component internally. Found a workaround mentioned here, it kinda sounds like a security issue tbh so this could get fixed in the future but here are more details on said workaround:

    Make a request to the URL which is supposed to trigger virtual smartcard unlock to access the user certificate, but with the HttpClient at native level (C#)

    I've created another Windows Runtime Component in my solution which does a simple POST to the url I want to access from InAppBrowser later on. While setting up the Windows.Web.Http.HttpClient, I fetch the user certificate from the smartcard and set it as HttpBaseProtocolFilter.ClientCertificate.

    public sealed class SSLHelper
    {
    
        private static String errorMessage = "";
        private static String statusMessage = "";
    
        public static IAsyncOperation<Boolean> establishSSLConnection(String url)
        {
            return connect(url).AsAsyncOperation<Boolean>();
        }
    
        public static String getErrorMessage()
        {
            return SSLHelper.errorMessage;
        }
    
        public static String getStatusMessage()
        {
            return SSLHelper.statusMessage;
        }
    
        private static async Task<Boolean> connect(String urlString)
        {
    
            Certificate clientCert = await getCertificateAsync();
    
            HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
            filter.ClientCertificate = clientCert;
            HttpClient client = new HttpClient(filter);
    
            try
            {
                System.Uri url = new System.Uri(urlString);
                HttpResponseMessage response = await client.PostAsync(url, new HttpStringContent(""));
                response.EnsureSuccessStatusCode();
                SSLHelper.statusMessage = response.StatusCode.ToString();
                return true;
            }
            catch (Exception e)
            {
                SSLHelper.errorMessage = e.ToString();
                return false;
            }
        }
    
    
        private static async Task<Certificate> getCertificateAsync()
        {
    
            CertificateQuery query = new CertificateQuery();
            query.IssuerName = "Sample Issuer";
    
            IReadOnlyList<Certificate> certStores = await CertificateStores.FindAllAsync(query);
    
            return certStores.FirstOrDefault<Certificate>();
        }
    
    }
    

    Make that code return as a promise on Javascript level and once it resolves, start the code which uses InAppBrowser to access the secure URL again. The native request causes the PIN prompt for virtual smartcard access, once you have entered the correct PIN, InAppBrowser / WebView can magically establish the connection.