Android 2.3.x javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found

匿名 (未验证) 提交于 2019-12-03 01:57:01

问题:

I am receiving this error only on (maybe some) 2.3.x devices. it works for any other devices running an Android version above that.

Here is my HTTPRequestController:

public class HttpRequestController {  private final static String TAG = "HttpRequestController";  private static HttpRequestController instance;  public enum Method {     PUT, POST, DELETE, GET }  private HttpRequestController() {  }  public static HttpRequestController getInstance() {     if (instance == null)         instance = new HttpRequestController();      return instance; }  public String doRequest(String url, HashMap data,         Method method, String token) throws Exception {      InputStream certificateInputStream = null;     if (MyApplication.PRODUCTION) {         certificateInputStream = MyApplication.context                 .getResources().openRawResource(R.raw.production_cert);         LogUtils.log("using production SSL certificate");     } else {         certificateInputStream = MyApplication.context                 .getResources().openRawResource(R.raw.staging_cert);         LogUtils.log("using staging SSL certificate");     }      KeyStore trustStore = KeyStore.getInstance("BKS");     try{     trustStore.load(certificateInputStream,             "re3d6Exe5HBsdskad8efj8CxZwv".toCharArray());     } finally {         certificateInputStream.close();     }       TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");     tmf.init(trustStore);     LogUtils.log("SSL: did init TrustManagerFactory with trust keyStore");     SSLContext context = SSLContext.getInstance("TLS");     context.init(null, tmf.getTrustManagers(), null);     LogUtils.log("SSL: did init context with trust keyStore");         URL request = new URL(url);     HttpsURLConnection urlConnection = (HttpsURLConnection) request             .openConnection();      LogUtils.log("SSL: did open HttpsURLConnection");         urlConnection.setHostnameVerifier(new StrictHostnameVerifier());     urlConnection.setSSLSocketFactory(context.getSocketFactory());     urlConnection.setConnectTimeout(15000);     LogUtils.log("SSL: did set Factory and Timeout.");      if (method != Method.GET){         urlConnection.setDoOutput(true);     }         urlConnection.setDoInput(true);         urlConnection.setRequestProperty("Content-Type", "application/json");         urlConnection.setRequestProperty("Accept", "application/json");      LogUtils.log("SSL: urlConnection did set request properties.");      if (token != null) {         urlConnection.setRequestProperty("Authorization", "Token " + token);     }         urlConnection.setRequestMethod(method.toString());         urlConnection.connect();          LogUtils.log("SSL: urlConnection did connect.");      if (method != Method.GET) {         ObjectMapper mapper = new ObjectMapper();         String jsonValue = mapper.writeValueAsString(data);         OutputStream os = urlConnection.getOutputStream();         os.write(jsonValue.getBytes());         os.flush();         LogUtils.log(TAG, "Params: " + jsonValue);     }      LogUtils.log(TAG, method.toString() + ": " + url);      InputStream in = null;     if (urlConnection.getResponseCode() == 200) {         in = urlConnection.getInputStream();     } else {         in = urlConnection.getErrorStream();     }     String response = convertStreamToString(in);      LogUtils.log(TAG, "Got response : " + url);     LogUtils.log(TAG, "Response : " + response);      return response; }  public String convertStreamToString(InputStream inputStream) {     BufferedReader buffReader = new BufferedReader(new InputStreamReader(             inputStream));     StringBuilder stringBuilder = new StringBuilder();      String line = null;     try {         while ((line = buffReader.readLine()) != null) {             stringBuilder.append(line + "\n");         }     } catch (IOException e) {         e.printStackTrace();     } finally {         try {             inputStream.close();         } catch (IOException e) {             e.printStackTrace();         }     }     return stringBuilder.toString(); }  public HttpClient retrieveHttpClient() {     return new MyHttpClient(MyApplication.context); } 

}

When i run the command:

openssl s_client -debug -connect www.mysitedomain.com:443 

I get the response:

-- some key stuff  -- Certificate chain  0 s:/OU=Domain Control Validated/CN=www.mydomainname.com    i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - G2  1 s:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - G2    i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA  2 s:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA    i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA --- Server certificate -----BEGIN CERTIFICATE-----  some more certificate stuff -----END CERTIFICATE-----  ubject=/OU=Domain Control Validated/CN=www.mydomainname.com issuer=/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - G2 --- No client certificate CA names sent --- SSL handshake has read 4091 bytes and written 328 bytes --- New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE SSL-Session:     Protocol  : TLSv1     Cipher    : DHE-RSA-AES256-SHA     Session-ID: 57C379C59483809A7FE1BF8E235C5BFA7789E62AAEBCA9BC14B5273F5D1304E7     Session-ID-ctx:      Master-Key: 6FCD498D1294415A42B57420F0C05AB903EF8E56CB6F1530390F73AF5E4CBC22B359D5CDA09811E075A5C598002C380D     Key-Arg   : None     Start Time: 1390473282     Timeout   : 300 (sec)     Verify return code: 0 (ok) --- 

so it returns okay... But it still gives me this error for the 2.3.x devices I tested.

I get an exception after this point:

LogUtils.log("SSL: urlConnection did set request properties."); 

Here is the exception:

01-23 10:20:28.459: W/System.err(1623): javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. 01-23 10:20:28.459: W/System.err(1623):     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:477) 01-23 10:20:28.459: W/System.err(1623):     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:328) 01-23 10:20:28.459: W/System.err(1623):     at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.setupSecureSocket(HttpConnection.java:185) 01-23 10:20:28.459: W/System.err(1623):     at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:433) 01-23 10:20:28.459: W/System.err(1623):     at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeConnection(HttpsURLConnectionImpl.java:378) 01-23 10:20:28.459: W/System.err(1623):     at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:205) 01-23 10:20:28.459: W/System.err(1623):     at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:152) 

