'javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found
I am using Retrofit to access my REST API. However, when I put my API behind ssl and access it by http://myhost/myapi then I get this error:
Do I need to do something extra now that my API is behind SSL?
Here is how I connect:
private final String API = "https://myhost/myapi";
private final RestAdapter REST_ADAPTER = new RestAdapter.Builder()
.setServer(API)
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
01-10 09:49:55.621 2076-2100/com.myapp.mobile D/Retrofit﹕ javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:401)
at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209)
at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:497)
at libcore.net.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:134)
at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:90)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:48)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:287)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:222)
at $Proxy12.signin(Native Method)
at com.myapp.loginactivity$3.doInBackground(LoginActivity.java:143)
at com.myapp.loginactivity$3.doInBackground(LoginActivity.java:136)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)
Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:282)
at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:202)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:595)
at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:398)
at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209)
at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:497)
at libcore.net.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:134)
at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:90)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:48)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:287)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:222)
at $Proxy12.signin(Native Method)
at com.myapp.LoginActivity$3.doInBackground(LoginActivity.java:143)
at com.myapp.LoginActivity$3.doInBackground(LoginActivity.java:136)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)
Solution 1:[1]
The reason this occur is the JVM/Dalvik haven't not confidence in the CA certificates in the system or in the user certificate stores.
To fix this with Retrofit, If you are used okhttp, with another client it's very similar.
You've to do:
A). Create a cert store contain public Key of CA. To do this you need to launch next script for *nix. You need openssl install in your machine, and download from https://www.bouncycastle.org/ the jar bcprov-jdk16-1.46.jar. Download this version not other, the version 1.5x is not compatible with android 4.0.4.
#!/bin/bash
if [ -z $1 ]; then
echo "Usage: cert2Android<CA cert PEM file>"
exit 1
fi
CACERT=$1
BCJAR=bcprov-jdk16-1.46.jar
TRUSTSTORE=mytruststore.bks
ALIAS=`openssl x509 -inform PEM -subject_hash -noout -in $CACERT`
if [ -f $TRUSTSTORE ]; then
rm $TRUSTSTORE || exit 1
fi
echo "Adding certificate to $TRUSTSTORE..."
keytool -import -v -trustcacerts -alias $ALIAS \
-file $CACERT \
-keystore $TRUSTSTORE -storetype BKS \
-providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath $BCJAR \
-storepass secret
echo ""
echo "Added '$CACERT' with alias '$ALIAS' to $TRUSTSTORE..."
B). Copy the file truststore mytruststore.bks in res/raw of your project

