How to get client certificate in WAS Liberty with SSL - ssl

I have configured my Was Liberty server with SSL
<ssl id="SSLConfig"
keyStoreRef="KeyStore"
clientAuthentication="true"
sslProtocol="TLS"
clientKeyAlias="****"
serverKeyAlias="***"
/>
It ask for the client's certificate and the Handshake ends correctly.
But after that I need to access to the client's certificate. I think the right way to do this is to use:
request.getAttribute("javax.servlet.request.X509Certificate");
But this returns null. I have tried to access this in a Filter, but it still return null.
Any Ideas? Thanks.

Related

Client certificate based authentication HAProxy and a general questions

I want to add to a Tomcat servlet (which is behind a HAProxy server) client based authentication so what I did was
I've updated Tomcat configuration by adding
<Connector port="18443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="want" sslProtocol="TLS"
keystoreFile="conf/cert/server-keystore.jks"
keystorePass="changeit"
truststoreFile="conf/cert/server-truststore.jks"
truststorePass="changeit" />
P.S more info on https://docs.bmc.com/docs/rsso1908/configuring-the-tomcat-server-for-certificate-based-authentication-907302408.html
I also updated my HAProxy configuration by updating several lines, i.e
listen VIP
bind 172.16.200.85:443 transparent ssl crt /etc/haproxy/cert/server.pem ca-file /etc/haproxy/cert/ca.crt verify required crl-file /etc/haproxy/cert/root_crl.pem
P.S more info on https://www.loadbalancer.org/blog/client-certificate-authentication-with-haproxy/
and when I navigate to a page I get prompted for a certificate immediately, and I don't want that. What I want is a specific path, i.e /login/me to prompt for a certificate to choose. Basically, I want the same solution for client certificate authentication as on
https://secure.login.gov/
--> select "Sign in with your government employee ID"
--> click on the button "Insert your PIV/CAC" [you get a certificate list to choose from]
My questions:
How they are able to to accomplish client certificate based authentication via (it seems) a path "/login/piv_cac"?
I'm asking this, because I've found answers on a stackoverflow configure tomcat for client authentication only for specific URL patterns that this is not possible to accomplish. I've also tried my self, but I get always prompted for a client certificate upon connecting to a Tomcat instance (before navigating to an authentication url)
Is it doable with two Tomcat instances behind a HAProxy?
If so, what would be a general HAProxy configuration [or steps] for this?
If not, what do I need to make it happen?
Do I need one Tomcat instance for "casual" human beings and other Tomcat instance for "certified" human beings?

Tomcat 9.x.x Client Authentication using X.509 Certificates

