Java: HTTPS requests with certificates using HttpClient (two-way authentication)
You need to send GET requests to the server via HTTPS. For these purposes, use Apache HttpClient 3.1.0
The problem is that the server requires a certificate (two-way authentication). I was provided with two files: client.cert.pem
and client.key.pem
, from which I obtained keystore.jks
and keystore.p12
using keytool and openssl.
I use curl
I am successfully getting data from the server
curl --cert client.cert.pem --key client.key.pem ...
But in the code I can't pass the certificate correctly.
By studying many examples on the Internet I came up with this code:
final char[] JKS_PASSWORD = "password".toCharArray();
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(
new File("ssl/keystore.jks"), JKS_PASSWORD, new TrustSelfSignedStrategy()).build();
HttpClient httpsClient = HttpClients.custom().setSSLContext(sslContext).build();
HttpResponse rResponse = httpsClient.execute(new HttpGet(MY_URL));
In this case y I get an exception:
java.lang.NoSuchMethodError: org.apache.http.impl.client.HttpClientBuilder.setSSLContext(Ljavax/net/ssl/SSLContext;)Lorg/apache/http/impl/client/HttpClientBuilder;
If you use SSLConnectionSocketFactory
:
...
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
sslContext,
new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
HttpClient httpsClient = HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build();
HttpResponse rResponse = httpsClient.execute(new HttpGet(MY_URL));
Discarded:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
I obviously messed up something, but I can't figure out what. Any help is welcome!
2 answers
I have a mobile application for android that uses its CA to verify the certificate chain. Try taking a piece of code or an algorithm from here. It's certainly not for the Apache client. But, I think, something common can be distinguished. before sending the request, I call
....
SSLConnection.setSSLSocketFactory(MakeOWN_CASocketFactory());
....
SSLSocketFactory MakeOWN_CASocketFactory() {
if (!useOwnCAForTLS || !serverURI.contains("DOMAIN.LOCAL")) return null;
if (sslContext == null) {
try {
Debug("SocketFactoryOWN_CA(): Получаем локальное хранилище...");
KeyStore localTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream rawRes = settingsContext.get().getResources().openRawResource(R.raw.OWN_CA_root_ca);
Debug("SocketFactoryOWN_CA():Загружаем локальное хранилище корневых сертификатов");
localTrustStore.load(rawRes, "PASSWORD_THERE".toCharArray());
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(localTrustStore);
sslContext = SSLContext.getInstance("TLS");
Debug("SocketFactoryOWN_CA(): формируем SSL-контекст");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception e) {
Debug("loadOWN_CATrustStore(): Ошибка: " + e.getMessage());
}
return null;
} else {
try {
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
} catch (Exception e) {
Debug("loadOWN_CATrustStore(): Ошибка: " + e.getMessage());
}
return (localSocketFactory = sslContext.getSocketFactory());
}
}
Try this way:
String keyPhrase = "your_key_there";
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(getClass().getClassLoader().getResourceAsStream("your_cert"), keyPhrase
.toCharArray());
SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, keyPhrase.toCharArray()).build();
HttpGet httpGet = new HttpGet("your_url_there");
HttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build();
HttpResponse response = httpClient.execute(httpGet);