The way I am calling this is here:

String response = HttpRequestController                             .getInstance()                             .doRequest(ApiUrls.LOGIN, params, Method.POST, null); 

It works for any other devices running an Android version above 2.3.x (from what I have tested).

The Android documentation appears to have nothing written on the subject of 2.3 compatibility.

回答1:

You have to tell the Android system to trust your certificate. Your problem is that Android after 2.3 accepts your certificate because it has it included on the trusted certificates list, but on the previous versions is not included, so, there is the problem.

I recommend you doing like on the Android documentation:

// Load CAs from an InputStream // (could be from a resource or ByteArrayInputStream or ...) CertificateFactory cf = CertificateFactory.getInstance("X.509"); // From https://www.washington.edu/itconnect/security/ca/load-der.crt InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt")); Certificate ca; try {     ca = cf.generateCertificate(caInput);     System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); } finally {     caInput.close(); }  // Create a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca);  // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore);  // Create an SSLContext that uses our TrustManager SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null);  // Tell the URLConnection to use a SocketFactory from our SSLContext URL url = new URL("https://certs.cac.washington.edu/CAtest/"); HttpsURLConnection urlConnection =     (HttpsURLConnection)url.openConnection(); urlConnection.setSSLSocketFactory(context.getSocketFactory()); InputStream in = urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out); 

I am doing the same, and it is working properly on every devices, with Android 2.3 and below, and the certificate of my site is a private one.

Just try it, and tell me if it is working now.

Hope it helps you!



回答2:

In case someone need the answer, I finally found the answer after 2 days of google. Basically we need to use custom TrustManager to trusts the CAs in our KeyStore. Credit to https://github.com/delgurth for the CustomTrustManager.

Please refer: https://github.com/ikust/hello-pinnedcerts/issues/2

KeyPinStore.java

