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)
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:
-
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
-
Download the policy files files at https://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
-
Uncompress it. It should contain a
README.txt
,local_policy.jar
andUS_export_policy.jar
-
Copy
local_policy.jar
andUS_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
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.