I'm trying to use Java and Selenium to test a website that requires a client certificate. When I browse to my site I get a popup like the one below to select the correct certificate.
My requirements are as follows:
Ideally the popup is never shown; i.e., the solution would involve invoking some API or setting some configuration to pin the correct certificate to use.
I tried a solution based on visually detecting the correct certificate using SikuliX (which does works) but I'm wondering if there is a better solution that does not rely on visually detecting the popup. Something that is less likely to fail across multiple versions of Windows and that is future proof if Microsoft decides to change the appearance of this popup.
Another idea that I had (but I don't know how/if it is possible) is to remove all installed certificates except for one so that the popup is never shown:
Does anyone know how to do this (in Java, possibly invoking CLI commands)?
Is it possible to start (using Selenium Java) an Internet Explorer Window that only knows the single certificate that I need?
Is it possible in Internet Explorer to set a default certificate for a given domain?
Handling Web Dialog Box/Popup Window using SeleniumYou can get the window handle of the pop-up window using the WindowHandle() function. I have created my own webpage to demonstrate popup handling in Selenium. You can copy the same code and paste in notepad and save it as html file to proceed with testing.
To handle the basic authentication popup, we can pass the username and password along with the web page's URL. When the login pop-up is prompted, we enter the username as “admin” and the password as “admin” and then login. Thus, the user would be successfully logged into the website.
The answer is simple, on manually opening a URL, the browser automatically imports the required certificates, and no error occurs. Simultaneously, in Selenium WebDriver, each run occurs on a new profile that doesn't have the SSL Certificates, hence the error.
The solution we use is quite clean, easy and portable between browsers and operating systems - use proxy server that will handle SSL handshake for you.
You can set up an in-memory Man-In-The-Middle proxy server in the same JVM the tests is running, or even multiple instances on different ports, each assigned to different client certificate. Then, when creating WebDriver instance, use setProxy method suitable for your browser. Note, the browser will be presented with server certificate that is installed on the proxy itself, not the target server, so there might be some invalid certificate errors that should be suppressed in WebDriver settings. Alternatively - the proxy may simply use valid server certificate, if it's key is available to you, in which case the connection is fully transparent for the test script.
One simple proxy server that offers what is required in Java is LittleProxy. Perhaps something like BrowserMob offers a more complete solution with readily available API.
An example using LittleProxy just needs a few (dozen) lines of boilerplate:
Step 1:
Extend org.littleshoot.proxy.MitmManager
class with something you can plugin into your code, making use of client certificates (e.g. p12 files or PEM files). Working example available publicly at this repo.
Step 2:
Start proxy server using client certificate and server certificate of your choice:
org.littleshoot.proxy.impl.DefaultHttpProxyServer.DefaultHttpProxyServer.bootstrap()
.withIdleConnectionTimeout(FIVE_MINUTES)
.withName(clientCertFile.getName())
.withPort(port)
.withAllowLocalOnly(localConnectionOnly)
.withManInTheMiddle(new MutualAuthenticationCapableMitmManager(
usingPKCS12File(clientCertFile, clientCertPassword),
usingPemKeyPair(serverKeyPair[0], serverKeyPair[1])))
.start();
Create another proxy for each client certificate you need reusing the same port or by startnig concurrent instances.
Step 3:
Start WebDriver using the proxy. Major browsers (IE, Firefox, Chrome) support the setting in similar fashion:
org.openqa.selenium.Proxy proxy = new Proxy();
proxy.setSslProxy("127.0.0.1:5555");
proxy.setNoProxy("<-loopback>"); // overwrite the default no-proxy for localhost, 127.0.0.1
FirefoxOptions options = new FirefoxOptions();
options.setProxy(proxy);
WebDriver driver = new FirefoxDriver(options);
Step 4:
When running tests, the browser will never bother you with any certificate prompts. Profit.
If using this technique, please be extra carefull to keep the secrets secure and especially the proxy server itself unreachable to third parties. Exposing keys is never good idea outside of secure corporate network, regardless if they are real (!!!) or fake.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With