2014-01-23 107 views
7

我僅在2.3.x設備上(可能有些)收到此錯誤。它適用於運行Android版本以上的任何其他設備。Android 2.3.x javax.net.ssl.SSLHandshakeException:java.security.cert.CertPathValidatorException:未找到認證路徑的信任錨點

這裏是我的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<Object, Object> 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); 
} 

}

當我運行命令:

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

我得到的迴應:

-- 
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) 
--- 

所以返回好嗎...但它仍然給我爲我測試的2.3.x設備發生這個錯誤。

我這點後得到一個異常:

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

這裏是個例外:

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) 

我打電話,這是這裏的方式:

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

它適用於任何其他運行2.3.x以上Android版本的設備(從我測試過的)。

Android文檔似乎沒有寫入關於2.3兼容性的主題。

回答

4

你必須告訴Android系統信任你的證書。您的問題在於2.3之後的Android接受您的證書,因爲它包含在可信證書列表中,但是之前的版本不包含在內,所以存在這個問題。

我建議你做這樣的Android文檔上:

// 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); 

我做相同的,這是正常工作的每個設備上,與Android 2.3及以下,和我的網站的證書是私人的。

試試吧,並告訴我它現在是否正在工作。

希望它可以幫助你!

+0

我用同樣的代碼,以谷歌文檔,但它說 '/load-der.crt:打開失敗:ENOENT(沒有這樣的文件或目錄)' – Naz141

+0

Naz141,你需要在您的項目中包含了您想要接受的證書(在本例中爲文件「load-der.crt」)。您可以加載它,例如從資產文件夾。 – zapotec

-1

注意使用正確的CA證書(不使用站點證書): You have to select the CA certificate, not the site certificate

1

如果有人需要的答案,我終於找到了答案後,谷歌的2天。基本上我們需要使用自定義的TrustManager來信任我們的KeyStore中的CA.用於CustomTrustManager的https://github.com/delgurth

請參考: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 < certificates.length; i++) { 
      CertificateFactory cf = CertificateFactory.getInstance("X.509"); 
      InputStream caInput = new BufferedInputStream(Application.context.getAssets().open("certificate/" + certificates[i])); 
      Certificate ca; 
      try { 
       ca = cf.generateCertificate(caInput); 
       System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); 
      } finally { 
       caInput.close(); 
      } 

      // Create a KeyStore containing our trusted CAs 
      keyStore.setCertificateEntry("ca" + i, ca); 
     } 

     // Use custom trust manager to trusts the CAs in our KeyStore 
     TrustManager[] trustManagers = {new CustomTrustManager(keyStore)}; 

     // Create an SSLContext that uses our TrustManager 
     // SSLContext context = SSLContext.getInstance("TLS"); 
     sslContext.init(null, trustManagers, null); 
    } 

    public SSLContext getContext() { 
     return sslContext; 
    } 
} 

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 < chain.length; i++) { 
      if (validateCert(reorderedChain[i])) { 
      return; 
      } 
     } 
     throw originalException; 
     } catch(Exception ex) { 
     ex.printStackTrace(); 
     throw originalException; 
     } 
    } 

    } 

    /** 
    * Checks if we have added the certificate in the trustStore, if that's the case we trust the certificate 
    * @param x509Certificate the certificate to check 
    * @return true if we know the certificate, false otherwise 
    * @throws KeyStoreException on problems accessing the key store 
    */ 
    private boolean validateCert(final X509Certificate x509Certificate) throws KeyStoreException { 
    return trustStore.getCertificateAlias(x509Certificate) != null; 
    } 

    /** 
    * Puts the certificate chain in the proper order, to deal with out-of-order 
    * certificate chains as are sometimes produced by Apache's mod_ssl 
    * @param chain the certificate chain, possibly with bad ordering 
    * @return the re-ordered certificate chain 
    */ 
    private X509Certificate[] reorderCertificateChain(X509Certificate[] chain) { 

    X509Certificate[] reorderedChain = new X509Certificate[chain.length]; 
    List<X509Certificate> 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<X509Certificate> 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<X509Certificate> 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<X509Certificate> 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; 
    } 
} 

要使用它,只是得到的SSLSocketFactory和應用它,如:

與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); 

與排球

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