【问题标题】:Okhttp3 - Accept all certificates and use a certificatePinnerOkhttp3 - 接受所有证书并使用 certificatePinner
【发布时间】:2017-07-06 10:40:35
【问题描述】:

我正在尝试固定我的服务器的自签名证书。 我的 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();

【问题讨论】:

    标签: java android ssl okhttp3


    【解决方案1】:
    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);
      }
    }
    

    【讨论】:

    • 虽然此答案可能是正确且有用的,但如果您 include some explanation along with it 解释它如何帮助解决问题,则最好。如果有变化(可能不相关)导致它停止工作并且读者需要了解它曾经是如何工作的,这在未来变得特别有用。
    • @KevinBrown 你是对的,只是想帮忙,但真的让这里的人不高兴。
    【解决方案2】:

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

    例如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 要求您的域仅使用您受信任的自签名证书。

    【讨论】:

    • 您提供的代码确实帮助了我,尽管它并没有完全回答我原来的问题。感谢您的帮助,我最终创建了一个有效的 TrustManager。
    • 是的,我试图回答我认为你正在尝试做的事情,添加一个自签名证书,这样你就有了一个安全的连接,但专门针对这一点。接受所有证书然后尝试固定似乎不是典型的用例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-20
    • 1970-01-01
    • 1970-01-01
    • 2013-04-11
    • 1970-01-01
    相关资源
    最近更新 更多