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.
The following should be considered when configuring cipher specifications assuming the appropriate support by the remote peer: AES based ciphers are more secure than the corresponding 3DES, DES, and RC4 based ciphers. AES-GCM ciphers are more secure than AES-CBC ciphers.
A cipher suite is a set of cryptographic algorithms. The schannel SSP implementation of the TLS/SSL protocols use algorithms from a cipher suite to create keys and encrypt information. A cipher suite specifies one algorithm for each of the following tasks: Key exchange. Bulk encryption.
A cipher suite is a set of cryptographic algorithms. This is used to encrypt messages between clients/servers and other servers. Dataverse is using the latest TLS 1.2 cipher suites as approved by Microsoft Crypto Board.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With