2014-01-23 3 views
7

Ich erhalte diesen Fehler nur auf (vielleicht einigen) 2.3.x-Geräten. Es funktioniert für alle anderen Geräte, auf denen eine Android-Version installiert ist.Android 2.3.x javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Vertrauensanker für Zertifizierungspfad nicht gefunden

Hier ist meine 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); 
} 

}

Wenn ich den Befehl ausführen:

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

erhalte ich die Antwort:

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

es so wieder in Ordnung ... Aber es gibt immer noch mir diesen Fehler für die 2.3.x-Geräte, die ich getestet habe.

Ich erhalte eine Ausnahme nach diesem Punkt:

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

Hier ist die Ausnahme:

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) 

So wie ich rufen werde das hier ist:

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

Es funktioniert für alle anderen Geräte mit einer Android-Version über 2.3.x (von dem, was ich getestet habe).

Die Android-Dokumentation scheint nichts zum Thema 2.3 Kompatibilität geschrieben zu haben.

Antwort

4

Sie müssen dem Android-System mitteilen, dass es Ihrem Zertifikat vertrauen soll. Ihr Problem ist, dass Android nach 2.3 Ihr Zertifikat akzeptiert, da es in der Liste der vertrauenswürdigen Zertifikate enthalten ist, aber auf den vorherigen Versionen nicht enthalten ist, so gibt es das Problem.

Ich empfehle Ihnen, wie auf der Android-Dokumentation zu tun:

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

ich das gleiche tue, und es funktioniert einwandfrei auf alle Geräte mit Android 2.3 und unten, und das Zertifikat von meiner Seite ein Privat.

Versuchen Sie es einfach und sagen Sie mir, ob es jetzt funktioniert.

Hoffe es hilft dir!

+0

verwendete ich den gleichen Code google doc nehmen, aber es sagt '/load-der.crt: öffnen, ist fehlgeschlagen: ENOENT (Keine solche Datei oder das Verzeichnis)' – Naz141

+0

Naz141, müssen Sie haben das Zertifikat, das Sie akzeptieren möchten, in Ihr Projekt eingefügt (in diesem Fall die Datei "load-der.crt"). Sie können es beispielsweise aus dem Ordner "Assets" laden. – zapotec

-1

Beachten Sie, das Recht CA Zertifikat zu verwenden (mit Angabe der Site-Zertifikat nicht verwenden): You have to select the CA certificate, not the site certificate

1

Falls jemand die Antwort benötigen, fand ich endlich die Antwort nach 2 Tagen von Google. Grundsätzlich müssen wir TrustManager verwenden, um den CAs in unserem KeyStore zu vertrauen. Guthaben an https://github.com/delgurth für den CustomTrustManager.

Bitte beachten: 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; 
    } 
} 

, es zu benutzen, nur um die SSLSocketFactory und wenden Sie es, zum Beispiel:

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

mit Volley

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