C). Setting SSLContext of the connection:
.............
okHttpClient = new OkHttpClient();
try {
KeyStore ksTrust = KeyStore.getInstance("BKS");
InputStream instream = context.getResources().openRawResource(R.raw.mytruststore);
ksTrust.load(instream, "secret".toCharArray());
// TrustManager decides which certificate authorities to use.
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ksTrust);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | KeyManagementException e) {
e.printStackTrace();
}
.................
Solution 2:[2]
This can happen for several reasons, including:
- The CA that issued the server certificate was unknown
- The server certificate wasn't signed by a CA, but was self signed
- The server configuration is missing an intermediate CA
please check out this link for solution: https://developer.android.com/training/articles/security-ssl.html#CommonProblems
Solution 3:[3]
Hi same problem i have solved you can try this
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.NETWORK
// SET SSL
public static OkClient setSSLFactoryForClient(OkHttpClient client) {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
client.setSslSocketFactory(sslSocketFactory);
client.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
return new OkClient(client);
}
Solution 4:[4]
Fix for Android N & above: I had similar issue and mange to solve it by following steps described in https://developer.android.com/training/articles/security-config
But the config changes, without any complicated code logic, would only work on Android version 24 & above.
Fix for all version, including version < N: So for android lower then N (version 24) the solution is to via code changes as mentioned above. If you are using OkHttp, then follow the customTrust: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java
Solution 5:[5]
You basically have four potential solutions to fix a "javax.net.ssl.SSLHandshakeException: " exception on Android
- Trust all certificates. Don't do this, unless you really know what you're doing.
- Create a custom SSLSocketFactory that trusts only your certificate. This works as long as you know exactly which servers you're going to connect to, but as soon as you need to connect to a new server with a different SSL certificate, you'll need to update your app.
- Create a Keystore file that contains Android's "master list" of certificates, then add your own. If any of those certs expire down the road, you are responsible for updating them in your app. I can't think of a reason to do this.
- Create a custom SSLSocketFactory that uses the built-in certificate KeyStore, but falls back on an alternate KeyStore for anything that fails to verify with the default. This is well explained in click here
Also, I want to elaborate more to point number 1. We can selectively skip some domain using manifest network config as explain:
Create a file "network_security_config.xml" in xml folder in res folder with following content.
<network-security-config xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android"> <domain-config> <domain includeSubdomains="true">191.1.1.0</domain> <domain includeSubdomains="true">your_domain</domain> <trust-anchors> <certificates src="system" /> <certificates src="user" /> </trust-anchors> </domain-config> </network-security-config>Add "network_security_config.xml" to application tag in manifest as:
android:networkSecurityConfig="@xml/network_security_config"
Thats it..done!!. You successfully skipped the SSL certificate.
Solution 6:[6]
There are 4 ways that I know of:
- import the certificate to your app and use it for the connection
- disable certificate checking
- add your certificate to the trusted system certificates in Android
- buy a verified certificate that is accepted by Android
I assume you don't want to pay for this, so I think the most elegant solution is the first one, what can be accomplished this way:
http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html
Solution 7:[7]
The SSL is not properly configured. Those trustAnchor errors usually mean that the trust store cannot be found. Check your configuration and make sure you are actually pointing to the trust store and that it is in place.
Make sure you have a -Djavax.net.ssl.trustStore system property set and then check that the path actually leads to the trust store.
You can also enable SSL debugging by setting this system property -Djavax.net.debug=all. Within the debug output you will notice it states that it cannot find the trust store.
Solution 8:[8]
This is a Server-Side issue.
Server side have .crt file for HTTPS, here we have to do combine
cat your_domain.**crt** your_domain.**ca-bundle** >> ssl_your_domain_.crt
then restart.
sudo service nginx restart
For me working fine.
Solution 9:[9]
After a some research i found the way to bypass ssl error Trust anchor for certification path not found. This might be not a good way to do but you can use it for a testing purpose.
private HttpsURLConnection httpsUrlConnection( URL urlDownload) throws Exception {
HttpsURLConnection connection=null;
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@SuppressLint("TrustAllX509TrustManager")
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@SuppressLint("TrustAllX509TrustManager")
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
SSLContext sc = SSLContext.getInstance("SSL"); // Add in try catch block if you get error.
sc.init(null, trustAllCerts, new java.security.SecureRandom()); // Add in try catch block if you get error.
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier usnoHostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
SSLSocketFactory sslSocketFactory = sc.getSocketFactory();
connection = (HttpsURLConnection) urlDownload.openConnection();
connection.setHostnameVerifier(usnoHostnameVerifier);
connection.setSSLSocketFactory(sslSocketFactory);
return connection;
}
Solution 10:[10]
I use this class and have no problem.
public class WCFs
{
// https://192.168.30.8/myservice.svc?wsdl
private static final String NAMESPACE = "http://tempuri.org/";
private static final String URL = "192.168.30.8";
private static final String SERVICE = "/myservice.svc?wsdl";
private static String SOAP_ACTION = "http://tempuri.org/iWCFserviceMe/";
public static Thread myMethod(Runnable rp)
{
String METHOD_NAME = "myMethod";
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty("Message", "Https WCF Running...");
return _call(rp,METHOD_NAME, request);
}
protected static HandlerThread _call(final RunProcess rp,final String METHOD_NAME, SoapObject soapReq)
{
final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
int TimeOut = 5*1000;
envelope.dotNet = true;
envelope.bodyOut = soapReq;
envelope.setOutputSoapObject(soapReq);
final HttpsTransportSE httpTransport_net = new HttpsTransportSE(URL, 443, SERVICE, TimeOut);
try
{
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() // use this section if crt file is handmake
{
@Override
public boolean verify(String hostname, SSLSession session)
{
return true;
}
});
KeyStore k = getFromRaw(R.raw.key, "PKCS12", "password");
((HttpsServiceConnectionSE) httpTransport_net.getServiceConnection()).setSSLSocketFactory(getSSLSocketFactory(k, "SSL"));
}
catch(Exception e){}
HandlerThread thread = new HandlerThread("wcfTd"+ Generator.getRandomNumber())
{
@Override
public void run()
{
Handler h = new Handler(Looper.getMainLooper());
Object response = null;
for(int i=0; i<4; i++)
{
response = send(envelope, httpTransport_net , METHOD_NAME, null);
try
{if(Thread.currentThread().isInterrupted()) return;}catch(Exception e){}
if(response != null)
break;
ThreadHelper.threadSleep(250);
}
if(response != null)
{
if(rp != null)
{
rp.setArguments(response.toString());
h.post(rp);
}
}
else
{
if(Thread.currentThread().isInterrupted())
return;
if(rp != null)
{
rp.setExceptionState(true);
h.post(rp);
}
}
ThreadHelper.stopThread(this);
}
};
thread.start();
return thread;
}
private static Object send(SoapSerializationEnvelope envelope, HttpTransportSE androidHttpTransport, String METHOD_NAME, List<HeaderProperty> headerList)
{
try
{
if(headerList != null)
androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope, headerList);
else
androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope);
Object res = envelope.getResponse();
if(res instanceof SoapPrimitive)
return (SoapPrimitive) envelope.getResponse();
else if(res instanceof SoapObject)
return ((SoapObject) envelope.getResponse());
}
catch(Exception e)
{}
return null;
}
public static KeyStore getFromRaw(@RawRes int id, String algorithm, String filePassword)
{
try
{
InputStream inputStream = ResourceMaster.openRaw(id);
KeyStore keystore = KeyStore.getInstance(algorithm);
keystore.load(inputStream, filePassword.toCharArray());
inputStream.close();
return keystore;
}
catch(Exception e)
{}
return null;
}
public static SSLSocketFactory getSSLSocketFactory(KeyStore trustKey, String SSLAlgorithm)
{
try
{
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustKey);
SSLContext context = SSLContext.getInstance(SSLAlgorithm);//"SSL" "TLS"
context.init(null, tmf.getTrustManagers(), null);
return context.getSocketFactory();
}
catch(Exception e){}
return null;
}
}
Solution 11:[11]
My answer might not be solution to your question but it will surely help others looking for similar issue like this one: javax.net.ssl.SSLHandshakeException: Chain validation failed
You just need to check your Android Device's Date and Time, it should be fix the issue. This resoled my problem.
Solution 12:[12]
OK, So I faced the same issue for my android app which have secured domain i.e. HTTPS,
There are 2 Solutions for it:
SOLUTION 1 (EASY but not recommended):
Use this OkHttpClient.Builder to your Retrofit and it will work for you.
class UnsafeOkHttpClient {
companion object {
fun getUnsafeOkHttpClient(): OkHttpClient.Builder {
try {
// Create a trust manager that does not validate certificate chains
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {
}
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {
}
override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> {
return arrayOf()
}
})
// Install the all-trusting trust manager
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory = sslContext.socketFactory
val builder = OkHttpClient.Builder()
builder.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
// builder.hostnameVerifier { _, _ -> true }
builder.hostnameVerifier ( hostnameVerifier = HostnameVerifier{ _, _ -> true })
return builder
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
}
SOLUTION 2 (LENGTHY but recommended):
- You need SSL certificate file i.e. ".pem" file for your domain.
- put that file into the assets folder
- Just copy and paste this class in your project
public class SSlUtilsw {
public static SSLContext getSslContextForCertificateFile(Context context, String fileName){
try {
KeyStore keyStore = SSlUtilsw.getKeyStore(context, fileName);
SSLContext sslContext = SSLContext.getInstance("SSL");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(null,trustManagerFactory.getTrustManagers(),new SecureRandom());
return sslContext;
}catch (Exception e){
String msg = "Error during creating SslContext for certificate from assets";
e.printStackTrace();
throw new RuntimeException(msg);
}
}
public static KeyStore getKeyStore(Context context,String fileName){
KeyStore keyStore = null;
try {
AssetManager assetManager=context.getAssets();
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput=assetManager.open(fileName);
Certificate ca;
try {
ca=cf.generateCertificate(caInput);
}finally {
caInput.close();
}
String keyStoreType=KeyStore.getDefaultType();
keyStore=KeyStore.getInstance(keyStoreType);
keyStore.load(null,null);
keyStore.setCertificateEntry("ca",ca);
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return keyStore;
}}
In your http client class of retrofit, add this
val trustManagerFactory: TrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) trustManagerFactory.init(null as KeyStore?) val trustManagers: Array<TrustManager> = trustManagerFactory.trustManagers if (trustManagers.size != 1 || trustManagers[0] !is X509TrustManager) { throw IllegalStateException("Unexpected default trust managers:" + trustManagers.contentToString()) } val trustManager = trustManagers[0] as X509TrustManager httpClient.sslSocketFactory(SSlUtils.getSslContextForCertificateFile( applicationContextHere, "yourcertificate.pem").socketFactory, trustManager)
And that's it.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
