2016-06-14 2 views
3

На самом деле, на этой неделе, у меня есть один и тот же вопрос, что и следующийOkHttp: SSLPeerUnverifiedException Не удалось найти доверенный сертификат, что подписанный сертификат X.509

OkHttpClient and Certificate Authority Validation in Android

В мой телефон (Motorola, Android 4.1.2), я отключил все ЦС DigiCert (внутри Settings - Security - Trusted credentials - System).

Мой код ниже:

public class CertPinActivity extends AppCompatActivity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_cert_pin); 

     try { 
      CertificatePinner certificatePinner = new CertificatePinner.Builder() 
        .add("github.com", "sha256/WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=") 
        .build(); 
      OkHttpClient client = new OkHttpClient.Builder() 
        .sslSocketFactory(getSSLSocketFactory()) 
        .certificatePinner(certificatePinner) 
        .build(); 

      Request request = new Request.Builder() 
        .url("https://github.com/square/okhttp/wiki/HTTPS") 
        .build(); 
      client.newCall(request).enqueue(new Callback() { 
       @Override 
       public void onFailure(Call call, IOException e) { 
        Log.e("onFailure","-------------------------------------------------"); 
        e.printStackTrace(); 
       } 

       @Override 
       public void onResponse(Call call, Response response) throws IOException { 
        Log.d("onResponse", response.body().string()); 
       } 
      }); 
     } catch (Exception e){ 
      e.printStackTrace(); 
     } 
    } 

    private SSLSocketFactory getSSLSocketFactory() 
      throws CertificateException, KeyStoreException, IOException, 
      NoSuchAlgorithmException, KeyManagementException { 
     CertificateFactory cf = CertificateFactory.getInstance("X.509"); 
     InputStream caInput = getResources().openRawResource(R.raw.github); // this is exported from Chrome then stored inside \app\src\main\res\raw path 
     Certificate ca = cf.generateCertificate(caInput); 
     caInput.close(); 
     KeyStore keyStore = KeyStore.getInstance("BKS"); 
     keyStore.load(null, null); 
     keyStore.setCertificateEntry("ca", ca); 
     String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); 
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); 
     tmf.init(keyStore); 
     SSLContext sslContext = SSLContext.getInstance("TLS"); 
     sslContext.init(null, tmf.getTrustManagers(), null); 
     return sslContext.getSocketFactory(); 
    } 
} 

Мое приложение получил следующее LogCat (жаль, что я укоротить, потому что это слишком долго)

06-14 09:10:10.065 30176-30211/com.example.okhttps3 E/onFailure: ------------------------------------------------- 
06-14 09:10:10.065 30176-30211/com.example.okhttps3 W/System.err: javax.net.ssl.SSLPeerUnverifiedException: Failed to find a trusted cert that signed X.509 Certificate: 
06-14 09:10:10.065 30176-30211/com.example.okhttps3 W/System.err: [ 
06-14 09:10:10.065 30176-30211/com.example.okhttps3 W/System.err: [ 
06-14 09:10:10.065 30176-30211/com.example.okhttps3 W/System.err: Version: V3 
06-14 09:10:10.065 30176-30211/com.example.okhttps3 W/System.err: Subject: CN=DigiCert SHA2 Extended Validation Server CA,OU=www.digicert.com,O=DigiCert Inc,C=US 
06-14 09:10:10.065 30176-30211/com.example.okhttps3 W/System.err: Signature Algorithm: SHA256WithRSAEncryption, params unparsed, OID = 1.2.840.113549.1.1.11 
06-14 09:10:10.065 30176-30211/com.example.okhttps3 W/System.err: Key: 
................................................................................... 
06-14 09:17:56.813 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.internal.tls.CertificateChainCleaner$BasicCertificateChainCleaner.clean(CertificateChainCleaner.java:132) 
06-14 09:17:56.813 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.CertificatePinner.check(CertificatePinner.java:149) 
06-14 09:17:56.813 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.internal.io.RealConnection.connectTls(RealConnection.java:252) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.internal.io.RealConnection.establishProtocol(RealConnection.java:196) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.internal.io.RealConnection.buildConnection(RealConnection.java:171) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.internal.io.RealConnection.connect(RealConnection.java:111) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.internal.http.StreamAllocation.findConnection(StreamAllocation.java:187) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:123) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.internal.http.StreamAllocation.newStream(StreamAllocation.java:93) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.internal.http.HttpEngine.connect(HttpEngine.java:296) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.internal.http.HttpEngine.sendRequest(HttpEngine.java:248) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.RealCall.getResponse(RealCall.java:243) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:201) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:163) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.RealCall.access$100(RealCall.java:30) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.RealCall$AsyncCall.execute(RealCall.java:127) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 
06-14 09:17:56.823 5934-5984/com.example.okhttps3 W/System.err:  at java.lang.Thread.run(Thread.java:856) 

