43

We use Oracle jdk 1.7.0_71 and Tomcat 7.0.55. Unfortunately we started to get the following exception during SSL connection between servers:

javax.net.ssl.SSLHandshakeException: server certificate change is restrictedduring renegotiation

What it means? How to prevent it?

The exception is disappeared after the Tomcat restart.

The full stack:

Caused by: javax.net.ssl.SSLHandshakeException: server certificate change is restrictedduring renegotiation
        at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) ~[?:1.7.0_71]
        at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1884) ~[?:1.7.0_71]
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276) ~[?:1.7.0_71]
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:266) ~[?:1.7.0_71]
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1402) ~[?:1.7.0_71]
        at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:209) ~[?:1.7.0_71]
        at sun.security.ssl.Handshaker.processLoop(Handshaker.java:878) ~[?:1.7.0_71]
        at sun.security.ssl.Handshaker.process_record(Handshaker.java:814) ~[?:1.7.0_71]
        at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1016) ~[?:1.7.0_71]
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312) ~[?:1.7.0_71]
        at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:702) ~[?:1.7.0_71]
        at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:122) ~[?:1.7.0_71]
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82) ~[?:1.7.0_71]
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140) ~[?:1.7.0_71]
        at org.apache.commons.httpclient.methods.EntityEnclosingMethod.writeRequestBody(EntityEnclosingMethod.java:506) ~[commons-httpclient-3.1.jar:?]
        at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2114) ~[commons-httpclient-3.1.jar:?]
        at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096) ~[commons-httpclient-3.1.jar:?]
        at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398) ~[commons-httpclient-3.1.jar:?]
        at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171) ~[commons-httpclient-3.1.jar:?]
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397) ~[commons-httpclient-3.1.jar:?]
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323) ~[commons-httpclient-3.1.jar:?]
        at org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor.executePostMethod(CommonsHttpInvokerRequestExecutor.java:205) ~[spring-web-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor.doExecuteRequest(CommonsHttpInvokerRequestExecutor.java:140) ~[spring-web-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor.executeRequest(AbstractHttpInvokerRequestExecutor.java:136) ~[spring-web-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.executeRequest(HttpInvokerClientInterceptor.java:192) ~[spring-web-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.executeRequest(HttpInvokerClientInterceptor.java:174) ~[spring-web-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.invoke(HttpInvokerClientInterceptor.java:142) ~[spring-web-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        ... 160 more
Michael
  • 10,063
  • 18
  • 65
  • 104

5 Answers5

50

This error message in client layer code is a consequence of code hardening following "SSL V3.0 Poodle Vulnerability - CVE-2014-3566" in recent Java updates. And it is a bug - here are work-arounds in case you cannot update your JRE immediately:

A first option is to force TLS protocol when establishing HTTPS connection:

If you can update HttpClient to a more recent version than 4.3.6, then SSLv3 will be disabled by default and your code should no longer report such exception.

If you cannot upgrade your HttpClient version, you will have to use this answer's code to restrict protocols to TLS: https://stackoverflow.com/a/26439487/737790

For other http access from Java 7 runtime, the following system property must be set

-Dhttps.protocols="TLSv1"

Full details can be found here: Java http clients and POODLE


A second option is to relax client check to still allow renegotiation with the following properties:

-Djdk.tls.allowUnsafeServerCertChange=true 
-Dsun.security.ssl.allowUnsafeRenegotiation=true


A third option is to "improve" your server certificates to include all IP addresses of your cluster members as Subject Alternative Names according to this post in Burp forum


A fourth option is to downgrade your Java version before this certificate/renegotiation checks have been added, so before 7u41 (to be confirmed)

Updates This buggy behaviour is now fixed in JDK updates 7u85 and 8u60. Credits to Pada for having found the JDK-8072385 reference.

Community
  • 1
  • 1
Yves Martin
  • 10,217
  • 2
  • 38
  • 77
  • I am afraid that all these options are bad workarounds for what was supposed to be code change in JIRA and Confluence. It seems that Apache HttpClient 4.3.6 disables SSLv3 by default, which means that JIRA and Confluence should just do the same. BTW, the SO contains more details related to JAVA 8 !!!. – sorin Jan 13 '15 at 14:37
  • Why JIRA and Confluence ? I find out that Burp is also impacted. May it be related to HttpClient or maybe also Spring behavior that changed recently ? – Yves Martin Jan 14 '15 at 07:58
  • 5
    As far as I can tell the code that caused this issue was: http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/rev/eabde5c42157 and now it seems like they have fixed it with: https://bugs.openjdk.java.net/browse/JDK-8072385 So the fix would be included in: 7u85 & 8u60 – Pada Apr 13 '15 at 11:35
  • 2
    I am now running Java 8u60 with `https.protocols="TLSv1"` but a deployed product still face this error. Definitely not fixed yet. – Yves Martin Oct 28 '15 at 07:32
  • I have raised a similar question but I am using java 1.8 and httclient 4.4.1 and using only TLSv1.2. Please see if you can help-http://stackoverflow.com/questions/37821428/server-certificate-change-is-restricted-during-renegotiation-for-tls-1-2-with-ja?noredirect=1#comment63108068_37821428 – Manu Jun 14 '16 at 23:07
  • https.protocols should not contain quotation, so the proper value is: -Dhttps.protocols=TLSv1 – Piotr Idzikowski Feb 05 '18 at 11:35
  • Why not ? I agree quotes are useless here, but should not prevent option to work. Do you notice failures comparing with and without quotes ? – Yves Martin Feb 11 '18 at 06:50
5

Following code piece worked for us in an enterprise environment under the following conditions;

  • seamless (run-time) certificate update is a critical requirement
  • it is too costly to update the HTTPClient used in the application
  • restricting https protocol to "TLSv1" doesn't have effect
  • the application is a JNLP served java client and neither the "allowUnsafeServerCertChange" and "allowUnsafeRenegotiation" are not allowed to be passed to the client application via JNLP arguments (i'm guessing JWS is blocking them due to security reasons)
  • setting the "allowUnsafeServerCertChange" and "allowUnsafeRenegotiation" via System.setProperty() calls during the bootstrap of the application doesn't have effect.

    if (e.getCause() instanceof SSLHandshakeException) {
        logger.debug("server https certificate has been altered");
        try {
            Class<?> c = Class.forName("sun.security.ssl.ClientHandshaker");
            Field allowUnsafeServerCertChangeField = c.getDeclaredField("allowUnsafeServerCertChange");
            allowUnsafeServerCertChangeField.setAccessible(true);
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(allowUnsafeServerCertChangeField, allowUnsafeServerCertChangeField.getModifiers() & ~Modifier.FINAL);
            allowUnsafeServerCertChangeField.set(null, true);
            logger.debug("client has been updated in order to support SSL certificate change (re-negotiation) on runtime.");
        }
        catch (Exception ex) {
            logger.debug("client cannot be updated to support SSL certificate change (re-negotiation) on runtime. Please restart the application.", ex);
        }
    }
    

Please note that this should be considered as a hack (introducing a vulnerability) and should be used in a trusted environment. One should try all the options in Yves' answer before going down this path.

Mert Z.
  • 553
  • 1
  • 8
  • 15
2

This can also be due to misconfigured connectivity, such as an haproxy with one or more load-balance targets pointing to the wrong IP address, so that X percent of requests get a different certificate.

nbryant
  • 31
  • 1
  • I agree. But in my context, it is relative to LDAP/SSL ActiveDirectory endpoints in cluster, and administrators seem reluctant to deploy the same certificate on both endpoints manually because it breaks automatic renewal mechanism. But you're right, a 10-year certificate with correct alternate names is the best option. – Yves Martin Oct 28 '15 at 07:36
2

I got this problem because the server side updated their certificates. Before this our client was working fine. We simply restarted our program & it went back to normal again.

Charlie
  • 639
  • 9
  • 19
0

I had this issue and it turned out to be that the client calling the service with a different cert behind a load balancer.

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
John
  • 316
  • 4
  • 6