{"id":859,"date":"2019-07-06T07:56:52","date_gmt":"2019-07-06T07:56:52","guid":{"rendered":"https:\/\/alphastar.net.au\/weblog\/?p=859"},"modified":"2019-07-08T12:34:10","modified_gmt":"2019-07-08T12:34:10","slug":"mutual-ssl-authentication","status":"publish","type":"post","link":"https:\/\/alphastar.net.au\/weblog\/2019\/07\/06\/mutual-ssl-authentication\/","title":{"rendered":"Mutual SSL authentication"},"content":{"rendered":"<p><strong>Introduction<\/strong><\/p>\n<p>When the web was in it&#8217;s infancy, data security was of little concern. Businesses were still trying to figure out what the web could be used for beyond a presence on a new emerging technology they had little idea about. Amazon entered the scene in 1994\u00a0 and over time e-commerce developed as a new way of selling things. The data security technology of the time was vastly undercooked and misunderstood at best, requiring each vendor to jimmy up their own solution using CGI-BIN or similar. This may have partially addressed the authentication issue (if at all), but the gate for data in transit was left wide open.\u00a0 An industry standard needed to be developed to fill the vacuum.<\/p>\n<p>SSL (secure Sockets Layer) was devised by the browser company of the day, Netscape Communications. Necessity being the mother of invention, Nestscape also gave us scripting on the browser through the poorly named &#8216;Javascript&#8217; language and cookies to make an inherently stateless protocol stateful.<\/p>\n<p>For data encryption and authentication, Netscape realised that something needed to be hooked in to the server side and browser in concert to ensure that websites could be a) trusted by the user and b) data could be sent confidentially. Both issues being critical to the uptake and acceptance of e-commerce.<\/p>\n<p>Netscape disappeared after a few years being a victim of the browser wars of the late 1990&#8217;s, being usurped by Internet Explorer and other browsers. They left the world with the blueprints to internet security &#8211; and programmers many bad days trying to understand it all.<\/p>\n<p>This article will briefly touch on SSL but spend more time on Mutual SSL with a practical example in Java SpringBoot. Mutual SSL extends the concept of SSL outside of the simple client(\/browser) server(\/web host) scenario. Taking what was built for the web and applying it to a business setting where geographically disparate systems can communicate sensitive data over a public internet.<\/p>\n<p>HTTPS is an industry standard method for encrypting traffic between the client and server in transit. It thwarts anyone listening to the traffic from being able to piece together the flow of data (aka Man in the middle attack) by performing encryption at the session layer. It makes for a good use case for authentication and data security in a re-adapted form.<\/p>\n<p>Before discussing mutual SSL, a refresher on Single SSL. The concepts will help to learn mutual SSL<\/p>\n<p><strong>SSL primer<\/strong> (<a href=\"#mutssl\">skip<\/a> if you know this already)<\/p>\n<p>To explain, lets take a look at the case of a user visiting a website https:\/\/example.com<\/p>\n<p>When a user visits this site with a standard web browser, the page is served and the browser displays a padlock to signify that the page is secure. Behind the scenes the client and server are performing a number of operations that are opaque to the user. This includes swapping available cipher lists, setting a symmetric session key for data encryption, performing public key operations on signed certificates to ensure that the website is legitimate. More detail on these operations follows.<\/p>\n<p>Firstly, the HTTPS server sends a certificate back to the user. This certificate is essentially a public key wrapped up with some additional information. This additional information is in the form of an X509 standard. This is a very old format existed before the common web as we know it and was developed for the LDAP protocol . It contains a number of standard fields. The most important being the CN (Common Name). This has been retrofitted for the web host to store the server&#8217;s domain name instead. The name of the organisation, it&#8217;s address, email address and other metadata including the expiry of the certificate are stored in this record.<br \/>An example certificate is shown below:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-879\" src=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/cert-x509-286x300.jpg\" alt=\"\" width=\"572\" height=\"600\" srcset=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/cert-x509-286x300.jpg 286w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/cert-x509.jpg 538w\" sizes=\"auto, (max-width: 572px) 100vw, 572px\" \/><\/p>\n<p>Essentially a certificate is just a public key with some additional describing metadata enclosing it.<\/p>\n<p>When the browser has received the certificate from the server, a check is made to determine whether it should trust or deny the connection based on the certificate. It does this by looking at it&#8217;s trust store.<\/p>\n<p>A trust store is a set of 90 or so certificates that are pre-loaded in to the browser. It might also be referred to as CA Certs or root certificates. Java has it&#8217;s own version of trust store with a number of different vendors. How do we know that these certificates are trustworthy? We just take it axiomatically that there are well known organisations out there that have been granted the privilege to vet companies and perform these actions. (There are means to revoke certificates that are deemed to be compromised, but that&#8217;s another story best not told)<\/p>\n<p>The appropriate certificate (let&#8217;s say it&#8217;s signed by Verisign) is loaded by the browser. The public key of the CA certificate is used to &#8220;unlock&#8221; the server certificate that has been sent to the browser. It can be determined if the server&#8217;s certificate was signed by Verisign (in this example) by performing a public key decryption on it.<\/p>\n<p><!--<img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/trust-store-300x108.jpg\" alt=\"\" width=\"600\" height=\"216\" class=\"alignnone size-medium wp-image-880\" srcset=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/trust-store-300x108.jpg 300w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/trust-store-768x277.jpg 768w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/trust-store.jpg 872w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/>--><br \/><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-883\" src=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/trust-store-blue-300x97.jpg\" alt=\"\" width=\"600\" height=\"194\" srcset=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/trust-store-blue-300x97.jpg 300w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/trust-store-blue-768x249.jpg 768w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/trust-store-blue-1024x332.jpg 1024w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/trust-store-blue.jpg 1165w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>If the certificate is successfully verified, nothing special happens. The web page is loaded normally, and that is a good outcome. If the certificate cannot be verified, then a prompt will display the error condition that the certificate cannot be trusted. Most people will have come across this at least once.<\/p>\n<p><strong>Certificate Authority setup<\/strong><\/p>\n<p>At some point in time Example.com will have generated a certificate on it&#8217;s own. A public key would be randomly generated and the details (domain name, physical address and so on) of the X509 certificate typed in.<\/p>\n<p>To give more credibility (and avoid browser security errors) the web host pays a fee to a CA to sign their certificate. It is their job to vouch for the credentials found in a certificate. They may use old school ways of doing this. Checking the address, telephone number, website domain records etc.<\/p>\n<p>Once satisfied, a CA can choose to SIGN (encrypt with their CA private key) the certificate and send it back to the requestor.<\/p>\n<p>This is known as a CSR (Certificate Signing Request). It&#8217;s a once-off backoffice function. The CA will send the certificate back to the requestor by some means.<br \/>Now at this point the web host has a certificate that has been authenticated &#8211; the same way that the government may a vouch for a citizen with a passport. It&#8217;s an extra level of assurance. In PKI lingo this is known as a &#8220;trust-chain&#8221;. On any website secured with HTTPS, one can view a trust chain. There is the option for a certificate to be signed by another certificate which in turn is (down the line) trusted by a ROOT certificate.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-890\" src=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/ms-1-300x101.jpg\" alt=\"\" width=\"300\" height=\"101\" srcset=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/ms-1-300x101.jpg 300w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/ms-1.jpg 375w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/p>\n<p>To recap: The CA already has it&#8217;s own counterpart public key stored in a set of well known trusted certificates. These are pre-loaded in to the browser and are implicitly trusted. The private key is held by the CA and never disclosed!<\/p>\n<p>The web host has a public key in a certificate that is ultimately sent back to the web browser.<\/p>\n<p>The browser is able to use the public key of the browser ca&#8217;s to correctly unlock the signed certificate (PubKa match with PrivKa). The public key is used to encrypt a shared symmetric session key. Everything works and this is how a browser can now trust an HTTPS connection.<\/p>\n<p><a name=\"mutssl\"><\/a><br \/><strong>Mutual SSL authentication<\/strong><\/p>\n<p>In a browser centric view, single SSL is sufficient since we only require the server to authenticate itself to the browser. In a business setting a requirement may be for applications to authenticate and encrypt traffic each other. Typically an application may act as both a server and a client. Therein lies a challenge!<\/p>\n<p>Mutual SSL allows for both endpoints to authenticate each other, as if the browser becomes a server and a server becomes a browser. The extra SSL handshake is performed in <strong>one call<\/strong> with a few more steps taken.<\/p>\n<p>The stand out benefit of mutual SSL over other forms of authentication is that it is a non-interactive password-less solution. Two applications running on separate hosts can authenticate and swap traffic confidentially all by virtue of each host configuring each other&#8217;s trust-store as a one time setup.<\/p>\n<p><strong>Code<\/strong><\/p>\n<p>I will first show a SpringBoot application that serves a simple JSON response, configure it to run on SSL and configure it&#8217;s keys. Next, a client will be coded to show the mutual authentication taking place. This will be verified by use of a protocol analyser to show the frames of data on the wire.<\/p>\n<p>First things first: The tricky aspect of setting up the keys. Both the client and server require the keys to be generated in a specific way. This can be done with utilities such as keytool or openssl. For this tutuorial, we wil use keytool which is shipped with Java.<\/p>\n<p>These steps will generate the necessary keys for client and server:<\/p>\n<p><code>#create server certificate. For \"First and last name\" [CN] type \"localhost\". For Orginizational Unit [OU] type \"Yin\"<\/code><br \/><code><br \/>\nkeytool -genkeypair -alias server-keypair -keyalg RSA -keysize 2048 -validity 3650<\/code><br \/><code><br \/>\n-keypass password -keystore server-keystore.jks -storepass password<\/code><\/p>\n<pre><code>#export keys(public) from above\nkeytool -exportcert -alias server-keypair -file server-public-key.cer -keystore\nserver-keystore.jks -storepass password\n\n#import above key in the client trust store and give to client\nkeytool -importcert -keystore client-truststore.jks -alias server-public-key -file\nserver-public-key.cer -storepass password -noprompt\n\n# Reverse the procedure to create client certificate<br \/># For \"First and last name\" [CN] type \"localhost\". For Orginizational Unit [OU] type \"Yang\"\n\n# generate client keystore\nkeytool -genkeypair -alias client-keypair -keyalg RSA -keysize 2048 -validity 3650\n-keypass password -keystore client-keystore.jks -storepass password\n\n# export keys public from above\nkeytool -exportcert -alias client-keypair -file client-public-key.cer -keystore\nclient-keystore.jks -storepass password\n\n# import it into the server keystore and give to server\nkeytool -importcert -keystore server-truststore.jks -alias client-public-key -file\nclient-public-key.cer -storepass password -noprompt<\/code><\/pre>\n<p>We now have the keys setup. The intermediate certs (step 2 and 5) can be deleted.<\/p>\n<p>A useful tool to inspect these files is keytool explorer (https:\/\/keystore-explorer.org\/).<\/p>\n<p>Opening the keystore we can see that the identity key has a private and public key. The truststore has a certificate only &#8211; which is the public key that was extracted out from the keystore.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-895\" src=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/client-300x27.jpg\" alt=\"\" width=\"600\" height=\"54\" srcset=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/client-300x27.jpg 300w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/client-768x69.jpg 768w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/client.jpg 934w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-896\" src=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/server-300x31.jpg\" alt=\"\" width=\"600\" height=\"62\" srcset=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/server-300x31.jpg 300w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/server-768x79.jpg 768w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/server.jpg 915w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p><strong>Java Springboot application<\/strong><\/p>\n<p>The application can be pulled from <a href=\"https:\/\/github.com\/rptrus\/ssl-mutual-auth\">github<\/a> or the zip file can be downloaded.<\/p>\n<p>Set up a SpringBoot application; and configure the properties as so:<br \/><code><br \/>\nsecurity.require-ssl=true<\/code><br \/><code><br \/>\nserver.ssl.key-store-type=JKS<\/code><br \/><code><br \/>\nserver.ssl.key-store=classpath:server-keystore.jks<\/code><br \/><code><br \/>\nserver.ssl.key-store-password=password<\/code><br \/><code><br \/>\nserver.ssl.key-alias=server-keypair<\/code><\/p>\n<p><code>server.ssl.trust-store=classpath:server-truststore.jks<\/code><br \/><code>server.ssl.trust-store-password=password<\/code><br \/><code>server.ssl.trust-store-type=JKS<\/code><br \/><code>server.ssl.client-auth=want<\/code><\/p>\n<p>The keystore contains the private\/public key pair of the <strong>server<\/strong> itself<br \/>The truststore contains the certificate of the <strong>client<\/strong><\/p>\n<p><strong>Model<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">@JsonInclude(JsonInclude.Include.NON_NULL)\n@JsonPropertyOrder({\n\"image\",\n\"desc\",\n\"title\"\n})\npublic class Response implements Serializable\n{\n\n@JsonProperty(\"image\")\nprivate String image;\n@JsonProperty(\"desc\")\nprivate String desc;\n@JsonProperty(\"title\")\nprivate String title;\n@JsonIgnore\n@Valid\nprivate Map&lt;String, Object&gt; additionalProperties = new HashMap&lt;String, Object&gt;();\nprivate final static long serialVersionUID = 8531467032914953885L;\n\n\/**\n* No args constructor for use in serialization\n*\n*\/\n\n\/\/import com.fasterxml.jackson.annotation.JsonProperty;\n\npublic Response() {\n}\n\n\/**\n*\n* @param title\n* @param image\n* @param desc\n*\/\npublic Response(String image, String desc, String title) {\nsuper();\nthis.image = image;\nthis.desc = desc;\nthis.title = title;\n}\n\n@JsonProperty(\"image\")\npublic String getImage() {\nreturn image;\n}\n\n@JsonProperty(\"image\")\npublic void setImage(String image) {\nthis.image = image;\n}\n\npublic Response withImage(String image) {\nthis.image = image;\nreturn this;\n}\n\n@JsonProperty(\"desc\")\npublic String getDesc() {\nreturn desc;\n}\n\n@JsonProperty(\"desc\")\npublic void setDesc(String desc) {\nthis.desc = desc;\n}\n\n@JsonProperty(\"title\")\npublic String getTitle() {\nreturn title;\n}\n\n@JsonProperty(\"title\")\npublic void setTitle(String title) {\nthis.title = title;\n}\n\n@JsonAnyGetter\npublic Map&lt;String, Object&gt; getAdditionalProperties() {\nreturn this.additionalProperties;\n}\n\n@Override\npublic int hashCode() {\nreturn new HashCodeBuilder().append(title).append(additionalProperties).append(image).append(desc).toHashCode();\n}\n\n@Override\npublic boolean equals(Object other) {\nif (other == this) {\nreturn true;\n}\nif ((other instanceof Response) == false) {\nreturn false;\n}\nResponse rhs = ((Response) other);\nreturn new EqualsBuilder().append(title, rhs.title).append(additionalProperties, rhs.additionalProperties).append(image, rhs.image).append(desc, rhs.desc).isEquals();\n}\n\n}\n\n<\/pre>\n<p><strong>Rest endpoints<\/strong><\/p>\n<p>There will be 3 endpoints set up, named bronze, silver and gold.<\/p>\n<p>Bronze &#8211; will deny all connections<br \/>Silver &#8211; will allow all connections<br \/>Gold &#8211; will only allow access to a client that has mutually authenticated<\/p>\n<p>The code for the REST endpoints is as follows:<\/p>\n<p><em><strong>Bronze<\/strong><\/em><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">@PostMapping(value = \"\/bronze\/person\", produces = \"application\/json\")\n@ResponseBody\npublic ResponseEntity personMethodTwo(@RequestBody Person person) {\npreamble(person);\nResponseContainer responseContainer = new ResponseContainer();\n\nList response = Collections.singletonList(new Response(\"Image2\", \"Basketball\", \"The Title2\"));\nresponseContainer.setResponse(response);\nreturn new ResponseEntity&lt;&gt;(responseContainer, HttpStatus.OK);\n\n}<\/pre>\n<p><em><strong>Silver<\/strong><\/em><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">@PostMapping(value = \"\/silver\/person\", produces = \"application\/json\")\n@ResponseBody\npublic ResponseEntity personMethodThree(@RequestBody Person person) {\npreamble(person);\nResponseContainer responseContainer = new ResponseContainer();\n\nList response = Collections.singletonList(new Response(\"SoccerBall.jpg\", \"Soccer ball\", \"The Title3\"));\nresponseContainer.setResponse(response);\nreturn new ResponseEntity&lt;&gt;(responseContainer, HttpStatus.OK);\n}\n\n<\/pre>\n<p><em><strong>Gold<\/strong><\/em><code><br \/>\n<\/code><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">@PostMapping(value = \"\/gold\/person\", produces = \"application\/json\")\n@ResponseBody\npublic ResponseEntity personMethod(@RequestBody Person person) {\nlogger.info(\"Incoming info (displayed and discarded)\");\nlogger.info(\"Name {}\", person.getName());\nlogger.info(\"AGE {}\", person.getAge());\n\n\n\nSimpleDateFormat format = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\nlogger.info(\"Hit the service at {}\", format.format(new java.util.Date()));\nResponseContainer responseContainer = new ResponseContainer();\n\nList responseList = new ArrayList&lt;&gt;();\nResponse response1 = new Response();\nResponse response2 = new Response();\nresponse1.setDesc(\"Beachball\");\nresponse1.setImage(\"ImageBeachBall.jpg\");\nresponse1.setTitle(\"Title 1\");\nresponse2.setDesc(\"Basketball\");\nresponse2.setImage(\"ImageBasketball.jpg\");\nresponse2.setTitle(\"Title 2\");\nresponseList.add(response1);\nresponseList.add(response2);\nresponseContainer.setResponse(responseList);\nreturn new ResponseEntity&lt;&gt;(responseContainer, HttpStatus.OK);\n}<\/pre>\n<p><code><\/code><\/p>\n<p>A lot of the boilerplate code is out of the way. Now to draw attention to the facilities that Spring provides for securing endpoints.<\/p>\n<p><strong>Antmatchers<\/strong><\/p>\n<p>Antmatcher is a regex-like way of specifying patterns and its heritage comes from ant build tool.<\/p>\n<p>The first consideration is whether the entire application is to be secured via mutual SSL auth. This is controlled by the flag<br \/><code>server.ssl.client-auth=want<\/code><\/p>\n<p>The flag can also be set to &#8220;need&#8221; which will require all connections to be mutually authenticated. For this application, we will set it to want as we still want other endpoints to function without needing mutual SSL.<\/p>\n<p>To provide this configuration extend the WebSecurityAdapter class.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">@Value(\"${X509SubjectMatch}\")\nprivate String valueToMatch;\n\n@Override\nprotected void configure(HttpSecurity http) throws Exception {\nString x509Subject = String.format(\"CN=localhost(.*%s.*)\",valueToMatch);<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">X509AuthenticatedUserDetailsService x509UserDetails = new X509AuthenticatedUserDetailsService();<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">x509UserDetails.setMatch(valueToMatch);<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">http.csrf().disable()\n.authorizeRequests()\n.antMatchers(\"\/bronze\/person\").denyAll()\n.antMatchers(\"\/silver\/person\").permitAll()\n.and().authorizeRequests()\n.antMatchers(\"\/gold\/person\").authenticated()\n.and().x509().subjectPrincipalRegex(x509Subject).authenticationUserDetailsService(x509UserDetails);\n\n}<\/pre>\n<p>The bronze and silver endpoints are controlled by the simple predicates denyall() and permitAll() respectively. We will use these as our base cases for testing.<\/p>\n<p>For mutual SSL endpoint, we will need a way to selectively apply certificate inspection of the client. This is controlled by the following code<\/p>\n<p><code><br \/>\n.and().authorizeRequests()<br \/>\n.antMatchers(\"\/gold\/person\").authenticated()<br \/>\n.and().x509().subjectPrincipalRegex(\"CN=localhost(.*something.*)\").authenticationUserDetailsService(new X509AuthenticatedUserDetailsService())<br \/>\n<\/code><\/p>\n<p>What is happening here is that the incoming certificate from the Client (which will be built and explained later) is being sent to the server. The server will read the subject line of the certificate. If it matches a value that we expect to see, then both endpoints will be mutually authenticated. If the subject doesn&#8217;t match in some regard, the connection is denied.<\/p>\n<p>A curious aspect is that the x509 predicate requires a UserDetailsService to be added. In this case there isn&#8217;t really a notion of a user or a password. We create this user class all the same, and perform additional checks. The password field can be left blank.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">@Component\npublic class X509AuthenticatedUserDetailsService implements AuthenticationUserDetailsService&lt;PreAuthenticatedAuthenticationToken&gt; {\n\n    private final org.slf4j.Logger logger = LoggerFactory.getLogger(this.getClass());\n    \n    private String partialSubject;\n\n    @Override\n    public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token)\n            throws UsernameNotFoundException {\n        logger.info(\"Entered loadUserDetails(...)\");\n        X509Certificate certificate = (X509Certificate)token.getCredentials();\n        if (!certificate.getSubjectX500Principal().getName().contains(partialSubject)) {\n            logger.warn(\"This is an unknown \/ unexpected cert\");\n        }\n\n        Collection&lt;GrantedAuthority&gt; authorities = Collections.EMPTY_LIST;\n        return  new User(certificate.getSubjectX500Principal().getName(), \"(un-necessary for certificate validation)\", authorities);\n    }\n    \n    public void setMatch(String partialSubject) {\n    \tthis.partialSubject  = partialSubject;\n    }\n}\n\n<\/pre>\n<p><code><\/code><\/p>\n<p><code><\/code><\/p>\n<p>The incoming client certificate can be inspected and it&#8217;s values read:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-935 size-full\" src=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/inspectSubject-1.jpg\" alt=\"\" width=\"888\" height=\"544\" srcset=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/inspectSubject-1.jpg 888w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/inspectSubject-1-300x184.jpg 300w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/inspectSubject-1-768x470.jpg 768w\" sizes=\"auto, (max-width: 888px) 100vw, 888px\" \/><\/p>\n<p>Next, we need to build a client to test it. Postman can be used, though you will need to configure it to send certificates (not explained in this article). Instead a small homegrown client will be built to show how certificates can be sent across in an HTTPS call. Remember, for normal HTTPS calls (for instance connecting to a website), this is not normally a concern for the user. All that would be required is changing HTTP to HTTPS.<\/p>\n<p>For mutual authentication SSL, we will require the client to send the certificate to the server, so that the server can validate it.<\/p>\n<p><strong>Client<\/strong><\/p>\n<p>The client will make use of ApacheHTTP Client for making outbound calls to the ReST service.<\/p>\n<p>The example JSON payload will look something like:<\/p>\n<p><code><br \/>\npublic static final String ONE_JSON = \"{\\n\" +<br \/>\n\"\\t\\\"name\\\" : \\\"tom\\\",\\n\" +<br \/>\n\"\\t\\\"age\\\" : \\\"23\\\"\\n\" +<br \/>\n\"}\";<\/code><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">package com.alphastar.service;\n\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.conn.ssl.SSLConnectionSocketFactory;\nimport org.apache.http.entity.StringEntity;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.http.impl.client.HttpClients;\nimport org.apache.http.ssl.PrivateKeyDetails;\nimport org.apache.http.ssl.PrivateKeyStrategy;\nimport org.apache.http.ssl.SSLContexts;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.stereotype.Component;\n\nimport com.alphastar.statics.JsonFiles;\n\nimport javax.annotation.PostConstruct;\nimport javax.net.ssl.SSLContext;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.net.Socket;\nimport java.security.*;\nimport java.security.cert.CertificateException;\nimport java.util.Map;\n\n@Component\npublic class HttpClientSend {\n\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @PostConstruct\n    public void go() {\n        try {\n            String CERT_ALIAS = \"client-keypair\", CERT_PASSWORD = \"password\";\n\n            \/\/ use classpath to avoid filesystem. File should be in resources folder root dir\n            InputStream clientKeystore = new ClassPathResource(\n                    \"client-keystore.jks\").getInputStream();\n\n            InputStream clientTruststore = new ClassPathResource(\n                    \"client-truststore.jks\").getInputStream();\n\n            KeyStore identityKeyStore1 = KeyStore.getInstance(\"jks\");\n            identityKeyStore1.load(clientKeystore, CERT_PASSWORD.toCharArray());\n\n            KeyStore trustKeyStore1 = KeyStore.getInstance(\"jks\");\n            trustKeyStore1.load(clientTruststore, CERT_PASSWORD.toCharArray());\n\n\n            SSLContext sslContext = null;\n            try {\n                sslContext = SSLContexts.custom()\n                        \/\/ load identity keystore\n                        .loadKeyMaterial(identityKeyStore1, CERT_PASSWORD.toCharArray(), new PrivateKeyStrategy() {\n                            @Override\n                            public String chooseAlias(Map&lt;String, PrivateKeyDetails&gt; aliases, Socket socket) {\n                                return CERT_ALIAS;\n                            }\n                        })\n                        \/\/ load trust keystore\n                        .loadTrustMaterial(trustKeyStore1, null)\n                        .build();\n            } catch (UnrecoverableKeyException e) {\n                e.printStackTrace();\n            }\n\n            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,\n                    new String[]{\"TLSv1.2\", \"TLSv1.1\"},\n                    null,\n                    SSLConnectionSocketFactory.getDefaultHostnameVerifier());\n\n            CloseableHttpClient client = HttpClients.custom()\n                    .setSSLSocketFactory(sslConnectionSocketFactory)\n                    .build();\n\n            logger.info(\"[GOLD] MUTUAL AUTH SSL ONLY\");\n            String ncwurl = \"https:\/\/localhost:8443\/gold\/person\";\n            HttpPost post = new HttpPost(ncwurl);\n            StringEntity ncwparams = new StringEntity(JsonFiles.ONE_JSON);\n            setPostAttrs(post,ncwparams);\n            HttpResponse response = client.execute(post); \/\/ object reused intentionally\n            printResponse(response);\n\n            logger.info(\"[BRONZE] FORBIDDEN BY ANTMATCHER\");\n            post  = new HttpPost(\"https:\/\/localhost:8443\/bronze\/person\");\n            setPostAttrs(post, ncwparams);\n            response = client.execute(post);\n            printResponse(response);\n\n            logger.info(\"[SILVER] ALLOW ALL BY ANTMATCHER\");\n            post  = new HttpPost(\"https:\/\/localhost:8443\/silver\/person\");\n            setPostAttrs(post, ncwparams);\n            response = client.execute(post);\n            printResponse(response);\n\n        } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | IOException e) {\n            throw new RuntimeException(e);\n        } catch (CertificateException e) {\n            e.printStackTrace();\n            logger.error(\"Error caught\", e);\n        }\n\n    }\n\n    private void setPostAttrs(HttpPost post, StringEntity ncwparams) {\n        post.setHeader(\"Accept\", \"application\/json\");\n        post.setHeader(\"Content-type\", \"application\/json\");\n        post.setEntity(ncwparams);\n    }\n\n    private void printResponse(HttpResponse response) throws IOException {\n        logger.info(\"Response Code: \" + response.getStatusLine().getStatusCode());\n        BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));\n        String line = \"\";\n        while (true) {\n            try {\n                if (!((line = rd.readLine()) != null)) break;\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n            System.out.println(line);\n        }\n    }\n}<\/pre>\n\n\n<p>To test the service, run the Server Application in Eclipse or IntelliJ. Then start the Client service and inspect. The client will return data for the gold() antMatcher using mutual authentication.<\/p>\n\n\n\n<p>The results should look similar to this:<\/p>\n\n\n\n<ul class=\"wp-block-gallery columns-1 wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\"><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"219\" src=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/results-1-1024x219.jpg\" alt=\"\" data-id=\"940\" data-link=\"https:\/\/alphastar.net.au\/weblog\/2019\/07\/06\/mutual-ssl-authentication\/results-1\/\" class=\"wp-image-940\" srcset=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/results-1-1024x219.jpg 1024w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/results-1-300x64.jpg 300w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/results-1-768x164.jpg 768w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/results-1.jpg 1071w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/li><\/ul>\n\n\n\n<p>The packet trace in Wireshark will look similar to this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"158\" src=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/packet-sniff-1024x158.jpg\" alt=\"\" class=\"wp-image-927\" srcset=\"https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/packet-sniff-1024x158.jpg 1024w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/packet-sniff-300x46.jpg 300w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/packet-sniff-768x118.jpg 768w, https:\/\/alphastar.net.au\/weblog\/wp-content\/uploads\/2019\/07\/packet-sniff.jpg 1213w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>The next article will extend this program to add API token for selected endpoints while keeping mutual SSL for others. Stay tuned&#8230;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction When the web was in it&#8217;s infancy, data security was of little concern. Businesses were still trying to figure out what the web could be used for beyond a&#8230; <\/p>\n","protected":false},"author":1,"featured_media":915,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-859","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-coding"],"_links":{"self":[{"href":"https:\/\/alphastar.net.au\/weblog\/wp-json\/wp\/v2\/posts\/859","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alphastar.net.au\/weblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alphastar.net.au\/weblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alphastar.net.au\/weblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/alphastar.net.au\/weblog\/wp-json\/wp\/v2\/comments?post=859"}],"version-history":[{"count":57,"href":"https:\/\/alphastar.net.au\/weblog\/wp-json\/wp\/v2\/posts\/859\/revisions"}],"predecessor-version":[{"id":944,"href":"https:\/\/alphastar.net.au\/weblog\/wp-json\/wp\/v2\/posts\/859\/revisions\/944"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/alphastar.net.au\/weblog\/wp-json\/wp\/v2\/media\/915"}],"wp:attachment":[{"href":"https:\/\/alphastar.net.au\/weblog\/wp-json\/wp\/v2\/media?parent=859"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alphastar.net.au\/weblog\/wp-json\/wp\/v2\/categories?post=859"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alphastar.net.au\/weblog\/wp-json\/wp\/v2\/tags?post=859"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}