Однако onResponse вызывается в следующих случаях:

  1. И .sslSocketFactory(getSSLSocketFactory()) и .certificatePinner(certificatePinner) удален;
  2. Только .sslSocketFactory(getSSLSocketFactory()) удален; (на самом деле, с эмулятором, когда система CA отключена, onFailure называется и внутри нее java.security.cert.CertPathValidatorException: Trust anchor for certification path not found брошен)
  3. Только .certificatePinner(certificatePinner) удален.

Logcat с onResponse, как показано ниже (слишком долго, так что я усечение):

06-14 09:06:23.143 26571-26616/com.example.okhttps3 D/onResponse: <!DOCTYPE html> 
                    <html lang="en" class=""> 
                    ..... 

Итак, мои вопросы:

  1. То же, как связь в начале в моем вопросе (фактически его 1-й выпуск);
  2. Почему мое приложение получает javax.net.ssl.SSLPeerUnverifiedException: Failed to find a trusted cert that signed X.509 Certificate, когда certificatePinner используется с getSSLSocketFactory? Обратите внимание, что внутреннее сообщение этого SSLPeerUnverifiedException отличается от Certificate pinning failure!, как указано в this JavaDoc.

UPDATE:

Для 1-го выпуска:

Похоже, этот вопрос (System/User Доверенные учетные данные не вступили в силу) произошло только в моих телефонов, которые работают Android 4.1. 2, я проверил 02 устройств, поэтому я должен связаться с производителем.

Для 2-го выпуска:

из комментария @Robert ниже "I assume you have to include the full certificate chain (or the root certificate if the server sends you the chain), not only the leave certificate", я экспортировал Root CA вместо этого, как скриншоте ниже, хотя и не завершена цепи, а внутри getSSLSocketFactory я изменил getResources().openRawResource(R.raw.github_rootca);

Теперь моя вторая проблема решена!

enter image description here

+0

Я не понимаю ваши первые «вопросы» Тион». – Robert

+0