I’m using Tomcat 9.0.19 and trying to enable X.509 cert.-based client authentication (AKA I&A) for a particular Web application.
In summary, the Tomcat works for an application that has basic I&A enabled over one-way TLS. When accessing the Web application that has certificate-based I&A, Tomcat does not seem to request a client certificate as part of the Server Hello message, prior to sending Server Hello Done and it later fails the authentication check:
02-Jan-2020 13:00:40.371 FINE [https-jsse-nio-443-exec-10] org.apache.catalina.authenticator.SSLAuthenticator.doAuthenticate Looking up certificates
02-Jan-2020 13:00:40.830 FINE [https-jsse-nio-443-exec-10] org.apache.catalina.authenticator.SSLAuthenticator.doAuthenticate No certificates included with this request
Traced the TLS flow in Wireshark and saw the TLS 1.2 handshake. Shortly after encrypted data is exchanged, the Tomcat sends an “Encrypted Alert” message and the socket is closed. Trying to contact the Tomcat from the browser, doing a GET. The browser does not prompt me to select a certificate, which also seems to point to Tomcat not requesting it from the browser.
Any help will be greatly appreciated!
More Details:
We have a set of certificates for the Tomcat and the client, issued by an Intermediate CA, which is signed (issued) by a Root CA. The trust stores have been setup on both sides (client and server) as well as key stores with the right certs/keys in them. The Web application is setup to require certificate I&A (web.xml):
<security-constraint>
<web-resource-collection>
<web-resource-name>All by default</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>OTService</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>certificate</realm-name>
</login-config>
The OTService role is setup in the Tomcat-Users.xml, along with a single user account:
Now, the Connector in server.xml is configured as follows:
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="100" SSLEnabled="true" scheme="https" secure="true">
<SSLHostConfig>
<Certificate certificateKeystoreFile="/apache-tomcat-9.0.19/conf/km/keyStore.jks"
certificateKeystorePassword="PASSWORD"
certificateKeyAlias="tomcat"
type="RSA" />
truststoreFile="/apache-tomcat-9.0.19/conf/km/trust_store.jks"
truststorePass="PASSWORD"
truststoreType="JKS"
certificateVerification="required"
clientAuth="true"
protocols="TLSv1.2"
</SSLHostConfig>
</Connector>
Any ideas why Tomcat would not request a client certificate?
The first issue that I discovered was that Tomcat ignored the Connector->SSLHostConfig settings for the trust store and used the JRE default trust store anyway. The way I discovered it was to have a browser save the negotiated TLS session key to a file (Google SSLKEYLOGFILE), then configured the Wireshark to use that file, captured the browser-Tomcat session and then was able to see every message in plaintext.
Next, I discovered that Tomcat was actually asking for a client cert., but the list of accepted Root CAs it was sending was from the default JRE cacerts file, not from the file specified by the truststoreFile attribute. Can have Tomcat use a different file across the board by adding a setenv.sh file to the Tomcat bin directory with Java properties to override default trust store location.
Now, I was in business, the browser was able to complete the TLS handshake, but then the authentication and authorization steps were failing. I finally determinate that the proper way to provide the cert. subject field in tomcat_users.xml file was not "CN=OU Client, OU=Control Systems, O=IoTOY, L=Scottsdale, S=AZ, C=US", but "CN=OU Client, OU=Control Systems, O=IoTOY, L=Scottsdale, ST=AZ, C=US". Finally, I had 2-way TLS working.
One thing to keep in mind is if anything running on the Tomcat attempts to connect over TLS to another system that uses commercial CA certs, it will fail because the truststore you're using now does not have commercial Root CAs' certs. One way to remediate it is to make a copy of the default JRE cacerts file and add your system-specific CA cert(s) to it and point to it from the setenv.sh file noted above.
When you have:
<Connector ...>
<SSLHostConfig>
<Certificate A=1 B=2 C=3 />
D=4 E=5 F=6
</SSLHostConfig>
</Connector>
then A,B,C are attributes of the Certificate object but D,E,F are NOT attributes of the SSLHostConfig object -- they are XML content which is different. Attributes need to be put IN THE TAG:
<Connector ... >
<SSLHostConfig certificateVerification="required" truststoreFile=... >
<Certificate ...keystore... />
</SSLHostConfig>
</Connector>
and that does cert-request on the initial handshake as desired (for me, tested on tomcat 9.0.14).

Client authentication on JBoss server

I'm trying to configure client authentication for my application running on JBoss. Expected result is that application requests user for certificate and if trusted one is provided, he will be able to work with application.
I've generated certificate and added one into trustore (JBoss.keystore) and also configured standalone.xml file as follow:
<connector name="https" protocol="HTTP/1.1" scheme="https" socket-binding="https" enable-lookups="false" secure="true">
<ssl name="ssl" key-alias="ssl alias" password="password" certificate-key-file="..\standalone\configuration\JBoss.keystore" protocol="TLSv1.2" verify-client="true"/>
<virtual-server name="my-host" />
</connector>
I thought that setting secure property true will do the trick, but browser does not ask for user certificate, but immediately returns error ERR_BAD_SSL_CLIENT_AUTH_CERT. Browser is configured to ask for certificate each time, if required.
How to change server configuration into expected behavior?
I have found an answer. The solution was to import CA certificate that signed client certificate into truststore, instead of importing client certificate itself.
After importing CA certificate, each certificate that was signed by CA and imported into browser is displayed to be chosen from.

Azure web role - Multiple ssl certs pointing to a single endpoint

