ews-java-api
ews-java-api copied to clipboard
How to trust SSL certificate?
Our Exchange Server is hosted on domain with self-signed certificate therefore I have this exception:
Caused by: microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException: The request failed. sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at microsoft.exchange.webservices.data.core.request.ServiceRequestBase.getEwsHttpWebResponse(ServiceRequestBase.java:729) ~[ews-java-api-2.0.jar:na]
at microsoft.exchange.webservices.data.core.request.ServiceRequestBase.validateAndEmitRequest(ServiceRequestBase.java:639) ~[ews-java-api-2.0.jar:na]
at microsoft.exchange.webservices.data.core.request.SimpleServiceRequestBase.internalExecute(SimpleServiceRequestBase.java:62) ~[ews-java-api-2.0.jar:na]
... 18 common frames omitted
Caused by: 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
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) ~[na:1.8.0_222]
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946) ~[na:1.8.0_222]
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316) ~[na:1.8.0_222]
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310) ~[na:1.8.0_222]
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1639) ~[na:1.8.0_222]
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223) ~[na:1.8.0_222]
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037) ~[na:1.8.0_222]
at sun.security.ssl.Handshaker.process_record(Handshaker.java:965) ~[na:1.8.0_222]
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064) ~[na:1.8.0_222]
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367) ~[na:1.8.0_222]
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395) ~[na:1.8.0_222]
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379) ~[na:1.8.0_222]
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:436) ~[httpclient-4.5.10.jar:4.5.10]
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:384) ~[httpclient-4.5.10.jar:4.5.10]
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142) ~[httpclient-4.5.10.jar:4.5.10]
at org.apache.http.impl.conn.BasicHttpClientConnectionManager.connect(BasicHttpClientConnectionManager.java:313) ~[httpclient-4.5.10.jar:4.5.10]
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393) ~[httpclient-4.5.10.jar:4.5.10]
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) ~[httpclient-4.5.10.jar:4.5.10]
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186) ~[httpclient-4.5.10.jar:4.5.10]
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) ~[httpclient-4.5.10.jar:4.5.10]
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) ~[httpclient-4.5.10.jar:4.5.10]
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) ~[httpclient-4.5.10.jar:4.5.10]
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) ~[httpclient-4.5.10.jar:4.5.10]
at microsoft.exchange.webservices.data.core.request.HttpClientWebRequest.executeRequest(HttpClientWebRequest.java:292) ~[ews-java-api-2.0.jar:na]
at microsoft.exchange.webservices.data.core.request.ServiceRequestBase.getEwsHttpWebResponse(ServiceRequestBase.java:720) ~[ews-java-api-2.0.jar:na]
... 20 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:397) ~[na:1.8.0_222]
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302) ~[na:1.8.0_222]
at sun.security.validator.Validator.validate(Validator.java:262) ~[na:1.8.0_222]
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:330) ~[na:1.8.0_222]
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:237) ~[na:1.8.0_222]
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:113) ~[na:1.8.0_222]
at microsoft.exchange.webservices.data.core.EwsX509TrustManager.checkServerTrusted(EwsX509TrustManager.java:83) ~[ews-java-api-2.0.jar:na]
at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:1099) ~[na:1.8.0_222]
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1621) ~[na:1.8.0_222]
... 40 common frames omitted
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141) ~[na:1.8.0_222]
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126) ~[na:1.8.0_222]
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280) ~[na:1.8.0_222]
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392) ~[na:1.8.0_222]
... 48 common frames omitted
How to trust self-signed certificate?
Ok. I found a solution creating TrustAllTrustManager. Will a PR be reviewed and accepted if I create it? Willn't it be a your policy violation?
How do we trust server's self signed certificate? We are currently having this issue.
This can be solved outside of this library I think. All you need to do is add the self-signed certificate to the trust store of the jre being used by the client.
https://docs.oracle.com/cd/E76310_01/pdf/1411/html/merch_sg/apps-chapter%207.htm#CHDJBIJD
By the way if you don't want to change the jre truststore you can also do this sort of thing locally in a webapp by modifying the SSLContext to use a truststore from the classpath (optionally extending the default truststore from the jre). Let me know if you need the code.
We are using system property to set trust-store and key-store. -Djavax.net.ssl.truststore=FILE_PATH_TS -Djavax.net.ssl.keystore=FILE_PATH_KS
and corresponding passwords
By the way if you don't want to change the jre truststore you can also do this sort of thing locally in a webapp by modifying the SSLContext to use a truststore from the classpath (optionally extending the default truststore from the jre). Let me know if you need the code.
Could you provide a sample code how to do this? Thanks in advance!
@szrnka-peter
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
public final class ExtendedTrustManager extends X509ExtendedTrustManager {
private final X509TrustManager defaultTm;
private final X509TrustManager customTm;
public ExtendedTrustManager(InputStream trustStoreInputStream, char[] trustStorePassword) {
try {
X509TrustManager defaultTm = null;
{
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
defaultTm = (X509TrustManager) tm;
break;
}
}
}
this.defaultTm = defaultTm;
final KeyStore trustStore;
trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(trustStoreInputStream, trustStorePassword);
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
X509TrustManager customTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
customTm = (X509TrustManager) tm;
break;
}
}
this.customTm = customTm;
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
| IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
defaultTm.checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
customTm.checkServerTrusted(chain, authType);
} catch (CertificateException e) {
defaultTm.checkServerTrusted(chain, authType);
}
}
@Override
public final X509Certificate[] getAcceptedIssuers() {
return defaultTm.getAcceptedIssuers();
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
checkClientTrusted(chain, authType);
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
throws CertificateException {
checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
checkServerTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
throws CertificateException {
checkServerTrusted(chain, authType);
}
public static SSLContext createTlsContextWithExtendedTrustManager(InputStream trustStore,
String trustStorePassword) throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager trustManager = new ExtendedTrustManager(trustStore,
trustStorePassword.toCharArray());
TrustManager[] trustManagers = new TrustManager[] { trustManager };
sslContext.init(null, trustManagers, new java.security.SecureRandom());
return sslContext;
}
}
To use:
String trustStorePassword = ...;
SSLContext context;
try (InputStream is = SomeClass.class.getResourceAsStream("/trustStore.jks")) {
context = ExtendedTrustManager.createTlsContextWithExtendedTrustManager(is,
trustStorePassword);
}
SSLSocketFactory factory = context.getSocketFactory();
Socket socket = factory.createSocket(host, port);
socket.setKeepAlive(true);
socket.setSoTimeout(readTimeoutMs);
Reader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));