Java client with security

Hi there,

I'm now using ES java client to communicate with a secured ( x-pack ) es cluster, my java code is like below,

@Bean(destroyMethod = "close")
public RestHighLevelClient getRestHighLevelClient() {
	List<HttpHost> httpHosts = new ArrayList<>();
	esProperties.getNodes().forEach(esServer -> {
		httpHosts.add(new HttpHost(esServer.getHost(), esServer.getPort(), esServer.getProtocol()));
	});
	
	final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
	credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(esProperties.getUserName(), esProperties.getPassword()));
	
	return new RestHighLevelClient(
		RestClient
			.builder(httpHosts.toArray(new HttpHost[httpHosts.size()]))
			.setHttpClientConfigCallback(
				httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
			)
			.setRequestConfigCallback(
				requestConfigBuilder -> requestConfigBuilder.setConnectTimeout(5000).setSocketTimeout(60000)
			)
	);
}

but I'll get exception as below:

Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
	at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) ~[na:1.8.0_45]
	at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1728) ~[na:1.8.0_45]
	at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:304) ~[na:1.8.0_45]
	at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296) ~[na:1.8.0_45]
	at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1478) ~[na:1.8.0_45]
	at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:212) ~[na:1.8.0_45]
	at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979) ~[na:1.8.0_45]
	at sun.security.ssl.Handshaker$1.run(Handshaker.java:919) ~[na:1.8.0_45]
	at sun.security.ssl.Handshaker$1.run(Handshaker.java:916) ~[na:1.8.0_45]
	at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_45]
	at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1369) ~[na:1.8.0_45]
	at org.apache.http.nio.reactor.ssl.SSLIOSession.doRunTask(SSLIOSession.java:281) ~[httpcore-nio-4.4.11.jar:4.4.11]
	at org.apache.http.nio.reactor.ssl.SSLIOSession.doHandshake(SSLIOSession.java:339) ~[httpcore-nio-4.4.11.jar:4.4.11]
	... 9 common frames omitted
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387) ~[na:1.8.0_45]
	at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) ~[na:1.8.0_45]
	at sun.security.validator.Validator.validate(Validator.java:260) ~[na:1.8.0_45]
	at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324) ~[na:1.8.0_45]
	at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:281) ~[na:1.8.0_45]
	at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:136) ~[na:1.8.0_45]
	at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1465) ~[na:1.8.0_45]
	... 17 common frames omitted
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Can anyone help me out on this, thanks very much in advance ...

Hi @wl105500396 ,

you will need to make a certificate that shares a valid chain with the certificate used by your ES cluster available to the Java client's JVM to fix the error pasted.

Either you import the ES cluster's certificate into the global Java keystore which should make it work automatically or better yet you do some thing like this:

  1. Create a new keystore with the certificate in it.
keytool -importcert -file my_domain.crt -keystore my_keystore.jks -keypass password -storepass password
  1. Use that keystore via initialization code like this as explained in here.
KeyStore truststore = KeyStore.getInstance("jks");
try (InputStream is = Files.newInputStream(keyStorePath)) {
    truststore.load(is, keyStorePass.toCharArray());
}
SSLContextBuilder sslBuilder = SSLContexts.custom()
    .loadTrustMaterial(truststore, null);
final SSLContext sslContext = sslBuilder.build();
RestClientBuilder builder = RestClient.builder(
    new HttpHost("localhost", 9200, "https"))
    .setHttpClientConfigCallback(new HttpClientConfigCallback() {
        @Override
        public HttpAsyncClientBuilder customizeHttpClient(
                HttpAsyncClientBuilder httpClientBuilder) {
            return httpClientBuilder.setSSLContext(sslContext);
        }
    });

Using a custom keystore instead of importing into the global cacert store has the advantage of continuing to work across Java runtime upgrades but both should work.

Hope that helps

hi @Armin_Braun,

Thank you very much for your reply. I will have a try on this solution right away.

BTW, as you may have known that when we use curl command to communicate with es cluster, we are able to disable ssl verification by setting '-k' option, which will only verify user name and password.

Could I disable the ssl verification for the java client just like the curl command ?

Frankly, I'd rather prefer this one if it is workable.

Thank you.

Hi @wl105500396

You could technically turn off the certificate validation yes (though I would rather recommend properly handling certificates and adding your own to the keystore from the security perspective :)).

But that said, below snippet should give you an SSLContext thet won't do any of the certificate chain verification I think.

                        SSLContext context = SSLContextBuilder.create().build();
                        context.init(
                            null,  new TrustManager[] {new X509ExtendedTrustManager() {
                                @Override
                                public void checkClientTrusted (X509Certificate[] chain, String authType, Socket socket) {

                                }

                                @Override
                                public void checkServerTrusted (X509Certificate [] chain, String authType, Socket socket) {

                                }

                                @Override
                                public void checkClientTrusted (X509Certificate [] chain, String authType, SSLEngine engine) {

                                }

                                @Override
                                public void checkServerTrusted (X509Certificate [] chain, String authType, SSLEngine engine) {

                                }

                                @Override
                                public X509Certificate [] getAcceptedIssuers () {
                                    return null;
                                }

                                @Override
                                public void checkClientTrusted (X509Certificate [] certs, String authType) {
                                }

                                @Override
                                public void checkServerTrusted (X509Certificate [] certs, String authType) {
                                }

                            }},
                        new SecureRandom());

Thank you @Armin_Braun

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.