SSL handshake failures due to incompatible Cipher Suite

Article ID:115003785531
3 minute readKnowledge base

Issue

  • The JDK of the server uses strong algorithms not included by default in the JDK being used

  • The connection from a Jenkins controller to Operations Center fails

  • The connection from an Agent to a Jenkins controller fails

  • The stacktrace shows:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
    at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2033)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1135)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)
    at hudson.remoting.Launcher.parseJnlpArguments(Launcher.java:479)
    at hudson.remoting.Launcher.run(Launcher.java:316)
    at hudson.remoting.Launcher.main(Launcher.java:277)

Environment

Resolution

This exception can be caused by different issues. But if you are using a strong cryptographic algorithm - typically AES 256 - and a JDK with a limited list of algorithms then this is most likely the problem.

A typical example is the Oracle JDK that do not include strong algorithm by default due to import regulations in some countries. This is explained in documentation of Java Cryptography Architecture Oracle Providers. In order to fix this, you need to install the JCE Unlimited Strength Policy.

The JCE Unlimited Strength Policy

It is a legacy from the time when the export of strong cryptography was prohibited / regulated. For these reasons the Oracle implementation of Java limits the strength of cryptographic algorithms by default. This has changed recently and starting from JDK 8u162, this JCE is included by default - see [JDK-8170157].

Images used by CloudBees Jenkins Enterprise uses the OpenJDK or are based on the openjdk image which contains strong ciphers and are not restricted by default.

How to install the JCE Unlimited Strength Policy

The policy is composed of jar files. These files must replace the one provided by default:

  1. Backup the files $JAVA_HOME/jre/lib/security/local_policy.jar and $JAVA_HOME/jre/lib/security/US_export_policy.jar:

     mv $JAVA_HOME/jre/lib/security/local_policy.jar $JAVA_HOME/jre/lib/security/local_policy.jar.default
     mv $JAVA_HOME/jre/lib/security/US_export_policy.jar $JAVA_HOME/jre/lib/security/US_export_policy.jar.default
  2. Download the policy files files at https://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html

  3. Uncompress it. It should contain a README.txt, local_policy.jar and US_export_policy.jar

  4. Copy local_policy.jar and US_export_policy.jar to $JAVA_HOME/jre/lib/security/

The application needs to be restarted so that changes are taken into account.

Troubleshooting

In order to troubleshoot this, you need to ensure that there is an overlap between the list of ciphers suite of the client and the server.

Check the ciphers suites of the Server

To check what cipher suites is / are used by the server, you can use curl with the following command:

$ curl -IvL $JENKINS_URL

Another solution is to use nmap and run the following command:

$ nmap --script ssl-enum-ciphers -p $HTTPS_PORT $SERVER_FQDN

where $SERVER_FQDN is the server endpoint, for example jenkins.example.com and $HTTPS_PORT the port that the server uses for HTTPS.

Check the cipher suite of the Client

SSL Client is Jenkins

If the client is Jenkins - for example the issue a Jenkins controller not able to connect to CJOC or another service - you can run the following groovy script under Manage Jenkins  Script Console to display the list of cipher suites:

import javax.net.ssl.SSLServerSocketFactory

def ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault()
def defaultCiphers = ssf.getDefaultCipherSuites()
def availableCiphers = ssf.getSupportedCipherSuites()

def ciphers = new TreeMap();

availableCiphers.each { cipher ->
    ciphers.put(cipher, Boolean.FALSE)
}

defaultCiphers.each { cipher ->
    ciphers.put(cipher, Boolean.TRUE)
}

println "Default\tCipher"
ciphers.each { k, v ->

    if (Boolean.TRUE == v) {
        print '*'
    } else {
        print ' '
    }
    print '\t'
    println k
}
return

SSL Client is not Jenkins

If the SSL client is not Jenkins - for example a Jenkins agent not able to connect to a Jenkins controller - the best way to check the cipher suite is to reproduce the issue with SSL debug enabled. You can enable SSL debug by adding the system property -Djavax.net.debug=ssl:handshake when running the application. When reproducing the issue, in the debug output you should see the following:

[...]
*** ClientHello, <protocol>
RandomCookie:  ...
Session ID:  ...
Cipher Suites: [<list of ciphers>]

Another solution is to use a simple program like the Ciphers.java from that link and run the following:

$ javac Ciphers.java
$ java Ciphers

This program lists the ciphers available on the client exactly like the above groovy script does.