Java Sslhandshakeexception "No Cipher Suites in Common"

Java SSLHandshakeException no cipher suites in common

You're initialising your SSLContext with a null KeyManager array.

The key manager is what handles the server certificate (on the server side), and this is what you're probably aiming to set when using javax.net.ssl.keyStore.

However, as described in the JSSE Reference Guide, using null for the first parameter doesn't do what you seem to think it does:

If the KeyManager[] parameter is null, then an empty KeyManager will
be defined for this context. If the TrustManager[] parameter is null,
the installed security providers will be searched for the
highest-priority implementation of the TrustManagerFactory, from which
an appropriate TrustManager will be obtained. Likewise, the
SecureRandom parameter may be null, in which case a default
implementation will be used.

An empty KeyManager doesn't contain any RSA or DSA certificates. Therefore, all the default cipher suites that would rely on such a certificate are disabled. This is why you get all these "Ignoring unavailable cipher suite" messages, which ultimately result in a "no cipher suites in common" message.

If you want your keystore to be used as a keystore, you'll need to load it and initialise a KeyManagerFactory with it:

    KeyStore ks = KeyStore.getInstance("JKS");
InputStream ksIs = new FileInputStream("...");
try {
ks.load(ksIs, "password".toCharArray());
} finally {
if (ksIs != null) {
ksIs.close();
}
}

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
.getDefaultAlgorithm());
kmf.init(ks, "keypassword".toCharArray());

The use kmf.getKeyManagers() as the first parameter to SSLContext.init().

For the other two parameters, since you're visibly not requesting client-certificate authentication, you should leave the trust manager to its default value (null) instead of copying/pasting a trust manager that's a potential cause of vulnerability, and you can also use the default null SecureRandom.

SSLHandshakeException: no cipher suites in common

javax.net.ssl.SSLHandshakeException: no cipher suites in common

This has two causes:

  1. The server doesn't have a private key and certificate, and possibly doesn't have a keystore at all. In such a case it can only use the insecure anonymous cipher suites, which are disabled by default, and should stay that way. So there is no cipher suite that it can agree to use with the client.

  2. Excessive restrictions on cipher suites imposed by client or server or both such that there can be no agreement.

Re your keystores and truststores, that all looks OK except that you are doing four import steps where you only need two. You don't need to import the server's certificate into the server's own truststore, or the client's certificate into the client's truststore. You only need this:

Server:

$ keytool -import -v -trustcacerts -alias clientkey -file ../client/client.cer -keystore cacerts.jks -keypass p@ssw0rd -storepass p@ssw0rd

Client:

$ keytool -import -v -trustcacerts -alias serverkey -file ../server/server.cer -keystore cacerts.jks -keypass changeit -storepass changeit

and you only need it because you're using a self-signed certificate. Simple solution: don't. Use a CA-signed certificate, which is trusted by the default truststore shipped with Java.

Java SSLHandshakeException: no cipher suites in common

The keystore for each authenticated party, always the server and here the client also because you specified NeedClientAuth, must have the PRIVATE KEY AND certificate(s), not merely the certificate(s). There are two ways to do this:

  • convert the OpenSSL generated privatekey plus the related certs to PKCS#12, and then either convert the PKCS#12 to JKS or just use the PKCS#12 in Java (JCE can handle it, and recent versions of Java8 even if you specify JKS! -- see http://www.oracle.com/technetwork/java/javase/8u60-relnotes-2620227.html under Keystore Compatibility Mode). See:

    How to import an existing x509 certificate and private key in Java keystore to use in SSL?

    How can i create keystore from an existing certificate (abc.crt) and abc.key files?

    Importing the private-key/public-certificate pair in the Java KeyStore

    convert certificate from pem into jks (disclosure: mine)

    How to create keystore from cer files (disclosure: mine)

  • generate EE privatekey and CSR in Java, then use OpenSSL (with CA key and cert) to issue the EE cert, and import the certs back into the Java keystore:

    keytool -keystore server.jks -genkeypair -keyalg RSA 
    # before j7 best to add -keysize 2048 see below
    keytool -keystore server.jks -certreq >server.csr
    openssl ca -in server.csr ... -out server.crt
    # or submit the CSR to a real CA and get its response

    # then either install the chain all at once:
    cat server.crt ca.crt >temp
    keytool -keystore server.jks -importcert -file temp
    # and confirm (need temp so stdin available for confirm;
    # if using a public CA, can add -trustcacerts and use pipe instead)

    # or install the certs separately, top down:
    keytool -keystore server.jks -importcert -file ca.crt -alias ca
    # and confirm, THEN
    keytool -keystore server.jks -importcert -file server.crt

    # (last) response must be 'Certificate reply was installed'
    # NOT merely 'Certificate was added' which means you messed up

    # and similarly for client

