I am trying to send emails from my application via TLS-SMTP.
This works when run locally (Tomcat7, Java7, Windows) but not in production (Tomcat7, Java6, Linux).
The trust store containing the public cert of the SMTP server is shipped with the application, set manually via
System.setProperty("javax.net.ssl.trustStore", "pathToJssecacerts")
and is identical in both cases.
I verified this using
System.getProperty("javax.net.ssl.trustStore")
just before the mail is sent which returns an absolute path pointing to the store in the respective environment.
Thus, to my knowledge, both application environments use exactly the same trust store. (Is there a way to be definitely sure?)
I'm using
System.setProperty("javax.net.debug", "ssl:handshake:trustmanager");
to get some more insight but the output differs considerably between the two environments, probably due to the differences between Java6 and Java7.
The error in production (Java6) reads:
...
SEND TLSv1 ALERT: fatal, description = certificate_unknown
WRITE: TLSv1 Alert, length = 2
called closeSocket()
handling exception: javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
So either there is a flaw in my reasoning, a bug, or there is another thing here that I haven't considered so far. Any ideas?
This message means that Java is unable to build a chain of trust using your certificate.
As a test you can try setting the property
System.setProperty("mail.smtp.ssl.trust", "*");
This property will override the default behaviour and trust all certificates.
Note: this is not recommended in production.
Related
I have a local environment with WebLogic 10.3.4 and and .ear app deployed on it. This app must communicate with external services via REST APIs. These external services are exposed in https and use wildcard certificates.
I receive the following exceptions when I try to connect to to one of these services.
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://dds-service.domain.com" General SSLEngine problem; nested exception is javax.net.ssl.SSLHandshakeException: General SSLEngine problem [...]
[...] Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target [...]
[...] Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
So what I tried in first instance was to open the same url the app tries to connect to in my browser, download its wildcard certificate (.cer Base64 encoded) and with key tool import it into the jvm's trust store that WebLogic loads and looks up when trying to validate a cert. I am sure is the correct one because i imported other certificates that caused the same error and also because of this log
<Loading trusted certificates from the jks keystore file C:\WEBLOG~1\wls\JROCKI~1.1-3\jre\lib\security\cacerts.>
At this point, I suppose the problem is related to the way I import the wildcard certificate in WebLogic. I tried to look for different ways to do it but, like this one, require a .pfx file that is not currently available to me at the moment.
Do I need a .pfx to solve this or is there another way?
If someone will ever have the same problem, here is the solution i found: it appears that WLS 10.3.x has issues in trusting certificates wth keys longer than 128 bits, this is what caused the problem in first instance. That said, 2 actions solved my problem
-DUseSunHttpHandler=true added as a VM argument in the setDomainEnv script
Enable JSSE SSL via WLS adminn console (Environment > Servers > server name > Configuration > SSL > Advanced > check the JSSE SSL box)
Click Save, and restart WLS server.
I have an Apache web server fronting a Tomcat 8 web server that is running my website, and I'm switching the top-level domain from my.website.ie to my.website.com. I have some code that runs in response to a particular request that generates a PDF. That code fetches an image (using a URL) which is served from the same web server e.g.
Image.getInstance(new URL("https://my.website.com/img/myimage.png"))
In addition to the domain change, I'm also changing my SSL certificate provider to LetsEncrypt (free SSL certs). My development website at the new .com domain is running and the certificate is valid and does not expire for a number of months.
I have another development server running on a separate machine that is still using the .ie domain. The Tomcat codebase running on both of these servers are identical right now. They are both trying to fetch the image at the URL shown above in that particular piece of code.
On the .ie server, the request that generates the PDF works correctly, without any problems fetching the image. On the .com server, the request fails with this error:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
My understanding of this error is that the certificate at the target URL is not trusted (e.g. self-signed), but that is not true in this case. Also, both of the servers are hitting the same URL for the image, so why is one server trusting the certificate and the other not?
I haven't made any additional configuration changes on the .ie server that I haven't made on the .com server (with respect to setting up the new certificate), so is there some other (mis)configuration that I haven't considered?
Not trusted means the used CA is not trusted by the software.
Self-signed certificates are never trusted.
Java has it's own trust store (only on Linux the system trust store is used AFAIR?). If the CA certificate is newer than the used Java version it may happen that Java does not trust the CA. Conclusion: Update your Java.
According to this Stackoverflow answer you need at least Java 8u101 for Let's Encrypt support:
Does Java support Let's Encrypt certificates?
The error says that the chain doesn't lead to a root certificate that is trusted. Trusted CA root certificates are stored in Java's root keystore where the root certificate issued by Let's Encrypt is obviously missing.
You can add the root certificate manually to the store or check if newer versions of Java already contain the certificate.
I have 2 fresh machines with the same version of JDK7 installed.
And on one of the machines I can successfully connect to the remote host from my Java application via HTTPS without any issues and without importing any certificates to the default 'cacerts' keystore.
However on the other machine I'm getting the subj error.
JDKs on both machines are absolutely the same. The only difference between machines is that one of them is located in Europe, and one in the USA (this is the one where I'm getting the error).
Does anybody have a clue to what could cause this error in my case?
UPDATE
Certificate of the host I'm trying to connect to is issued by QuoVadis CA, which by default has the appropriate record in cacerts in Java distibution.
UPDATE2
Looks like the problematic machine for some reason doesn't receive the whole certificate chain from the machine to which it tries to connect (I can see only the last certificate from the chain and right after that follows a message certificate_unknown). So it doesn't get the root certificate.
Besides that the only difference I can see in the log is that working machine uses TLS_RSA_WITH_AES_128_CBC_SHA cipher suite and not working machine uses TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA cipher suite.
Could this be a reason of my problem? (I also cannot understand why they use different cipher suites while JDKs are the same and they are connecting to the same machine)
Try to compare the keystore certificates once. I had a similar issue where one machine had other applications which added the same certificates and it was working fine, but a stand alone machine was not.
I have configured my Tomcat instance to use SSL on port 8443. I've verified it's working by hitting the main tomcat page thru https:8443 on my browser.
Now I'm trying to understand what I need to do to get a Java program to read from an HTTPS URL on that tomcat server. I followed the instructions here:
Java SSL Tutorial
I just copied the .keystore file down to my client that I generated with Java's keytool on my web server. It is self signed, just for dev work. This seemed a little weird to me since that also has the private key, right? I thought I would do something to export the public key and put that on my client, but I can't find a good guide on what steps I need to for that.
Anyway, when I tried using the .keystore generated on my server in my client, I get this error:
***
%% Invalidated: [Session-1, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA]
main, SEND TLSv1 ALERT: fatal, description = certificate_unknown
main, WRITE: TLSv1 Alert, length = 2
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
I've set my client up to run with these JVM args:
-Djavax.net.ssl.keyStore=.keystore -Djavax.net.ssl.keyStorePassword=changeit -Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol -Djavax.net.debug=ssl
It does print out a bunch of stuff in that .keystore, so I'm sure it is reading it. But the server doesn't seem to like it when it tries to handshake.
What do I need to do to get that SSL Java client reading data from my webserver?
------- edit
Oops, I just noticed I was using the wrong JVM args for client. I changed to this and now it seems get further.
-Djavax.net.ssl.trustStore=.keystore -Djavax.net.ssl.trustStorePassword=changeit
I still haven't gotten it to read URL data yet. And I'm still wondering how to just give the client the public key so it can do its decryption instead of the entire server keystore.
---------- edit #2
Finally got it working. Had a couple of roadblocks along the way:
For some reason I had to make my URLs like this in Eclipse:
URL myurl = new URL("https", host, port, "/docs/setup.html", new sun.net.www.protocol.https.Handler());
HttpsURLConnection con = (HttpsURLConnection)myurl.openConnection();
Otherwise I'd get a class cast exception on the second line.
And, I had to regenerate my server .keystore file and copy to my client with an alternate name like this:
keytool -genkey -alias tomcat -keyalg RSA -ext san=ip:<my server ip>
I'd still like to gain a better understanding of what's needed on the client rather than the whole keystore, but at least I can play around with it now.
Java "keystore" files are used to serve two conceptually different purposes. One purpose is to serve as a key store, which is where one stores key pairs used to prove the machine's own identity. The other purpose is to serve as a trust store, which is used to store information used to identify other machines that one trusts.
You shouldn't copy the server's keystore file onto a client, since as you say it contains the private key of the server, which it contains because the file is a key store for the server. Rather, you want to create your own keystore file that serves as a trust store for the client, in which you want to import the server's certificate so your client will know to trust the server. To do that, you export a certificate from the server's keystore, and then import that certificate into the client's keystore file.
Some more detailed information appears in my related answer to this question:
Secret Key SSL Socket connections in Java
I have a webapp that sends a SOAP request to a 3rd party server. When the request is made on my local computer it works without a problem, but when I deploy my application to my server I get an error with the following causes:
com.sun.xml.messaging.saaj.SOAPExceptionImpl: java.security.PrivilegedActionException: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Message send failed
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: algorithm check failed: MD2withRSA is disabled
I've searched around but I can't find anything relevant to my situation. It's probably worth noting that the request I'm making is to an https url. My computer is running Windows XP and the server is running Slackware Linux. Any ideas what might be causing the server to reject the request?
Check your Java versions on your local machine, and your server.
From here, it seems the jvm 6u17 disabled MD2 as it is insecure, and whatever you connect to is using MD2
MD2withRSA is highly vulnerable and therefore deactivated in Sun... aeh Oracle's JVM. You should ask the owner of the remote service, whether his server supports more secure encryption methods (I think, older Apache HTTPd versions do offer MD2withRSA by default...). In order to resolve this problem without forcing the provider to change the method, you may use your own implementation of the X509TrustManager that accepts the old method.
A Google search on "MD2withRSA" showed this URL as the first hit, that seems to point to a change in a certain Java version. Probably the verasiuons on your local computer and the server do not match.
Newer Java 7 (version 1.7) allows re-enabling MD2 via $JAVA_HOME/jre/lib/security/java.security file. Download and install Java 7 and modify java.security file in text editor as follows
1) Remove MD2 from following property
jdk.certpath.disabledAlgorithms= # MD2
2) Ensure following property is commented out
# jdk.tls.disabledAlgorithms=MD5, SHA1, DSA, RSA keySize < 2048
3) Restart java application
Warning: MD2 is disabled by default in Jdk 7 because it is insecure. However, it can be enabled as described above to support older deployments.