I have two laptops running the same project on them. Both instances are configured to use Java JDK 6 (same minor version). However one machine is not able to connect to a remote REST service. By running applications with -Djavax.net.debug=all option I came to the conclusion that the issue is with the ClientKeyExchange step as this step does not happen on the problematic laptop.
A post in the IBM developer community suggests that "hardware crypto device being used is not on the supported list". However I have no understanding about "hardware crypto devices".
What I can tell is that "enabled cipher suites", "excluded cipher suites", "cipher suites have been set to" lists printed by means of -Djavax.net.debug=all are the same on both machines.
Using JDK cacerts.
Found the solution. Project is using JAX-RS so calls to external service happened through that framework and because of that I could not dig to the problem since there were no errors. So I tried different approach: tried to access the service by using simple javax.net.ssl.HttpsURLConnection which showed me java.lang.NoClassDefFoundError: Could not initialize class javax.crypto.SunJCE_b and therefore I changed my local_policy.jar and US_export_policy.jar files (thanks to #Charlie Could not initialize class javax.crypto.SunJCE_b)
I think I had changed policy jars on first laptop and forgot to do this on the new laptop.
Related
I have been surprised to observe that the code ClientBuilder.newBuilder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS).build().target("https://www.oracle.com/java/technologies/naming-conventions.html").request().get(); throws a “Read timed out” exception using the Jersey JAX-RS client implementation. (When I tried to report this, I was told that it is not a bug.)
I expected that Jersey would use the default Java truststore, and that the default Java truststore would include CA certificates that permit to trust Oracle’s (and many other) website.
Indeed, running keytool -list -cacerts, I see that my truststore contains 166 certificates. I suppose that some of them permit to validate Oracle’s website, but I do not know how I can check that (well, apart from doing exactly what I am trying to do with Jersey).
I realize that I can download Oracle’s certificate and add it to my truststore, but I obviously do not want to do this for every classical web site out there to which I would like to connect using SSL. I would rather like to understand which web sites are supposed to be trusted by default in a “normal” Java installation (is there an official documentation about this somewhere?); how I can check whether my specific installation has some problem that prevents Oracle’s web site to be trusted; or, if it is normal, whether I can tell Java once and for all to trust the web sites that, say, Firefox or Chromium would trust by default.
This answer, for example, suggests that OpenJdk (which is what I use) should trust by default the same web sites as Firefox. And Firefox obviously trusts Oracle.
What am I missing?
Edit I realize it seems relevant to specify that I use Debian stable, as this issue might be OS-specific. I wonder if there’s something going wrong on my installation. “As far as I'm aware every distro patches OpenJDK to use its own list, which I guess is why this issue hasn't got much attention on GNU/Linux systems” -- Andrew Haley on OpenJDK ML. “Debian has tooling to create a cacert file from the system‘s keystore. There is a hook system that updates the cacert every time the system‘s keystore is changed. lib/security/cacert is actually a symlink to that file. (…) In case of Debian, it‘s identical with Mozilla‘s list.” -- aahlenst on GH Adoptium issue. Indeed: ls -l /usr/lib/jvm/default-java/lib/security/cacerts reveals that it links to
/etc/ssl/certs/java/cacerts.
As per the firm's policy we are required to encrypt communication channels b/w our client processes with MQ server. In our distributed system, we have a few Java processes required to establish connection with MQ. Let me say, process A and B.
A and B run on different virtual machines. They have EXACTLY the same environment including JRE of same version(1.8.0_151-b12), same dependencies, same JCE extension files. We added ssl related JVM parameters to the launching scripts of each process, such as
-Djavax.net.ssl.trustStore
-Djavax.net.ssl.trustStorePassword
-Djavax.net.ssl.keyStore
-Djavax.net.ssl.keyStorePassword
the corresponding trustStore and keyStore files are in place and properly referred to.
A works fine and we observed the SSL handshake and encrypted messages. However, process B failed with messages such as:
ignoring unavailable cipher TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
...
Caused by: java.lang.IllegalArgumentException: Cannot support
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA with currently installed providers
I did spend time checking online and one relevant post is from here : it suggested downloading and upgrading JCE files - the thing is that I already have JCE files (unlimited) installed and those are the same for both processes. I don't think JCE files is the root of the problem.
My question is, does the MQ team also have to install JCE files as well, in order for the client to establish SSL connection? Is the configuration on my end completed, anything I might have missed?
Update: problem solved. See my comments below.
Unlimited Strength Jurisdiction Policy Files were not included and enabled (crypto.policy set to unlimited) by default until 1.8.0_162. At 1.8.0_151 Unlimited Strength Jurisdiction Policy Files were included but not enabled.
Please check that you in fact running Java 1.8.0_151-b12 on both VMs, if you are then make sure the java security file crypto.policy setting on the working VM is identical to the non-working VM.
On October 19th, I upgraded to OpenJDK Java 1.8.0_232 and my Java processes started failing to connect to a service fronted by stunnel using an EC server certificate. I have an EC client certificate that I would present to the server if the initial handshake would succeed, but it's not getting that far.
I was unable to obtain the version number of the previous version of Java that was running -- I had a long-running process on the client that was able to connect to the service successfully. Java has been upgraded but that client hadn't been restarted since the upgrade. I restarted the client and it, too, began to fail.
I'm about to start reading the announcement to see if there is anything in there that would suggest a "fix" to something, but I believe I have everything configured correctly on both client and server. My current thinking is that this is a bug in the JVM.
Using ssltest, I have been able to confirm that I can handshake with the server using multiple cipher suites when providing the correct client certificate, as long as I use Java 1.8.0_181 or Java 11.0.3 (these are the two versions I happened to have laying around on my laptop for testing). Using the same files, command-line, etc. fails when using Java 1.8.0_232.
Has anyone seen anything like this?
UPDATE
I have downloaded x86-64 OpenJDK versions 8u222 and 8u232 from here and I can confirm that version 8u222 will connect and 8u232 will not connect.
UPDATE
Downgrading to the previous version of OpenJDK has solved my problem for the time being. I'm using Debian Stretch, and I was able to downgrade with this command:
$ sudo apt-get install openjdk-8-jdk-headless=8u222-b10-1~deb9u1 openjdk-8-jre-headless=8u222-b10-1~deb9u1
Note that I only have the "headless" packages installed, so I only downgraded the "headless" packages.
UPDATE
I can confirm that the client certificate is a red herring, here. I relaxed the requirements on my server to not require the client certificate and the initial TLS handshake still fails. I'm trying to narrow this down to the simplest test case I can get.
UPDATE
Still trying to diagnose the problem, here. I have a test server where I can launch it under various configurations and see what happens. I have determined that while Java 8u222 supports the secp256k1 curve, Java 8u232 does not, and I get the handshake failure.
The secp256k1 curve has been disabled with jdk 8u232 (see https://java.com/en/download/faq/release_changes.xml). It can be re-enabled with the java system property jdk.tls.namedGroups.
The example on the faq lists a few other Obsolete NIST EC Curves that are disabled as well. The curves listed there are sect283k1, sect283r1, sect409k1, sect409r1, sect571k1, sect571r1 and secp256k1. The system property jdk.tls.namedGroups takes a comma separated list of those names.
Amazon "upgraded" the SSL security in its AWS Java SDK in the 1.3.21 version. This broke access any S3 buckets that have periods in their name when using Amazon's AWS Java API. I'm using version 1.3.21.1 which is current up to Oct/5/2012. I've provided some solutions in my answer below but I'm looking for additional work arounds to this issue.
If you are getting this error, you will see something like the following message in your exceptions/logs. In this example, the bucket name is foo.example.com.
INFO: Unable to execute HTTP request: hostname in certificate didn't match:
<foo.example.com.s3.amazonaws.com> != <*.s3.amazonaws.com>
OR <*.s3.amazonaws.com> OR <s3.amazonaws.com>
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:220)
at org.apache.http.conn.ssl.StrictHostnameVerifier.verify(StrictHostnameVerifier.java:61)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:149)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:130)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:390)
You can see documentation of this problem on the AWS S3 discussion forum:
https://forums.aws.amazon.com/thread.jspa?messageID=387508񞦴
Amazon's response to the problem is the following.
We should be able to fix this by using the older path style method of bucket addressing (instead of the newer virtual host style addressing) for buckets with this naming pattern. We'll get started on the fix and ensure that our internal integration tests have test cases for buckets names containing periods.
Any workaround or other solutions? Thanks for any feedback.
Original: October 2012
Turns out that Amazon "upgraded" the SSL security on S3 in late September 2012. This broke access any S3 buckets that have periods in their name when using Amazon's AWS Java API.
This is inaccurate. S3's SSL wildcard matching has been the same as when S3 launched back in 2006. What's more likely is that the AWS Java SDK team enabled stricter validation of SSL certificates (good), but ended up breaking bucket names that have been running afoul of S3's SSL cert (bad).
The right answer is that you need to use path-style addressing instead of DNS-style addressing. That is the only secure way of working around the issue with the wildcard matching on the SSL certificate. Disabling the verification opens you up to Man-In-The-Middle attacks.
What I don't presently know is if the Java SDK provides this as a configurable option. If so, that's your answer. Otherwise, it sounds like the Java SDK team said "we'll add this feature, and then add integration tests to make sure it all works."
Update: October 2020
AWS has announced that path-style addressing is deprecated will be going away in the near-future. AWS’ advice is to use DNS-compatible bucket names, which means no periods (among a few other things). Certain newer features of S3 require DNS-compatible bucket names (e.g., accelerated transfer).
If you require a bucket name which contains periods (which will also be disallowed for new buckets in the near future), my best advice is to put a CloudFront distribution in front of it if you want to hit it over HTTPS.
Amazon released version 1.3.22 which resolves this issue. I've verified that our code now works. To quote from their release notes:
Buckets whose name contains periods can now be correctly addressed again over HTTPS.
There are a couple of solutions that I can see, aside from waiting till Amazon releases a new API.
Obviously you could roll back to 1.3.20 version of the AWS Java SDK. Unfortunately I needed some of the features in 1.3.21.
You can replace the org.apache.http.conn.ssl.StrictHostnameVerifier in the classpath. This is a hack however which will remove all SSL checking for Apache http connections I think. Here's the code that worked for me: http://pastebin.com/bvFELdJE
I ended up downloading and building my own package from the AWS source jar. I applied the following approximate patch to the HttpClientFactory source.
===================================================================
--- src/main/java/com/amazonaws/http/HttpClientFactory.java (thirdparty/aws) (revision 20105)
+++ src/main/java/com/amazonaws/http/HttpClientFactory.java (thirdparty/aws) (working copy)
## -93,7 +93,7 ##
SSLSocketFactory sf = new SSLSocketFactory(
SSLContext.getDefault(),
- SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
+ SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
The right fix is to change from domain-name bucket handling to path based handling.
Btw, the following seems like it might work but it does not. The AWS client specifically requests the STRICT verifier and does not use the default one:
SSLSocketFactory.getSystemSocketFactory().setHostnameVerifier(
SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
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.