{"id":25416383,"url":"https://github.com/forderud/windowsclientauth","last_synced_at":"2025-10-25T20:33:52.078Z","repository":{"id":59298752,"uuid":"521425677","full_name":"forderud/WindowsClientAuth","owner":"forderud","description":"Windows examples of certificates for client authentication in web and TLS socket scenarios","archived":false,"fork":false,"pushed_at":"2024-08-20T10:53:10.000Z","size":393,"stargazers_count":3,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-08-20T14:05:57.402Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/forderud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-04T21:53:21.000Z","updated_at":"2024-08-20T10:53:13.000Z","dependencies_parsed_at":"2024-08-07T17:12:35.198Z","dependency_job_id":"31e6b7be-e42e-4a22-a6cb-7b2431205657","html_url":"https://github.com/forderud/WindowsClientAuth","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forderud%2FWindowsClientAuth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forderud%2FWindowsClientAuth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forderud%2FWindowsClientAuth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forderud%2FWindowsClientAuth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/forderud","download_url":"https://codeload.github.com/forderud/WindowsClientAuth/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239162792,"owners_count":19592343,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-16T16:54:53.428Z","updated_at":"2025-10-25T20:33:52.072Z","avatar_url":"https://github.com/forderud.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"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.\n\n\n## Getting started instructions\n\nPrerequisites:\n* Windows computer\n* [Visual Studio](https://visualstudio.microsoft.com/) - for buiding the C++ and C# sample projects\n* [Git](https://git-scm.com/) for Windows - for OpenSSL that's required for the Python projects\n* [Python](https://www.python.org/) - to run the test web server\n\n### Generate certificates for testing (optional)\nTest 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.\n\n\n### Install root certificate (optional)\nThis step will remove the \"Not secure\" warning when testing from a web browser.\n\n\nInstall the root certificate:\n* Either: From an admin command prompt: `certutil –addstore –f \"root\" TestRootCertificate.cer`\n* Or: Double-click on `TestRootCertificate.cer`, select \"Install Certificate\", select \"Local Machine\" as store location, then \"Trusted Root Certificate Authorities\" as certificate store.\n\nThe root certificate will now show up in the Windows \"Manage computer certificates\" window:\n\n![CertMgr Root](figures/CertMgrRoot.png) \n\n\n### Install client certificate\nThis step will enable the web browser to use the client certificate for authentication against the server.\n\nInstall the client certificate:\n* Either: From a command prompt: `certutil -user –importpfx ClientCert.pfx NoRoot,NoExport` (password \"1234\")\n* Or: Double-click on `ClientCert.pfx`, select \"Install Certificate\", select \"Current User\" as store location, enable \"Protect private key... (Non-exportable)\", then install with default settings.\n* Or: From the web browser \"Manage certificates\" menu: Import `ClientCert.pfx` into \"Personal\" certificate store with default settings.\n\nThe client certificate will now show up in the Windows \"Manage user certificates\" window:\n\n![CertMgr Client](figures/CertMgrClient.png) \n\nIt will also show up in the web browser certificate dialogs:\n\n![Browser Cert Install](figures/BrowserCertInstall.png) \n\n\n### TPM system check\nRun `tpm.msc` to open the \"Trusted Platform Module (TPM) Management\" snap-in to view TPM details for the current system:  \n![image](https://github.com/user-attachments/assets/296aec64-d1da-4d38-948e-1631132c5af4)\n\n\n### TPM storage of private keys\nInstalled 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 through \"_Microsoft Platform Crypto Provider_\" for enhanced HW-enforced security.\n\nRSA keys can be imported to the TPM with `certutil [-user] -csp TPM -p \"\" -importpfx ClientCert.pfx NoExport`. However, that seem to fail with `NTE_BAD_TYPE` for EC-DSA certificates. It's possible to use [TPMImport](https://github.com/glueckkanja-pki/TPMImport) as work-around with a `TPMImport.exe [-user] -v ClientCert.pfx \"\"` command.\n\nKey storage can be verified with `certutil [-user] -store My`. Provider `Microsoft Platform Crypto Provider` verifies that the private key is actually stored in the TPM.\n\n### TPM for hardware identification\nThe [TpmIdentifier](/TpmIdentifier) project demonstrates how the TPM endorsement key (`EKpub`) can be used to uniquely identify a computer.\n\n## Client authentication\nThe `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.\nDouble-click on `WebServer.py` to start the test web server to be used for testing of client authentication.\n\n### Testing from web browser\nSteps:\n* Open https://localhost:443/ in a web browser.\n* Select `ClientCert` in the certificate selection menu.\n\n![Browser Cert Select](figures/BrowserCertSelect.png)\n\n* Observe that the selected certificate is listed in the generated webpage.\n\n![Browser Webpage](figures/BrowserWebpage.png)\n\n### Programmatic HTTP communication\n\n\u003ctable\u003e\n    \u003cthead\u003e\n        \u003ctr\u003e\n            \u003cth\u003eLanguage\u003c/th\u003e\n            \u003cth\u003eSecure certificate store support\u003c/th\u003e\n            \u003cth\u003eHTTP API(s)\u003c/th\u003e\n            \u003cth\u003eSample code\u003c/th\u003e\n            \u003cth\u003eLimitations\u003c/th\u003e\n        \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n        \u003ctr\u003e\n            \u003ctd rowspan=3\u003eC++ Win32\u003c/td\u003e\n            \u003ctd rowspan=3\u003e\u003ca href=\"https://learn.microsoft.com/en-us/windows/win32/seccng/cng-portal\"\u003eCNG\u003c/a\u003e\u003c/td\u003e\n            \u003ctd\u003e\u003ca href=\"https://learn.microsoft.com/en-us/windows/win32/winhttp/iwinhttprequest-interface\"\u003eWinHTTP\u003c/a\u003e\u003c/td\u003e\n            \u003ctd rowspan=3\u003eSee \u003ca href=\"WebClientCpp/\"\u003eWebClientCpp\u003c/a\u003e\u003c/td\u003e\n            \u003ctd\u003eNone discovered\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003c!-- \u003ctd\u003e\u003c/td\u003e --\u003e\n            \u003c!-- \u003ctd\u003e\u003c/td\u003e --\u003e\n            \u003ctd\u003e\u003ca href=\"https://learn.microsoft.com/en-us/windows/win32/wininet/portal\"\u003eWinINet\u003c/a\u003e\u003c/td\u003e\n            \u003c!-- \u003ctd\u003e\u003c/td\u003e --\u003e\n            \u003ctd\u003eNone discovered\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003c!-- \u003ctd\u003e\u003c/td\u003e --\u003e\n            \u003c!-- \u003ctd\u003e\u003c/td\u003e --\u003e\n            \u003ctd\u003e\u003ca href=\"https://learn.microsoft.com/en-us/windows/win32/api/msxml6/\"\u003eMSXML6\u003c/a\u003e\u003c/td\u003e\n            \u003c!-- \u003ctd\u003e\u003c/td\u003e --\u003e\n            \u003ctd\u003eUnable to access certificates in \u003ca href=\"https://stackoverflow.com/a/38779903/3267386\"\u003e\"Local Computer\\Personal\" store\u003c/a\u003e\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd\u003eC++/C# UWP\u003c/td\u003e\n            \u003ctd\u003e\u003ca href=\"https://learn.microsoft.com/en-us/uwp/api/windows.security.cryptography.certificates.certificatestores\"\u003eCertificateStores\u003c/a\u003e\u003c/td\u003e\n            \u003ctd\u003e\u003c/td\u003e\n            \u003ctd\u003eSee \u003ca href=\"WebClientUwp/\"\u003eWebClientUwp\u003c/a\u003e\u003c/td\u003e\n            \u003ctd\u003eUnable to access certificates in \u003ca href=\"https://github.com/MicrosoftDocs/winrt-api/issues/2288\"\u003e\"Local Computer\\Personal\" store\u003c/a\u003e\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd\u003eC#/.Net\u003c/td\u003e\n            \u003ctd\u003e\u003ca href=\"https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509store\"\u003eX509Store\u003c/a\u003e\u003c/td\u003e\n            \u003ctd\u003e\u003c/td\u003e\n            \u003ctd\u003eSee \u003ca href=\"WebClientNet/\"\u003eWebClientNet\u003c/a\u003e\u003c/td\u003e\n            \u003ctd\u003eNone discovered\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd\u003eJava\u003c/td\u003e\n            \u003ctd\u003e\u003ca href=\"https://www.oracle.com/technical-resources/articles/javase/security.html\"\u003eLeveraging Security in the Native Platform Using Java ..\u003c/a\u003e\u003c/td\u003e\n            \u003ctd\u003e\u003c/td\u003e\n            \u003ctd\u003eNot yet tested\u003c/td\u003e\n            \u003ctd\u003eTBD\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd\u003ePython\u003c/td\u003e\n            \u003ctd\u003eNo known support (see \u003ca href=\"../../issues/10\"\u003e#10\u003c/a\u003e)\u003c/td\u003e\n            \u003ctd\u003e\u003c/td\u003e\n            \u003ctd\u003eSee \u003ca href=\"WebClientPy/\"\u003eWebClientPy\u003c/a\u003e (file-based certificate handling)\u003c/td\u003e\n            \u003ctd\u003e\u003ca href=\"https://github.com/sethmlarson/truststore/issues/78\"\u003eUnable to use certificate store for mTLS\u003c/a\u003e. \u003ci\u003eMight\u003c/i\u003e need to use \u003ca href=\"https://github.com/carsonyl/pypac\"\u003ePyPAC\u003c/a\u003e for PAC proxy support.\u003c/td\u003e\n        \u003c/tr\u003e\n    \u003c/tbody\u003e\n\u003c/table\u003e\n\nAll 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:\n```\nClient certificate: ClientCert\n\n\u003chtml\u003e\u003chead\u003e\u003ctitle\u003eClient certificate authentication test\u003c/title\u003e\u003c/head\u003e\n\u003cbody\u003e\n\u003cp\u003eRequest path: /\u003c/p\u003e\n\u003cp\u003eSuccessfully validated \u003cb\u003eclient certificate\u003c/b\u003e: (commonName: ClientCert), issued by (commonName: TestRootCertificate).\u003c/p\u003e\n\u003c/body\u003e\u003c/html\u003e\n```\n\n### Programmatic TLS socket communication\nClient 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](https://learn.microsoft.com/en-us/windows/win32/secauthn/performing-authentication-using-schannel) with Winsock underneath, and _not_ on OpenSSL. Direct [Winsock](https://learn.microsoft.com/en-us/windows/win32/winsock/secure-winsock-programming) usage _might_ also work, but that remain to be investigated.\n\nThe reason for OpenSSL _not_ being supported, is that OpenSSL is unable to access private keys through the [CNG](https://learn.microsoft.com/en-us/windows/win32/seccng/cng-portal) 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](https://github.com/rticommunity/openssl-cng-engine/issues/46).\n\n## Proxy settings\nThe above API alternatives will automatically utilize Windows proxy settings.\n\n#### Alternatives for configuring proxy settings\n1. From the \"Windows Settings\" -\u003e \"Proxy\" UI (only for per-user proxy settings)\n1. Through [`netsh winhttp set advproxy`](https://learn.microsoft.com/en-us/windows/win32/winhttp/netsh-exe-commands#set-advproxy) that was introduced in Windows 11\n1. Through [WinINet](https://learn.microsoft.com/en-us/windows/win32/wininet/about-wininet) which also works on Windows 10.\n\nThe [`ProxySettingsPerUser=0`](https://learn.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-ie-clientnetworkprotocolimplementation-hklmproxyserver) registry key can be configured to make proxy settings system-wide, so that it's applied to all user accounts including background services. This will have the side-effect of making the Windows proxy UI read-only. Proxy changes then instead needs to be done programatically from a process with admin privileges.  \n![image](https://github.com/user-attachments/assets/376eb228-144f-44cd-be42-49ca444666e1)  \n\nSystem-wide proxy settings will be stored in the `HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings` registry folder.\n\n### Proxy configuration on Windows 11\nSystem-wide proxy configuration:\n```\necho { \"Proxy\":\"\", \"ProxyBypass\":\"\", \"AutoconfigUrl\":\"https://mycompany.com/pac.pac\", \"AutoDetect\":true} \u003e proxy-settings.json\nnetsh winhttp set advproxy setting-scope=machine settings-file=proxy-settings.json\n```\n\nShow proxy settings: `netsh winhttp show advproxy`\n```\nCurrent WinHTTP advanced proxy settings:\n{\n        \"ProxyIsEnabled\":       false,\n        \"AutoConfigIsEnabled\":  true,\n        \"AutoconfigUrl\":        \"https://mycompany.com/pac.pac\",\n        \"AutoDetect\":   true,\n        \"PerUserProxySettings\": false\n}\n```\n\n### Proxy configuration on Windows 10\nThere's unfortunately no feature-complete command-line tool for proxy configuration included with Windows 10. However, there's a [Configure proxy settings for Azure Stack HCI](https://learn.microsoft.com/en-us/azure/azure-local/manage/configure-proxy-settings) document that points to a `WinInetProxy` script with sample code.\n\nThe `ProxyConfig` project in this repo is based on the WinInetProxy sample, and can be used to configure proxy settings from the command-line on Windows 10 machines without netsh winhttp set advproxy. This project uses the WinINet [`InternetSetOption`](https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetsetoptionw) function to configure `INTERNET_PER_CONN_PROXY_SERVER`, `INTERNET_PER_CONN_PROXY_BYPASS` and `INTERNET_PER_CONN_AUTOCONFIG_URL` settings. It can also set the `ProxySettingsPerUser=0` registry key to make proxy changes system-wide.\n\nSystem-wide AutoConfigURL proxy configuration:\n```\nProxyConfig.exe scope machine\nProxyConfig.exe autoproxy https://mycompany.com/pac.pac\n```\n\nSystem-wide proxy server and bypass list configuration:\n```\nProxyConfig.exe scope machine\nProxyConfig.exe setproxy proxy.mycompany.com:8080 *.mycompany.com\n```\n\nHow to switch back to default proxy settings:\n```\nProxyConfig.exe clear\nProxyConfig.exe scope default\n```\n\nNote: `netsh winhttp set proxy` can unfortunately not be used for this, since it doesn't update proxy settings for the current user. It also lacks AutoConfigURL support.\n\n### Proxy usage\nMost SW (including  [.Net runtime](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/WinInetProxyHelper.cs) and [Chrome/Chromium](https://github.com/chromium/chromium/blob/main/components/winhttp/proxy_configuration.cc)) automatically use [WinHttpGetIEProxyConfigForCurrentUser](https://learn.microsoft.com/en-us/windows/win32/api/winhttp/nf-winhttp-winhttpgetieproxyconfigforcurrentuser) together with [WinHttpGetProxyForUrl](https://learn.microsoft.com/en-us/windows/win32/api/winhttp/nf-winhttp-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](https://docs.python.org/3/library/urllib.request.html#urllib.request.getproxies) without specifying the exact mechanism.\n\n\n## Code signing\nThe `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.\n\nHow to sign a binary:\n* From a developer command prompt, run `signtool sign /a \u003cFileName\u003e.exe`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforderud%2Fwindowsclientauth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fforderud%2Fwindowsclientauth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforderud%2Fwindowsclientauth/lists"}