AWS EC2 with Elastic Load balancer not sending SSL Certificate - java

I have a server that is sending an HTTPPost request with the Apache HttpClient in Java. I am trying to send the post with my cert attached for validation and the other side is saying they are not receiving anything SSL information. I inherited this code/server setup with minimal documentation so I don't necessarily know it was set up properly in the first place.
Here is the setup.
AWS EC2 server. Linux
Elastic Load balancer setup with SSL Cert.
a. Verified this is working as intended when I visit the server via port 443
Tomcat7 running API server
Java 1.8.0_251
Apache httpclient-4.5.9
Being that there are ways to attach the certificate to each level of this I don't fully understand where the problem is.
Most of the ways I have found revolve around attaching the SSL to the outgoing request in the httpclient object but have been unsuccessful. They are a variation on the following...
KeyStore identityKeyStore = KeyStore.getInstance("JKS");
identityKeyStore.load(identity_file, CERTPASSWORD.toCharArray());
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(identityKeyStore, CERTPASSWORD.toCharArray()).build(); // load client certificate
sslConnectionSocketFactory = new SSLConnectionSocketFactory(
sslContext,
new String[]{"TLSv1.2", "TLSv1.1"},
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
This has not worked and I have verified the .jks file is properly formatted and verified the password. Although if this works as I test on a windows machine I would assume this would be the best option.
This server only has outgoing requests to a few sources all of which will need to be SSL verified so I don't mind if it is sent on every outgoing https request.
Is there somewhere else in my setup that I should be looking into attaching the certificate?

If there is a Load balancer with SSL configured then the SSL stops at the load balancer, and your client certificate is never making it to the server. Client SSL certificates only get passed to the server when the server has an SSL certificate installed on it, and the load balancer is in TCP passthrough mode.

Related

SSL Configuration for Http Client

this question might sound I bit dummy but I have researched many questions/answers here and can't find the answer for my case.
Currently I am using RestTemplate library to make HTTP requests for my java library that I am currently working on. In order to have successful HTTP call to HTTPS URLs i needed to add a SSL configuration for my HTTP client. Something like this:
clientBuilder
.disableCookieManagement()
.setDefaultRequestConfig(requestConfig)
.setSSLSocketFactory(new SSLConnectionSocketFactory(SSLContexts.custom()
.loadTrustMaterial(null, new TrustSelfSignedStrategy())
.build()));
So this library is supposed to be shipped to the user as a jar executable application and I know that using self-signed SSL certificates is not a good idea for general usage since there might have web servers that do not acknowledge it. I read that If I get a signed SSL certificate then I should save the proper keys on Keystore and also use Trustore to acknowledge the server's certificate. I do not think I can just pass Keystore and Trustore to the client who executes my java library, so my question here is, does Java has a built-in SSL certificate, so I could somehow just do some configuration on HTTP client and the built-in certificates would be used. As far as I know, node.js offers something like that.
Could anyone just give me a explanation of how this works for java spring-boot?
There are two separate certificate verifications that could be happening. To connect to a server using https, you need to receive the server's certificate and validate it using a truststore.
In addition, it is possible for you to have a client certificate, and to pass that to the server so it can authenticate your client. Unless you have been told you need to do that, you don't. Unless the server has been specifically configured to do it, it isn't possible. If it is what you need to do, you need to obtain a client certificate, install it into a keystore and use that keystore in your client.
So for normal https, you do not need a keystore.
Whether you need "TrustSelfSignedStrategy" depends on the server.
If the server has a valid signed SSL certificate, you do not need to do anything special, RestTemplate will just work.
If the server has a self-signed certificate, you need to either configure the client to accept any self-signed certificate, or load the server's certificate into a truststore so the client knows to accept that specific certificate.

java https://localhost (SSL) - possible without installing certs on client?

After reading the following, I'm still stuck on making the barest-minimum https://localhost stand-alone install-free webserver java app. It needs to be library-free, use Java 8, and accept connections from the browser without first installing any special client certs. I'm unclear if this is at all possible with self-signed certs because it only has to work for "localhost".
How make SSL server socket support both http & https in java?
My simple java HTTPS server only works for localhost
Simple Java HTTPS server
So far I've generated some key files using
openssl genrsa -aes128 -out privkey.pem 2048 # makes privkey.pem
openssl req -new -x509 -key privkey.pem # makes cert.crt
and I've cobbled together the bare minimum Kotlin setup function
private fun ssl():SSLServerSocketFactory {
val password = "MYPASSWORD".toCharArray()
val kmf = KeyManagerFactory.getInstance("SunX509")
val tmf = TrustManagerFactory.getInstance("SunX509")
val sslContext = SSLContext.getInstance("TLS")
// initialise the keystore
KeyStore.getInstance("JKS").let { ks->
FileInputStream("lig.keystore").use {
ks.load(it, password)
}
kmf.init(ks, password)
tmf.init(ks)
}
// setup the HTTPS context and parameters
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null)
return sslContext.serverSocketFactory
}
ssl().createServerSocket().use { serverSocket ->
serverSocket.reuseAddress = true
serverSocket.bind(InetSocketAddress(port))
logger.info { "WebServer ready and listening on ${serverSocket.localPort}" }
But I'm having trouble how to finish it off: Do I need to make a lig.keystore file? Can this even be done without installing certs on the client's browser?
There are two common approaches to getting a secure connection between a client (browser) and server via HTTPS:
You can obtain SSL certificate for the server that is signed by a Root Certification Authority (CA) that is trusted by the user's web browser by default.
You can generate a self-signed SSL certificate, and have the user import it into their web browser as a trusted cert.
What you have done so far seems to be to generate a server-side keystore with a self-signed cert in it and (more or less) configured the Kotlin server to use it. The problem is the client (browser). There is no safe way to get the browser to trust the self-signed cert without the involvement of the user or the user's sysadmin. (Safe ... as in safe for the user!)
And no legitimate CA should ever issue an SSL cert for "localhost"; e.g. https://www.ssl2buy.com/wiki/how-to-get-ssl-certificate-for-web-applications-that-runs-on-localhost
Impasse.
OK, so lets step back. The purpose of using HTTPS / SSL is to ensure that:
The user's web browser is talking to the correct server, and not some other server that is impersonating it.
The connection between the browser and the server is encrypted so that no third party can snoop on the traffic.
But you are trying to do this for a localhost connection. The localhost IP address is a loopback address. Unless the OS kernel is compromised, you are guaranteed that network packets sent via a loopback connection will not leave the host.
You can dismiss the "impersonation" problem. Assuming that the user's machine has not been compromised, nobody else can launch a "fake" server on the user's machine.
You can dismiss the "snooping" problem. Assuming that the user's machine has not been compromised:
The packets won't go off-host, so they can't be snooped on any "external" networks.
The only person who can "snoop" the packets on the loopback network is the user him / herself.
So, the solution is simple. Use "http" for your "localhost" connection. It should be secure ... assuming that the user's machine has not been compromised.
Note: if the user's machine has been compromised, then the bad guys have other ways to intercept the traffic that SSL won't protect against.
Another specific case:
I'm facing a web app from https that would load local data at http://localhost
Safari web browser blocks because of unsecure communication (http) in a secure flow (https).
This behavour could be discussed, but in that case self signed certificate for localhost would help., even with a warning from Safari browser.

SSL client (Java) is not sending a certificate back to the server in two-way SSL handshake

We are trying to access a restful web service resource hosted on IIS server with https protocol.
When we disable TWO WAY SSL Auth (server side validation of client certificate disabled) everything works fine.
When the IIS imposes TWO WAY SSL (server side validation of client certificate enabled) we are getting the below exception:
403 - Forbidden: Access is denied.
You do not have permission to view this directory or page using the credentials that you supplied.
We are using java 1.8 update 102, IIS server 7.5 and TLS 1.2 for ssl
For detailed issue please open the below link:
For details SSL Debug log, certificates, client program
It will be great help if someone help us.
Thanks!
See this warning in the SSL log:
no suitable certificate found - continuing without client authentication
Your server is sending a list of accepted CAs to request a client certificate, but your client does not find a suitable one. It seems your keystore has the correct certificate. Ensure that your certificate is correct, for example installing it in the browser and navigating to a protected resource
May be it is a configuration issue of your Java client. Please read HttpClientBuilder documentation carefully
System properties will be taken into account when configuring the default implementations when useSystemProperties() method is called prior to calling build().
You did not call useSystemProperties().
See also this bug report that might affect you https://issues.apache.org/jira/plugins/servlet/mobile#issue/HTTPCLIENT-1477

Undertow HTTPS listener

I'm trying to serve a simple 'Hello world!' response over HTTPS with self-signed certificate using Undertow embedded server. However I get ERR_SSL_VERSION_OR_CIPHER_MISMATCH on the client (Chrome) and SSLHandshakeException: no cipher suites in common in server logs.
It seems that additionally to KeyManager[] array I need to provide the TLS protocol to use, but I can not found where I should put it. Complete example is avaiable at https://github.com/isopov/undertow-https-test/blob/master/src/main/java/com/sopovs/moradanen/UndertowHttpsTest.java
while at https://github.com/isopov/undertow-https-test/blob/master/src/main/java/com/sopovs/moradanen/JettyHttpsTest.java there is example of similar test using Jetty server (to prove that certificate is good).
This change to code worked for me:
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(getKeyManagers(), null, null);
Undertow.builder().addHttpsListener(10443, "0.0.0.0", sslContext)...

Mule ESB does not accept SSL cert when deployed on server, but works fine on local dev machine

I have Mule ESB deployed to a Linux server and Anypoint Studio running in my local windows dev environment. I have a fairly simple flow that includes a SalesForce connector. Salesforce REST API has a valid cert signed by VeriSign and my local instance of Mule happily accepts it and connects with no problems. However, when Mule runs on the server it always throws "PKIX path building failed, unable to find valid certification path to requested target".
I've tried using the default java keystore, specifying a keystore with javax.net.ssl.keystore, nothing works.
On my local machine I've ended up doing the following to show that Mule is using the right trust store:
I generated an empty truststore and added it to my AnyPoint project as a resource. I created an HTTPS connector configuration in order to explicitly specify the truststore I want to be used for my SalesForce connector, and pointed it to my empty truststore. When I try to run the project locally, I get the exact same SSL error (as I would expect, since it's an empty truststore). I then take the VeriSign CA cert and add it to my empty truststore. After that, locally everything works just fine. To me, this confirms that my mule project is using the truststore that I've added as a resource to the project itself.
I then export this project and deploy it to my server. On the server it throws the SSL error.
Can there be some weird JVM config differences that could cause this?
It turns out that Mule does not use a different trust store for each service. If you have multiple services deployed to it, the last service to declare a trust store explicitly will force all other services to use that same trust store, overwriting whatever configuration they may have. This was happening in my case. I found this out by echoing out System.getProperty("javax.net.ssl.trustStore"); to log and realized it's a trust store that was a resource in some completely different deployed project but being used by mine.
Seems like a pretty bad screw up by the Mule guys.
I agree with your suspicion that the JDK on your linux server doesn't trust the proper certificates. However, this doesn't need to stop your application from doing so.
I've been able to make the salesforce connector trust a given certificate by doing the following:
KeyStore truststore = KeyStore.getInstance("JKS");
truststore.load(myKeystoreInputStream, myKeystorePassword.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(truststore);
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, tmf.getTrustManagers(), null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
The key is the last line. The salesforce connector uses HttpsURLConnection directly to connect to the server, while the HTTPS connector doesn't. This will allow your mule application to use a different trust store for salesforce than it uses for one or more HTTPS connectors. You can use this to control the SSL certificates that your application will trust, independently from the certificates that the server's JVM trusts.

Categories