Okhttp3 - 接受所有证书并使用certificatePinner

时间:2017-02-17 03:28:53

标签: java android ssl okhttp3

我试图固定我服务器的自签名证书。 我的OkHttpClient有两个参数,第一个是ssl Socket Factory:

final TrustManager[] trustAllCerts = new TrustManager[] {
        new X509TrustManager() {
            @SuppressLint("TrustAllX509TrustManager")
            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {}

            @SuppressLint("TrustAllX509TrustManager")
            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {}

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                  return new X509Certificate[0];
            }
         }
     };

// Install the all-trusting trust manager
SSLContext sslContext;
try {
     sslContext = SSLContext.getInstance("SSL");
     sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
 } catch (NoSuchAlgorithmException | KeyManagementException e) {
     e.printStackTrace();
     FirebaseCrash.report(e);
     return null;
}

// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

其次是证书引脚:

new CertificatePinner.Builder()
    .add("bogus.com", "sha1/BOGUS")
    .build()

注意:如果我没有添加certificatePinner,那么一切正常。问题是当请求被执行时,CertificatePinner.check()被调用:

if (pins.isEmpty()) return;

显然,如果我设置了一个(非空的)certificatePinner,该方法将不会停在那里并将继续。然后继续检查 "至少有一个固定为我的主机名的证书是可信证书"

问题是我在getAcceptedIssuers中为我的TrustManager传递了一个空数组 - 这意味着自签名证书将触发异常,因为它没有明确地信任" getAcceptedIssues"。似乎无法固定在" getAcceptedIssuers"中明确信任的证书。

有没有办法解决这个问题?这是设计吗?

这就是我构建OkHttpClient的方式:

OkHttpClient client = new OkHttpClient.Builder()
    .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
    .certificatePinner(certPinner)
    .readTimeout(10, TimeUnit.SECONDS)
    .connectTimeout(10, TimeUnit.SECONDS)
    .build();

2 个答案:

答案 0 :(得分:6)

private static OkHttpClient getUnsafeOkHttpClient() {
  try {
    // Create a trust manager that does not validate certificate chains
    final TrustManager[] trustAllCerts = new TrustManager[] {
        new X509TrustManager() {
          @Override
          public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
          }

          @Override
          public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
          }

          @Override
          public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[]{};
          }
        }
    };

    // Install the all-trusting trust manager
    final SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
    // Create an ssl socket factory with our all-trusting manager
    final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]);
    builder.hostnameVerifier(new HostnameVerifier() {
      @Override
      public boolean verify(String hostname, SSLSession session) {
        return true;
      }
    });

    OkHttpClient okHttpClient = builder
        .connectTimeout(15, TimeUnit.SECONDS)
        .writeTimeout(15, TimeUnit.SECONDS)
        .readTimeout(15, TimeUnit.SECONDS)
        .build();
    return okHttpClient;
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}

答案 1 :(得分:3)

TrustManager,CertificatePinner和Hostname验证都有不同但重要的事情。如果您想使用自签名证书但仍然具有安全性,而不是纯粹为了便于本地开发的自签名证书,那么您可能想要创建一个有效的TrustManager。

e.g。 https://github.com/yschimke/oksocial/blob/3757196cde420b9d0fe37cf385b66f4cdafb1ae1/src/main/java/com/baulsupp/oksocial/security/CertificateUtils.java#L19

  public static X509TrustManager load(List<File> serverCerts)
      throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException {
    return trustManagerForKeyStore(keyStoreForCerts(serverCerts));
  }

  public static X509TrustManager trustManagerForKeyStore(KeyStore ks)
      throws NoSuchAlgorithmException, KeyStoreException {
    TrustManagerFactory tmf =
        TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

    tmf.init(ks);

    return (X509TrustManager) tmf.getTrustManagers()[0];
  }

  public static KeyStore keyStoreForCerts(List<File> serverCerts)
      throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException {
    CertificateFactory cf = CertificateFactory.getInstance("X.509");

    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(null);

    for (int i = 0; i < serverCerts.size(); i++) {
      try (InputStream is = new FileInputStream(serverCerts.get(i))) {
        X509Certificate caCert = (X509Certificate) cf.generateCertificate(is);
        ks.setCertificateEntry("cacrt." + i, caCert);
      }
    }
    return ks;
  }

这将从加载系统证书开始,因此您的客户端仍可用于加载外部托管的图像等。

然后,您可以使用CertificatePinner要求只为您的域使用您信任的自签名证书。