import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate;  import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; public class KeyPinStore {     private static final String[] certificates = {"certificate1.crt", "certificate2.crt", "certificate3.crt", "certificate4.crt"};     private static KeyPinStore instance = null;     private SSLContext sslContext = SSLContext.getInstance("TLS");      public static synchronized KeyPinStore getInstance() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {         if (instance == null) {             instance = new KeyPinStore();         }         return instance;     }      private KeyPinStore() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {         String keyStoreType = KeyStore.getDefaultType();         KeyStore keyStore = KeyStore.getInstance(keyStoreType);         keyStore.load(null, null);         for (int i = 0; i 

CustomTrustManager.java

import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.List;  import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager;  /**  * A custom X509TrustManager implementation that trusts a specified server certificate in addition  * to those that are in the system TrustStore.  * Also handles an out-of-order certificate chain, as is often produced by Apache's mod_ssl  */ public class CustomTrustManager implements X509TrustManager {    private final TrustManager[] originalTrustManagers;   private final KeyStore trustStore;    /**    * @param trustStore A KeyStore containing the server certificate that should be trusted    * @throws NoSuchAlgorithmException    * @throws KeyStoreException    */   public CustomTrustManager(KeyStore trustStore) throws NoSuchAlgorithmException, KeyStoreException {     this.trustStore = trustStore;      final TrustManagerFactory originalTrustManagerFactory = TrustManagerFactory.getInstance("X509");     originalTrustManagerFactory.init(trustStore);      originalTrustManagers = originalTrustManagerFactory.getTrustManagers();   }    /**    * No-op. Never invoked by client, only used in server-side implementations    * @return    */   public X509Certificate[] getAcceptedIssuers() {     return new X509Certificate[0];   }    /**    * No-op. Never invoked by client, only used in server-side implementations    * @return    */   public void checkClientTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {   }     /**    * Given the partial or complete certificate chain provided by the peer,    * build a certificate path to a trusted root and return if it can be validated and is trusted    * for client SSL authentication based on the authentication type. The authentication type is    * determined by the actual certificate used. For instance, if RSAPublicKey is used, the authType should be "RSA".    * Checking is case-sensitive.    * Defers to the default trust manager first, checks the cert supplied in the ctor if that fails.    * @param chain the server's certificate chain    * @param authType the authentication type based on the client certificate    * @throws java.security.cert.CertificateException    */   public void checkServerTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {     try {       for (TrustManager originalTrustManager : originalTrustManagers) {         ((X509TrustManager) originalTrustManager).checkServerTrusted(chain, authType);       }     } catch(CertificateException originalException) {       try {         // Ordering issue?         X509Certificate[] reorderedChain = reorderCertificateChain(chain);         if (! Arrays.equals(chain, reorderedChain)) {           checkServerTrusted(reorderedChain, authType);           return;         }         for (int i = 0; i  certificates = Arrays.asList(chain);      int position = chain.length - 1;     X509Certificate rootCert = findRootCert(certificates);     reorderedChain[position] = rootCert;      X509Certificate cert = rootCert;     while((cert = findSignedCert(cert, certificates)) != null && position > 0) {       reorderedChain[--position] = cert;     }      return reorderedChain;   }    /**    * A helper method for certificate re-ordering.    * Finds the root certificate in a possibly out-of-order certificate chain.    * @param certificates the certificate change, possibly out-of-order    * @return the root certificate, if any, that was found in the list of certificates    */   private X509Certificate findRootCert(List certificates) {     X509Certificate rootCert = null;      for(X509Certificate cert : certificates) {       X509Certificate signer = findSigner(cert, certificates);       if(signer == null || signer.equals(cert)) { // no signer present, or self-signed         rootCert = cert;         break;       }     }      return rootCert;   }    /**    * A helper method for certificate re-ordering.    * Finds the first certificate in the list of certificates that is signed by the sigingCert.    */   private X509Certificate findSignedCert(X509Certificate signingCert, List certificates) {     X509Certificate signed = null;      for(X509Certificate cert : certificates) {       Principal signingCertSubjectDN = signingCert.getSubjectDN();       Principal certIssuerDN = cert.getIssuerDN();       if(certIssuerDN.equals(signingCertSubjectDN) && !cert.equals(signingCert)) {         signed = cert;         break;       }     }      return signed;   }    /**    * A helper method for certificate re-ordering.    * Finds the certificate in the list of certificates that signed the signedCert.    */   private X509Certificate findSigner(X509Certificate signedCert, List certificates) {     X509Certificate signer = null;      for(X509Certificate cert : certificates) {       Principal certSubjectDN = cert.getSubjectDN();       Principal issuerDN = signedCert.getIssuerDN();       if(certSubjectDN.equals(issuerDN)) {         signer = cert;         break;       }     }      return signer;   } } 

To use it, just get the SSLSocketFactory and apply it, eg:

with HttpsURLConnection

KeyPinStore keystore = KeyPinStore.getInstance(); SSLSocketFactory sslSF = keystore.getContext().getSocketFactory(); URL url = new URL("https://certs.cac.washington.edu/CAtest/"); HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection(); urlConnection.setSSLSocketFactory(sslSF); InputStream in = urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out); 

with Volley

KeyPinStore keystore = KeyPinStore.getInstance(); SSLSocketFactory sslSF = keystore.getContext().getSocketFactory(); RequestQueue mRequestQueue = Volley.newRequestQueue(context, new HurlStack(null, sslSF)); 


回答3:

Be aware to use the right CA certificate (do not use the site certificate) :



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!