{"id":2173,"date":"2016-03-06T12:47:45","date_gmt":"2016-03-06T18:47:45","guid":{"rendered":"http:\/\/www.mooreds.com\/wordpress\/?p=2173"},"modified":"2021-11-21T12:39:41","modified_gmt":"2021-11-21T18:39:41","slug":"step-back-and-review-the-problem-ssl-edition","status":"publish","type":"post","link":"https:\/\/www.mooreds.com\/wordpress\/archives\/2173","title":{"rendered":"Step back and review the problem: SSL Edition"},"content":{"rendered":"<p>I spent the better half of a week recently getting an understanding of how SSL works and how to get client certificates, self signed certificates and java all playing nicely. \u00a0However, in the end, taking a big step back and examining the problem led to an entirely different solution.<\/p>\n<p>The problem in question is access to a secure system from a heroku application. \u00a0The secure system is protected by an IP based firewall, basic authentication over SSL, and client certificates that were signed by a non standard certificate authority (the owner of the secure system).<\/p>\n<p>We are using HttpClient from Apache for all HTTP communication, and there&#8217;s a <a href=\"https:\/\/hc.apache.org\/httpcomponents-client-ga\/httpclient\/examples\/org\/apache\/http\/examples\/client\/ClientCustomSSL.java\">nice example<\/a> (but, depending on your version of the client, run <code>loadKeyMaterial<\/code> too, as that is what ends up sending the client cert). \u00a0When I ran it from my local machine, after setting the IP to be one of the static IPs, I ended up seeing a wide variety of errors. \u00a0Oh, there are many possible errors! \u00a0But finally, after making sure that the I could <a href=\"http:\/\/www.agentbob.info\/agentbob\/79-AB.html\">import the private key into the store<\/a>, was using the correct <a href=\"https:\/\/blogs.oracle.com\/coffeys\/entry\/jdk_and_use_of_rc4\">cipher\/java version combo<\/a>, writing a stripped down client that didn&#8217;t interface with any other parts of the system, learning about <a href=\"http:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/guides\/security\/jsse\/ReadDebug.html\">the <code>javax.net.debug=ssl<\/code> switch<\/a>, making sure the public certificates were all in the store, and that I had IP based access to the secure system, I was able to watch the system go through a number of the steps outlined here:<\/p>\n<figure id=\"attachment_2174\" aria-describedby=\"caption-attachment-2174\" style=\"width: 484px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-2174\" src=\"http:\/\/www.mooreds.com\/wordpress\/wp-content\/uploads\/2016\/02\/sslmessages.gif\" alt=\"SSL Message flow\" width=\"484\" height=\"418\" \/><figcaption id=\"caption-attachment-2174\" class=\"wp-caption-text\">Image from the <a href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/guides\/security\/jsse\/JSSERefGuide.html\">JSSE Guide<\/a><\/figcaption><\/figure>\n<p>But I kept seeing this exception: <code>error: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure<\/code>. I couldn&#8217;t figure out how to get past that. From searching there were a number of issues that all manifested with this error message, and &#8220;guess and check&#8221; wasn&#8217;t working. Interestingly, I saw this error only when running on my local machine with the allowed static IP. When I ran on a different computer that was going through a proxy which provided a static IP, the client certificate wasn&#8217;t being presented at all.<\/p>\n<p>After consulting with the other organization and talking with members of the team about this roadblock, I took a step back and validated the basics. I read the <a href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/guides\/security\/jsse\/JSSERefGuide.html\">JSSE Reference guide<\/a>. I used the openssl tools to <a href=\"https:\/\/www.openssl.org\/docs\/manmaster\/apps\/verify.html\">verify that the chain of certificates was valid<\/a>. (One trick here is that if you have intermediate certs in between your CA and the final cert, you can add only one <code>-untrusted<\/code> switch. So if you have multiple certs in between, you need to combine the PEM files into one file.) I validated that the <a href=\"https:\/\/www.sslshopper.com\/certificate-key-matcher.html\">final certificate in the keystore matched the CSR and the private key<\/a>. Turns out I had the wrong key, and that was the source of the handshake issue. Doh!<\/p>\n<p>After I had done that, I took a look at the larger problem. <a href=\"http:\/\/stackoverflow.com\/questions\/15512360\/get-a-finite-list-of-ip-addresses-for-my-heroku-app\">Heroku doesn&#8217;t guarantee IP addresses, not even a range<\/a>. There are add on solutions (proximo, quotaguard, fixie) that do provide a static IP address, and that was our initial plan. However, all of these are proxy based solutions. A quick search turns up the unpleasant reality that <a href=\"http:\/\/apache-http-server.18135.x6.nabble.com\/How-to-pass-a-Client-Certificate-through-a-Reverse-Proxy-td4754227.html\">proxies can&#8217;t pass client certificates<\/a>. The post talks about a reverse proxy, but it applies as well to regular proxies. From the post:<\/p>\n<blockquote><p>Yes, because a client can only send its certificate by using encrypted and<br \/>\nSIGNED connection, and only the client can sign the certifikate (sic) so server<br \/>\ncan trust it. The proxy does not know the clients private key, otherwise the<br \/>\nconnection would not be secure (or not in the way most people know that).<br \/>\n&#8230;<br \/>\nSSL is made up to avoid man-in-the-middle attack, and the reverse proxy IS<br \/>\nthe man-in-the-middls. Either you trust it (and accept what it sends) or<br \/>\ndon&#8217;t use it.<\/p><\/blockquote>\n<p>All my work on having the java code create the client certificate was a waste. Well, not a total waste because now I understood the problem space in a way I hadn&#8217;t before, so I could perform far better searches.<\/p>\n<p>I opened a support request with our proxy provider, but it was pretty clear from the internet, the support staff and the docs that this was a niche case. I don&#8217;t know if any static IP proxy providers support client certificates, but I wasn&#8217;t able to find one.<\/p>\n<p>Instead, we were able to use AWS elastic IP and nginx to set up <a href=\"https:\/\/www.nginx.com\/resources\/admin-guide\/nginx-tcp-ssl-upstreams\/\">our own proxy<\/a>. Since we controlled it, we could install the client certificate and key on it, and have the heroku instance connect to that proxy. (Tips for those instructions&#8211;make sure you download the openssl source, as nginx wants to compile it into the web server. And use at least version 1.9 of the community software.)<\/p>\n<p>So, I made some mistakes in this process, and in a personal <a href=\"https:\/\/www.scrumalliance.org\/community\/articles\/2014\/april\/key-elements-of-sprint-retrospective\">retro<\/a>, I thought it&#8217;d be worth talking about them.<\/p>\n<ul>\n<li>I jumped into searching too quickly. SSL and private certs is a complicated system that has a lot of moving pieces, and it would have been worth my time looking at an overview.<\/li>\n<li>While I was focusing on accessing the system from the java code, there were hints about the proxy issue. I didn&#8217;t consider the larger picture and was too focused on solving the immediate issue.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>I spent the better half of a week recently getting an understanding of how SSL works and how to get client certificates, self signed certificates and java all playing nicely. [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,75],"tags":[],"class_list":["post-2173","post","type-post","status-publish","format-standard","hentry","category-java","category-security"],"_links":{"self":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts\/2173","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/comments?post=2173"}],"version-history":[{"count":4,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts\/2173\/revisions"}],"predecessor-version":[{"id":3497,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts\/2173\/revisions\/3497"}],"wp:attachment":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/media?parent=2173"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/categories?post=2173"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/tags?post=2173"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}