Resolving "SSLHandshakeException: Received fatal alert: certificate_unknown" error in BrowserMob-Proxy
BrowserMob-Proxy tool is widely used in combination with Selenium when there is a requirement to capture or modify traffic between your automated test and web application under test. However, as it often happens, when you deal with HTTPs the things do not always go smoothly.
Here in the article we’re going to look at one of such problems. Namely you might encounter the following error:
ERROR l.ClientToProxyConnection| (NEGOTIATING_CONNECT) [id: 0x38d98718, L:0.0.0.0/0.0.0.0:34075 ! R:/127.0.0.1:37270]: Caught an exception on ClientToProxyConnection io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:461) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267)
Let’s see why this might happen and how to resolve this.
Why does this happen
In two words this happens because your browser does not trust the certificate that it receives from the server. However when we’re talking about the proxy, your client code receives the certificate from BrowserMob-Proxy server, not from the end site you interacting with in your Selenium automated tests.
Experiment
Let’s execute our proxy in standalone mode, or pause our code right after the proxy has started. Then configure our web browser to use that proxy settings and call "google.com" page manually. What we can see there? We see that the browser is reporting insecure connection. Let’s click the warning and watch the details of our certificate:
Certificate details dialog shows that the server certified by the certificate received by our browser is "google.com" indeed. But there is also one more thing: issuer of the certificate is shown as "LittleProxy MITM".
So the reason your browser fails to validate the certificate is that it is issued by untrusted party.
A bit more insight
When you call "google.com" without having BrowserMob-Proxy enabled, the server of Google returns SSL certificate chain to your browser. The leaf certificate of the chain certifies the domain you are connecting to, the root certificate of the chain represents "GlobalSign" certification authority. This certificate is normally installed to system or browser’s trust store by default so that all the certificates issued by "GlobalSign" are trusted.
When you enable BrowserMob-Proxy it becomes a Man In The Middle (MITM). It substitutes the original certificate by the one issued by BrowserMob-Proxy itself (it is generated on-the-fly). The schema of such interaction would look like this:
Since neither your browser nor your system has that CA in trust store, the browser does not consider such connection secure.
Solution
Quite a long introduction but it is always worth understanding what underlies the issue rather than just know how to fix it. But.. how to fix it?.
There are two ways you can fix the issue. First one is generate the certificate once, add it to the trust store of your browser and then re-use it in your tests. The downside is that it will require some extra coding in your tests. You can find the example (and some other configuration examples) in BrowserMob-Proxy github page.
Easy way
Much easier is to make your browser trust all certificates. We start from proxy set up:
browserMobProxy = new BrowserMobProxyServer(); browserMobProxy.start(0); Proxy proxy = ClientUtil.createSeleniumProxy(browserMobProxy);
and proceed with the configuration relevant to your browser:
Chrome:
ChromeOptions chromeOptions = new ChromeOptions(); chromeOptions.setProxy(proxy); chromeOptions.setAcceptInsecureCerts(true); driver = new ChromeDriver(chromeOptions);
Firefox:
FirefoxOptions firefoxOptions = new FirefoxOptions(); firefoxOptions.setAcceptInsecureCerts(true); firefoxOptions.setProxy(proxy); driver = new FirefoxDriver(firefoxOptions);
Opera:
OperaOptions operaOptions = new OperaOptions(); operaOptions.setProxy(proxy); operaOptions.setCapability(CapabilityType.ACCEPT_INSECURE_CERTS, true); driver = new OperaDriver();
Safari:
SafariOptions safariOptions = new SafariOptions(); safariOptions.setProxy(proxy); safariOptions.setCapability(CapabilityType.ACCEPT_INSECURE_CERTS, true); driver = new SafariDriver();
Internet Explorer:
InternetExplorerOptions internetExplorerOptions = new InternetExplorerOptions(); internetExplorerOptions.setProxy(proxy); internetExplorerOptions.setCapability(CapabilityType.ACCEPT_INSECURE_CERTS, true); driver = new InternetExplorerDriver();
This seems to be it. We learned how to work with HTTPs resources using BrowserMob-Proxy tool. I appreciate your feedback so feel free to share your questions and thoughts with me using this form.