2016-06-14 2 views
3

사실, 이번 주, 나는 다음과 같은OkHttp는 : SSLPeerUnverifiedException은 (모토로라 X.509 인증서

과 같은 질문
내 휴대 전화에서 OkHttpClient and Certificate Authority Validation in Android

이 서명 한 신뢰할 수있는 인증서를 찾지 못했습니다 Android 4.1.2), 모든 DigiCert CA를 비활성화합니다 (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(); 
    } 
} 

내 애플 다음 로그 캣을 얻었다 (죄송합니다 너무 오래 때문에 나는 잘라야)

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이 호출되고 그 안에 이 던졌습니다.
  3. .certificatePinner(certificatePinner) 만 제거되었습니다. (너무 오래 내가 잘라 때문에) 아래 onResponse

로그 캣은 :

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

그래서, 내 질문은 :

  1. 내 질문의 시작 부분에있는 링크와 동일 (실제로 그의 첫 번째 문제);
  2. certificatePinnergetSSLSocketFactory과 함께 사용하면 내 앱이 javax.net.ssl.SSLPeerUnverifiedException: Failed to find a trusted cert that signed X.509 Certificate이되는 이유는 무엇입니까? 이 SSLPeerUnverifiedException의 내부 메시지는 this JavaDoc에 언급 된 Certificate pinning failure!과 다릅니다.

UPDATE : 1 문제에 대한

:

이 문제 (시스템/효과적이지 사용자 신뢰할 수있는 자격 증명) 만 안드로이드 4.1를 실행하는 내 휴대폰에서 일어난 것 같은데. 2, 02 장치를 확인 했으므로 제조업체에 문의해야합니다.

"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" 아래 @Robert의 코멘트에서, 나는 루트 대신, 다음 스크린 샷과 같이 체인을 완성, 그리고 비록 getSSLSocketFactory 안에 내가 getResources().openRawResource(R.raw.github_rootca);

로 변경 CA 수출 : 2 문제에 대한

이제 제 2 호가 해결되었습니다!

enter image description here

+0

첫 번째 "ques ". – Robert

+0

@ 로버트 : [첫 번째 문제는] [이 질문] (http://stackoverflow.com/questions/37772360/okhttpclient-and-certificate-authority-validation-in-android)과 같습니다. 시스템 CA를 비활성화했기 때문에'.sslSocketFactory (getSSLSocketFactory())'가 제거되었지만'onResponse'는 여전히 – BNK

답변

1

인증서 피닝 따라서 인증서가 고정 된 인증서와 일치해야하는 사용의 TrustManager 신뢰해야하는 추가 보안이다.

DigiCert CA 인증서를 사용 중지했기 때문에 해당 인증서는 신뢰할 수있는 TrustManager에서 신뢰하지 않으므로 SSLPeerUnverifiedException이 표시됩니다.

동작 나는 CertificatePinner의 JavaDoc을에 설명되어 있습니다 설명 : 자체 서명 된 인증서

에 대한

같은 인증서가 승인되지 않은 경우 CertificatePinner는 자체 서명 된 인증서를 고정하는 데 사용할 수 없습니다 TrustManager에 의해.

+0

을 호출했습니다.sslSocketFactory (getSSLSocketFactory())'제거 됨, 'SSLPeerUnverifiedException'이 수신되지 않음 – BNK

+0

github이 자체 서명하지 않은 인증서를 사용한다고 생각합니다 – BNK

+0

문서 링크의 예외 메시지는'SSLPeerUnverifiedException : Certificate pinning failure!'입니다. SSLPeerUnverifiedException : X.509 인증서에 서명 한 신뢰할 수있는 인증서를 찾지 못했습니다. ' – BNK

2

당신은 당신의 기본 시스템 인증서 trustore, (사용 여부를 사용자 정의 trustore없이 고정 인증서) 피어 인증서를 확인하는 것이 가능하지 않기 때문에 당신은 항상,

    를 예외를 얻을 것이다를 사용하지 않도록 설정하면 당신이 Certificate pinning을 사용 해달라고하고 default system certificate trustore이 연결은 Certificate pinning을 사용 해달라고하면 OK
  • 될 것입니다합니다 (Digicert 높은 보증 EV 루트 CA와) working하고있는 경우
  • 당신의 default system certificate trustore는 012,361,991입니다연결이
  • 당신이 인증서는 고정 및 기본 시스템 인증서 trustore가 연결이 될 것입니다합니다 (Digicert 높은 보증 EV 루트 CA에) 노력하고 있습니다 사용하는 경우 실패합니다 OK
  • Certificate pinning하고 default system certificate trustore를 사용하는 경우 당신이 Certificate pinning를 사용하는 경우 연결이
  • 실패합니다 not working하고 당신의 default system certificate trustorenot working 그리고 당신은 설정 custom trustore with the digicert CA 연결이 될 것입니다 OK
  • Certificate pinning를 사용하는 경우와 default system certificate trustore is not working 당신은 당신의 연결

예 (자바) (OkHttp3 CustomTrust example에 따라)를 사용하여 인증서 고정 +의 SSLSocketFactory + 사용자 정의의 트러스트

(digicert 실패합니다 setup a custom trustore without the digicert CA .cer에는 Digicert High Assurance EV 루트 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 맞아. 안드로이드 시스템의 신뢰할 수있는 CA에서 특정 인증서가 사용 중지 된 경우에도 휴대 전화에서 연결이 이루어집니다. – Ashwin

+0

@Ashwin 및 vzamanillo, 제 1 호에 대한 제 업데이트 된 답변을 읽어주세요. – BNK

관련 문제