@Robert: похоже, [этот вопрос] (http://stackoverflow.com/questions/37772360/okhttpclient-and-certificate-authority-validation-in-android), на самом деле его первый выпуск. Поскольку я отключил CA системы, однако, хотя '.slSocketFactory (getSSLSocketFactory())' удален, 'onResponse' все еще называется – BNK

ответ

1

пиннинга Сертификат является дополнительной ценной бумагой, поэтому сертификат должен быть доверенным используется TrustManager и он должен соответствовать возлагали сертификат.

Поскольку вы отключили сертификаты CA DigiCert, сертификату не доверяет доверенный TrustManager, и поэтому вы получаете SSLPeerUnverifiedException.

поведение, как я описал документирован в JavaDoc из CertificatePinner:

Примечания о самоподписанных сертификатах

CertificatePinner не может быть использован, чтобы прикрепить самостоятельно подписанный сертификат, если такой сертификат не принимается по TrustManager.

+0

Как и в моем вопросе, если только'.sslSocketFactory (getSSLSocketFactory()) 'removed,' SSLPeerUnverifiedException' не получил – BNK

+0

Я думаю, что сертификат, который github использует, не является самоподписанным – BNK

+0

Сообщение об исключении в ссылке документа: 'SSLPeerUnverifiedException: сбой при выдаче сертификата! ', однако мое приложение получило' SSLPeerUnverifiedException: Не удалось найти доверенный сертификат, который подписал сертификат X.509' – BNK

2

Если отключить по умолчанию trustore сертификат системы, (с использованием или не сертификат прижав без пользовательского trustore) вы всегда получите исключение, потому что не будет возможности проверить сертификат партнера, так

  • Если вы не используете Certificate pinning и ваш default system certificate trustore является working (с Digicert High Assurance EV Root CA) подключение будет OK
  • Если вы не используете Certificate pinning и ваш default system certificate trustore является not working подключение будет FAIL
  • Если вы используете сертификат пиннинг и используемое по умолчанию trustore сертификата системы работает (с Digicert High Assurance EV Root CA) подключение будет OK
  • Если вы используете Certificate pinning и ваш default system certificate trustore это not working подключение будет FAIL
  • Если вы используете Certificate pinning и ваш default system certificate trustore является not working и вы настроить custom trustore with the digicert CA ваше соединение будет OK
  • Если вы используете Certificate pinning и ваш default system certificate trustore is not working и вы setup a custom trustore without the digicert CA ваше соединение будет FAIL

пример (Java) с использованием сертификата прижав + SSLSocketFactory + пользовательские TrustManager (на основе OkHttp3 CustomTrust example)

(DigiCert .cer содержит Digicert High Assurance EV Root CA)

import java.io.File; 
import java.io.FileInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.security.GeneralSecurityException; 
import java.security.KeyStore; 
import java.security.cert.Certificate; 
import java.security.cert.CertificateFactory; 
import java.util.Arrays; 
import java.util.Collection; 

import javax.net.ssl.KeyManagerFactory; 
import javax.net.ssl.SSLContext; 
import javax.net.ssl.SSLSocketFactory; 
import javax.net.ssl.TrustManager; 
import javax.net.ssl.TrustManagerFactory; 
import javax.net.ssl.X509TrustManager; 

import org.junit.Test; 

import junit.framework.TestCase; 
import okhttp3.Call; 
import okhttp3.Callback; 
import okhttp3.CertificatePinner; 
import okhttp3.OkHttpClient; 
import okhttp3.Request; 
import okhttp3.Response; 

public class OkHttpTest extends TestCase { 

    @Test 
    public void test() { 
     try { 
      CertificatePinner certificatePinner = new CertificatePinner.Builder() 
        .add("github.com", "sha256/WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=").build(); 

      X509TrustManager trustManager; 
      SSLSocketFactory sslSocketFactory; 

      try { 
       trustManager = trustManagerForCertificates(new FileInputStream(new File("/tmp/digicert.cer"))); 
       SSLContext sslContext = SSLContext.getInstance("TLS"); 
       sslContext.init(null, new TrustManager[] { trustManager }, null); 
       sslSocketFactory = sslContext.getSocketFactory(); 
      } catch (GeneralSecurityException e) { 
       throw new RuntimeException(e); 
      } 

      OkHttpClient client = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, trustManager) 
        .certificatePinner(certificatePinner).build(); 

      Request request = new Request.Builder().url("https://github.com/square/okhttp/wiki/HTTPS").build(); 

      client.newCall(request).enqueue(new Callback() { 
       @Override 
       public void onFailure(Call call, IOException e) { 
        e.printStackTrace(); 
       } 

       @Override 
       public void onResponse(Call call, Response response) throws IOException { 
        System.out.println("onResponse: " + response.body().string()); 
       } 
      }); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    private X509TrustManager trustManagerForCertificates(InputStream in) throws GeneralSecurityException { 
     CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); 
     Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in); 
     if (certificates.isEmpty()) { 
      throw new IllegalArgumentException("expected non-empty set of trusted certificates"); 
     } 

     // Put the certificates a key store. 
     char[] password = "password".toCharArray(); // Any password will work. 
     KeyStore keyStore = newEmptyKeyStore(password); 
     int index = 0; 
     for (Certificate certificate : certificates) { 
      String certificateAlias = Integer.toString(index++); 
      keyStore.setCertificateEntry(certificateAlias, certificate); 
     } 

     // Use it to build an X509 trust manager. 
     KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
     keyManagerFactory.init(keyStore, password); 
     TrustManagerFactory trustManagerFactory = TrustManagerFactory 
       .getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
     trustManagerFactory.init(keyStore); 
     TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); 
     if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { 
      throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); 
     } 
     return (X509TrustManager) trustManagers[0]; 
    } 

    private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { 
     try { 
      KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
      InputStream in = null; 
      keyStore.load(in, password); 
      return keyStore; 
     } catch (IOException e) { 
      throw new AssertionError(e); 
     } 
    } 
} 
+1

Спасибо, с моим кодом, если я удалю как '.sslSocketFactory (getSSLSocketFactory())', так и '.certificatePinner (certificatePinner)', в моем телефоне 'onResponse' все еще вызывается в эмуляторе' onFailure' под названием – BNK

+1

@vzamanillo: He верно. Соединение выполняется на моем телефоне, даже когда конкретный сертификат отключен в доверенных центрах системы Android. – Ashwin

+0

@Ashwin и vzamanillo, пожалуйста, прочитайте мой обновленный ответ о первом выпуске, спасибо – BNK

Смежные вопросы