The latter method (separate entries for ca.crt and privatekey+server.crt) has the advantage this same file can be used as both the keystore and truststore, you don't need to use serverkey as clienttrust and vice versa. If these were real systems, this would be a security benefit.

A final note: you should start using RSA 2048-bit keys. 1024-bit has been prohibited by authorities like NIST and CABforum since 2014, and although Java still accepts them, most browsers and many other tools are already warning for them and likely soon will reject them. For similar reasons you should sign the certificates with at least SHA256 -- this can be set in the config file used by ca or you can just use the commandline flag -md sha256.

SSLHandshakeException:no cipher suites in common

This turned out to be an Application Context issue. I was defining the server SSL configuration beneath my src/test/java. One of my Autowired fields was pulling a client SSL configuration bean from src/main/java, not the server bean I thought it was. Hence the double ClientHellos.

Added a couple @Qualifiers to make sure my server config was being Autowired where I thought it was

javax.net.ssl.SSLHandshakeException: no cipher suites in common no cipher suites in common

You will have to use SSLContext for this purpose. Check out the sample code which I implemented in one of my applications below. Client context means you become the client and call some back end. Server context means you accept the client requests.

public class SSLUtil {
private static String KEY_STORE_TYPE = "JKS";
private static String TRUST_STORE_TYPE = "JKS";
private static String KEY_MANAGER_TYPE = "SunX509";
private static String TRUST_MANAGER_TYPE = "SunX509";
private static String PROTOCOL = "TLS";

private static SSLContext serverSSLCtx = null;
private static SSLContext clientSSLCtx = null;

public static SSLContext createServerSSLContext(final String keyStoreLocation,
final String keyStorePwd)
throws KeyStoreException,
NoSuchAlgorithmException,
CertificateException,
FileNotFoundException,
IOException,
UnrecoverableKeyException,
KeyManagementException {
if (serverSSLCtx == null) {
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
keyStore.load(new FileInputStream(keyStoreLocation), keyStorePwd.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KEY_MANAGER_TYPE);
keyManagerFactory.init(keyStore, keyStorePwd.toCharArray());
serverSSLCtx = SSLContext.getInstance(PROTOCOL);
serverSSLCtx.init(keyManagerFactory.getKeyManagers(), null, null);
}

return serverSSLCtx;
}

public static SSLContext createClientSSLContext(final String trustStoreLocation,
final String trustStorePwd)
throws KeyStoreException,
NoSuchAlgorithmException,
CertificateException,
FileNotFoundException,
IOException,
KeyManagementException {
if (clientSSLCtx == null) {
KeyStore trustStore = KeyStore.getInstance(TRUST_STORE_TYPE);
trustStore.load(new FileInputStream(trustStoreLocation), trustStorePwd.toCharArray());
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TRUST_MANAGER_TYPE);
trustManagerFactory.init(trustStore);
clientSSLCtx = SSLContext.getInstance(PROTOCOL);
clientSSLCtx.init(null, trustManagerFactory.getTrustManagers(), null);
}

return clientSSLCtx;

}

}

Finally make sure you import the trusted server certificate to the client key store. Literally server and client should have different key stores. The key store used in the client side is referred to as client trust store since we are trusting the server certificate here. This article may help.

Java - no cipher suites in common

The good news is that your code is getting a far as connecting with an SSL server, and attempting an SSL handshake.

One of the first things that happens in an SSL handshake, is that the two sides try to agree on a mutually acceptable cipher suite. A cipher suite is a named combination of authentication, encryption, message authentication and key exchange algorithms.

Typically the server has a list of cipher suites it understands and considers acceptably secure, and the client has another list.

For example, an OpenSSL server configured to only support TLS1 and above, might have this cipher suite list:

ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384
ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1
ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1
SRP-DSS-AES-256-CBC-SHA SSLv3 Kx=SRP Au=DSS Enc=AES(256) Mac=SHA1
SRP-RSA-AES-256-CBC-SHA SSLv3 Kx=SRP Au=RSA Enc=AES(256) Mac=SHA1
SRP-AES-256-CBC-SHA SSLv3 Kx=SRP Au=SRP Enc=AES(256) Mac=SHA1
DHE-DSS-AES256-GCM-SHA384 TLSv1.2 Kx=DH Au=DSS Enc=AESGCM(256) Mac=AEAD
DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH Au=RSA Enc=AESGCM(256) Mac=AEAD
DHE-RSA-AES256-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AES(256) Mac=SHA256
DHE-DSS-AES256-SHA256 TLSv1.2 Kx=DH Au=DSS Enc=AES(256) Mac=SHA256
DHE-RSA-AES256-SHA SSLv3 Kx=DH Au=RSA Enc=AES(256) Mac=SHA1
DHE-DSS-AES256-SHA SSLv3 Kx=DH Au=DSS Enc=AES(256) Mac=SHA1
DHE-RSA-CAMELLIA256-SHA SSLv3 Kx=DH Au=RSA Enc=Camellia(256) Mac=SHA1
DHE-DSS-CAMELLIA256-SHA SSLv3 Kx=DH Au=DSS Enc=Camellia(256) Mac=SHA1
ECDH-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AESGCM(256) Mac=AEAD
ECDH-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AESGCM(256) Mac=AEAD
ECDH-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AES(256) Mac=SHA384
ECDH-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AES(256) Mac=SHA384
ECDH-RSA-AES256-SHA SSLv3 Kx=ECDH/RSA Au=ECDH Enc=AES(256) Mac=SHA1
ECDH-ECDSA-AES256-SHA SSLv3 Kx=ECDH/ECDSA Au=ECDH Enc=AES(256) Mac=SHA1
AES256-GCM-SHA384 TLSv1.2 Kx=RSA Au=RSA Enc=AESGCM(256) Mac=AEAD
AES256-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA256
AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1

... etc. (there are 80 in total on my system)

The client tells the server which ones it supports. The server picks one that's on its list of supported cipher suites, and responds telling the client which one to use.

You have chosen to configure your client with a list of only one cipher suite. It seems that the server does not support TLS_RSA_WITH_AES_128_CBC_SHA and therefore the server has responded with a handshake failure, including a useful message: no cipher suites in common.

It's not a good idea to specify just one cipher suite, because the fewer you allow, the less likely it is that it'll be in the server's list.

Unless you have a good reason to specify cipher suites, you should probably leave your client to use the default list.

getSupportedCipherSuites() will provide you all the cipher suites Java can support -- don't enable all of these, as many are disabled by default for good reason.

getEnabledCipherSuites(), called before you set any, will give you the defaults. If the defaults aren't fit for your needs, you could start with these defaults and add/remove as necessary.


The file containing your private key is as safe as any other file -- it won't get leaked via the SSLServerSocket. You should think about other ways it might get read -- make sure it's read-protected, and so on. Consider making it encrypted, and then think about how to protect the password. There's a balance to be chosen here between convenience, cost and security. Some systems require a human to type in the keyfile password every time the system starts. Other systems accept the risk, and have a password file or an unencrypted keyfile (trusting OS file permissions, physical and network security).



Related Topics



Leave a reply



Submit