Is there a way I can have multiple ssl certificates point to a single inputendpoint in a service definition? For example, lets say I have two url's.
service.foo.net/Service.svc
service.doo.net/Service.svc
I want both of these addresses to resolve to my windows azure service, but I'm not sure how to configure this in the service definition.
<Certificates>
<Certificate name="service.foo.net" storeLocation="LocalMachine" storeName="My" />
<Certificate name="service.doo.net" storeLocation="LocalMachine" storeName="My" />
</Certificates>
<Endpoints>
<InputEndpoint name="HttpsIn" protocol="https" port="443" certificate="service.foo.net" />
</Endpoints>
According to this MSDN article, each input endpoint must have a unique port. Is there any way to specify more than once certificate for this endpoint?
Unfortunately this is not possible. Azure is re-exposing an SSL limitation. The SSL limitation is interesting, and the reason you can't use v-hosts over SSL. Lets walk through an example:
You connect to https://ig2600.blogspot.com
That resolves to some ip address - say 8.8.8.8
Your browser now connects to 8.8.8.8
8.8.8.8 must preset a certificate before your browser will send any data
the browser verifies the ceritificate presented is for ig2600.blogspot.com
You send the http request, which contains your domain name.
Since the server needs to present a certificate before you tell it the host name you want to talk to, the server can't know which certificate to use if multiple are present, thus you can only have a single cert.
"Oliver Bock"'s answer may work for you and "Igor Dvorkin"'s answer is not valid anymore since IIS 8 with Windows Server 2012 supports SNI, which enables you to add a "hostheader" to HTTPS bindings and having multiple SSL certificates to different domains listening to the same HTTPS port.
You need to automate the process of installing the certificates on the machine and add HTTPS bindings to IIS.
I'm a Microsoft Technical Evangelist and I have posted a detailed explanation and a sample "plug & play" source-code at:
http://www.vic.ms/microsoft/windows-azure/multiples-ssl-certificates-on-windows-azure-cloud-services/
This post indicates you will need a "multi domain certificate", which seems to be a certificate that can match multiple DNS names in step 5 of Igor's answer. I have not tried it, but presumably this certificate can be uploaded to Azure in the usual way.

How to force WCF client to send client certificate?

I'm trying to access a publicly-hosted SOAP web service (not WCF) over https, and I'm getting an error that I've never seen before. First, here are the facts:
This service requires client certificates. I have a certificate that is signed by the same CA as the server's certificate.
I know that the URL is available, as I can hit it in Internet Explorer. IE brings up the "choose certificate" window, and if I pick it (and ignore the server-host-name-does-not-match-certificate error), it goes on and gives me an HTTP 500 error.
If I open the site in Chrome, after picking the cert and ignoring the error, I get a normal error message about WSA Action = null.
If I open the site in FireFox, after ignoring the error, I get a page about how the server couldn't validate my certificate. It never asked me to pick one, so that makes perfect sense.
Now, the exception:
Error occurred while executing test 12302: System.ServiceModel.Security.SecurityNegotiationException: Could not establish secure channel for SSL/TLS with authority 'ihexds.nist.gov:9085'. ---> System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel.
at System.Net.HttpWebRequest.GetResponse()
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
I've traced the interaction with WireShark, but because I'm not an expert in the TLS protocol, I could be missing clues as to what's going on. Here, however, is what I do see:
C -> S Client Hello
Contains things like a random number, date/time, cypher suites supported, etc
S -> C Server Hello, Certificate, Certificate Request, Server Hello Done
Contains the server's certificate, and a request for a client certificate
C -> S Certificate, Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message
HERE IS THE INTERESTING PART -- The first part of this packet is the Certificate handshake, where I assume the client certificate would be, but there are no certificates present (Certificates Length: 0).
S -> C Alert (Level: Fatal, Description: Bad Certificate)
Well, yeah, there was no certificate sent.
My binding is set up as follows:
<binding name="https_binding">
<textMessageEncoding />
<httpsTransport useDefaultWebProxy="false" />
</binding>
My behavior is set up as follows:
<behavior name="clientcred">
<clientCredentials>
<clientCertificate findValue="69b6fbbc615a20dc272a79caa201fe3f505664c3" storeLocation="CurrentUser" storeName="My" x509FindType="FindByThumbprint" />
<serviceCertificate>
<authentication certificateValidationMode="None" revocationMode="NoCheck" />
</serviceCertificate>
</clientCredentials>
<messageInspector />
</behavior>
My endpoint is set up to use both the binding and the behavior. Why does WCF refuse to send the certificate when it creates the https connection?
I solved the problem, but I do not understand why this configuration change fixed it. I changed this line:
<httpsTransport useDefaultWebProxy="false" />
to this:
<httpsTransport useDefaultWebProxy="false" requireClientCertificate="true" />
and magically it started working. I had understood that the requireClientCertificate "knob" was for server-side, so I hadn't tried it during my wrangling. Apparently I was wrong.
There should have been a CertificateRequest from the server, naming acceptable cert types and CAs. If your certificate doesn't match those it won't be sent.
It could be a problem negotiating which security protocol to use. Specifically im thinking that the server might not like WCF trying to use TLS 1.0.
To see if this is the case try to add the following before invoking the service
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3
This could be added either in client code or by placing it in an IEndpointBehavior