No cipher suites in common while establishing a secure connection
Asked Answered
R

1

7

I'm trying to establish a secure connection between two Java projects but I'm getting a SSLHandshakeException (no cipher suites in common). This are the methods to create sockets in both sides:

Client:

private SSLSocket getSocketConnection() throws SSLConnectionException {
    try {

        /* Load properties */
        String keystore = properties.getProperty("controller.keystore");
        String passphrase = properties.getProperty("controller.passphrase");
        String host = properties.getProperty("controller.host");
        int port = Integer.parseInt(properties
                .getProperty("controller.port"));

        /* Create keystore */
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(new FileInputStream(keystore), passphrase.toCharArray());

        /* Get factory for the given keystore */
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(keyStore);
        SSLContext ctx = SSLContext.getInstance("SSL");
        ctx.init(null, tmf.getTrustManagers(), null);
        SSLSocketFactory factory = ctx.getSocketFactory();

        return (SSLSocket) factory.createSocket(host, port);
    } catch (Exception e) {
        throw new SSLConnectionException(
                "Problem connecting with remote controller: "
                        + e.getMessage(), e.getCause());
    }
}

Server:

private SSLServerSocket getServerSocket() throws SSLConnectionException {
    try {

        /* Load properties */
        Properties properties = getProperties("controller.properties");

        String keystore = properties.getProperty("controller.keystore");
        String passphrase = properties.getProperty("controller.passphrase");
        int port = Integer.parseInt(properties
                .getProperty("controller.port"));

        /* Create keystore */
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(new FileInputStream(keystore), passphrase.toCharArray());

        /* Get factory for the given keystore */
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(keyStore);
        SSLContext ctx = SSLContext.getInstance("SSL");
        ctx.init(null, tmf.getTrustManagers(), null);
        SSLServerSocketFactory factory = ctx.getServerSocketFactory();

        return (SSLServerSocket) factory.createServerSocket(port);
    } catch (Exception e) {
        throw new SSLConnectionException(
                "Problem starting auth server: "
                        + e.getMessage(), e.getCause());
    }
}

I have a RSA key generated with keytool. This code load it from disk.

What I'm doing wrong?

UPDATE: I added the a call to setEnabledCipherSuites in both sides with this array:

String enableThese[] =
{
    "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
    "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
    "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA"
};

I get the same result.

Randazzo answered 14/3, 2013 at 9:34 Comment(4)
Hard to believe. Are you sure you aren't calling setEnabledCipherSuites() anywhere?Appoint
I'm not doing it. Indeed I don't know what that method does. I'll take a look in Google.Dram
@EJP I added a call to that method in both sides (post updated) but I get the same exception.Dram
The idea is to remove any code that constrains the cipher suites, not add it. You've made it worse. If you have standard Java at both ends the situation you describe is impossible, as Java supports dozens of cipher suites, and the same ones as server and client. There must be more to this.Appoint
T
14

On the server side, you're not initialising the keystore/keymanagers, only the truststore/trustmanagers: ctx.init(null, tmf.getTrustManagers(), null).

On the server, initialising the keymanager is always necessary to configure the server certificate. Initialising the truststore is only necessary when you want to use client-certificate authentication. (There are more details in this question for the difference between keymanager and trustmanager.)

Without any keymanager configured, there is no RSA or DSA based certificate available, so no cipher suite that rely on a certificate for authentication (all the ones enabled by default are) are available. Hence, you get no cipher suites in common between the client and the server.

You'd need something like this:

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keystore, password.toCharArray()); // That's the key's password, if different.
// ...
ctx.init(kmf.getKeyManagers(), null, null);

It's not clear from your example, but you shouldn't of course use the same keystore on the client (as a truststore) and on the server side (as a keystore): the private key should only be known to the server, and doesn't need to be in the client's trust store.


EDIT: (I'll try to re-explain in a different way, since it wasn't clear for everyone. Perhaps it might help.)

The following code initialises the SSLContext with a null array of key managers (the first argument): ctx.init(null, tmf.getTrustManagers(), null). (There is no default key manager.)

The key manager is what manages your (private) keys and certificates, on the side where the code is running. On the server, the key manager is what's responsible for handling the server certificate and its private key. The key manager is itself usually initialised by the "keystore keystore". "keystore" in Java can have multiple meanings. One of the meaning of keystore is the entity into which keys and certificates can be stored, typically a file. Such a keystore can be used to initialise a trust manager1 (in which case it's referred to as the truststore) or a key manager (in which case it's referred to as a keystore). Sorry, not my choice of names, but that's the way the system properties are called.

When the server is configured with a null key manager, it is configured without any certificate and associated private key. Therefore, it doesn't have any RSA or DSA certificate. Therefore, it won't be able to use any of the *_RSA_* or *_DSS_* cipher suites, whether they've been explicitly enabled or not (they will be disabled automatically by lack of certificate to use with them). This effectively discards any cipher suite enabled by default (or any such cipher suite enabled explicitly anyway). Hence, there is "no cipher suite in common".

In short, an SSLContext on the server side needs to be configured with a certificate and its private key2. This is done by configuring its key manager. In turn, this is often done by using a keystore with a KeyManagerFactory (not a TrustManagerFactory).

1: The trust manager uses local trust anchors (e.g. trusted CA certificates) to evaluate trust in a remote party (i.e. a server trusting a client certificate or a client trusting a server certificate).

2: Some cipher suites supported by the JSSE don't need certificates, but they're either anonymous cipher suites (insecure) or Kerberos cipher suites (which need to be set up differently altogether). Both are disabled by default.

Tertia answered 14/3, 2013 at 10:23 Comment(7)
Thank you very much. That solved my problem. Just one question. What do I have to do to create a different keystore for both. Could you point me to a related article o something else? Thank you for your help.Dram
The easiest is probably to export the certificate and re-import it into a new keystore. Use keytool -list to find the alias, then keytool -export -keystore ... -alias ... -file host.crt and keytool -import ....Tertia
I think the downvoter find this answer a little confusing. I find it confusing myself, but the question you linked have a better answer to it. However, I am still confuse :(Kidding
@MelvinLai, I've just added a bit more text, perhaps it might help.Tertia
THANK YOU. This explanation is definitely better than online examples! :DKidding
@Tertia Thanks a lot, your explanation ( especially after editing) saved meNightrider
Works well in ubuntu . Initializing the keyManger done the trickEvesham

© 2022 - 2024 — McMap. All rights reserved.