forderud / WindowsClientAuth

Windows examples of certificates for client authentication in web and TLS socket scenarios
MIT License
3 stars 0 forks source link

Examples of how to use certificates for client authentication in web and TLS socket scenarios. The examples are geared towards Windows with trusted certificate storage, but all principles also apply to other operating systems.

Getting started instructions

Prerequisites:

Generate certificates for testing (optional)

Test certificates are already added to the TestCertificates folder. These can be re-generated by running powershell .\GenerateCertificates.ps1 from the TestCertificates subfolder. Please note that this script will leave certificate "residue" in the "Current User\Personal" certificate store.

Install root certificate (optional)

This step will remove the "Not secure" warning when testing from a web browser.

Install the root certificate:

The root certificate will now show up in the Windows "Manage computer certificates" window:

CertMgr Root

Install client certificate

This step will enable the web browser to use the client certificate for authentication against the server.

Install the client certificate:

The client certificate will now show up in the Windows "Manage user certificates" window:

CertMgr Client

It will also show up in the web browser certificate dialogs:

Browser Cert Install

TPM storage of private key

Installed certificates will by default have their private key managed by the SW-based "Microsoft Software Key Storage Provider" when importing non-exportable. It's also possible to store the private key in the TPM chip for enhanced HW-enforced security.

This should in principle by possible with certutil -user -csp TPM -p "" -importpfx ClientCert.pfx NoExport. However, that doesn't seem to work as expected, and instead leads to a NTE_INVALID_PARAMETER error. This appears to be a known issue, and one can use the TPMImport tool as work-around. The certificate can then be imported to the TPM with TPMImport.exe -user -v ClientCert.pfx "".

One can verify the actual key storage with certutil -user -store My ClientCert. You'll then get Provider = Microsoft Platform Crypto Provider if the private key is actually stored in the TPM.

Client authentication

The clientAuth OID (1.3.6.1.5.5.7.3.2) EKU field in the client certificate enables it to be used for client authentication. Double-click on WebServer.py to start the test web server to be used for testing of client authentication.

Testing from web browser

Steps:

Browser Cert Select

Browser Webpage

Programmatic HTTP communication

Language Secure certificate store support HTTP API(s) Sample code Limitations
C++ Win32 CNG WinHTTP, WinINet & MSXML6 See WebClientCpp Unable to access certificates in "Local Computer\Personal" store with MSXML6.
C++/C# UWP CertificateStores See WebClientUwp Unable to access certificates in "Local Computer\Personal" store
C#/.Net X509Store See WebClientNet None discovered
Java Leveraging Security in the Native Platform Using Java .. Not yet tested TBD
Python No known support (see #10) See WebClientPy (file-based certificate handling) Unable to use certificate store for mTLS. Might need to use PyPAC for PAC proxy support.

All the language samples are command-line applications that tries to authenticate against https://localhost:443/ using the client certificate. The applications can be run without any arguments and will output the following on success:

Client certificate: ClientCert

<html><head><title>Client certificate authentication test</title></head>
<body>
<p>Request path: /</p>
<p>Successfully validated <b>client certificate</b>: (commonName: ClientCert), issued by (commonName: TestRootCertificate).</p>
</body></html>

Programmatic TLS socket communication

Client certificates can also be used for authentication when using "raw" TLS/SSL sockets directly. However, it's then important that the underlying socket library is based on schannel with Winsock underneath, and not on OpenSSL. Direct Winsock usage might also work, but that remain to be investigated.

The reason for OpenSSL not being supported, is that OpenSSL is unable to access private keys through the CNG API. There does exist a openssl-cng-engine project that seeks to address this gap, but client autentication doesn't appear to be supported yet.

Proxy settings

The above API alternatives will automatically utilize the Windows proxy settings for the currently logged in user.

Proxy settings can either be configured from the "Windows Settings" -> "Proxy" UI, through the Netsh winhttp set advproxy command, or by directly setting Internet Settings registry values (AutoConfigURL example).

How to inspect proxy settings (if running Win11):

> netsh winhttp show advproxy

Current WinHTTP advanced proxy settings:

{
        "Proxy":         "http://proxy.mycompany.com:8080",
        "ProxyBypass":   "mycompany.com",
        "AutoconfigUrl": "https://mycompany.com/pac.pac",
        "AutoDetect":    true
}

It's usually not a good idea to combine Proxy & ProxyBypass settings with AutoconfigUrl as shown above, since the settings would undermine each other. The same settings are also found in the HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings registry folder (use regedit.exe to view them), which also works on Win10.

Most SW (including .Net runtime and Chrome/Chromium) appear to be using WinHttpGetProxyForUrl to determine which proxy server to use for a given HTTP request. This simplifies networking code, since the application doesn't need to parse proxy settings directly. Python urllib documents that proxy settings are automatically picked up from Windows registry without specifying the exact mechanism.

Code signing

The codeSigning OID (1.3.6.1.5.5.7.3.3) EKU field in the client certificate enables it to be used for code signing.

How to sign a binary: