Tin tưởng tất cả các chứng chỉ với okHttp


111

Đối với mục đích thử nghiệm, tôi đang cố gắng thêm một nhà máy sản xuất ổ cắm vào ứng dụng khách okHttp của mình tin cậy mọi thứ trong khi proxy được thiết lập. Điều này đã được thực hiện nhiều lần, nhưng việc triển khai nhà máy sản xuất ổ cắm đáng tin cậy của tôi dường như còn thiếu một số thứ:

class TrustEveryoneManager implements 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 null;
    }
}
OkHttpClient client = new OkHttpClient();

final InetAddress ipAddress = InetAddress.getByName("XX.XXX.XXX.XXX"); // some IP
client.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ipAddress, 8888)));

SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] trustManagers = new TrustManager[]{new TrustEveryoneManager()};
sslContext.init(null, trustManagers, null);
client.setSslSocketFactory(sslContext.getSocketFactory);

Không có yêu cầu nào được gửi ra khỏi ứng dụng của tôi và không có trường hợp ngoại lệ nào đang được ghi lại, vì vậy, có vẻ như nó đang không thành công trong okHttp. Khi điều tra thêm, có vẻ như có một Ngoại lệ bị nuốt chửng trong okHttp'sConnection.upgradeToTls() khi bắt tay đang bị ép buộc. Ngoại lệ tôi đang được đưa ra là:javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.

Đoạn mã sau tạo ra một mã SSLContexthoạt động giống như một sự quyến rũ trong việc tạo SSLSocketFactory không đưa ra bất kỳ ngoại lệ nào:

protected SSLContext getTrustingSslContext() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
    final SSLContextBuilder trustingSSLContextBuilder = SSLContexts.custom()
            .loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true; // Accepts any ssl cert whether valid or not.
                }
            });
    return trustingSSLContextBuilder.build();
}

Vấn đề là tôi đang cố gắng xóa hoàn toàn tất cả các phụ thuộc Apache HttpClient khỏi ứng dụng của mình. Mã cơ bản với Apache HttpClient để tạo ra SSLContextcó vẻ đủ đơn giản, nhưng tôi rõ ràng là thiếu một số thứ vì tôi không thể định cấu hìnhSSLContext để phù hợp với điều này.

Có ai có thể tạo ra một triển khai SSLContext mà không cần sử dụng Apache HttpClient không?


2
"Vì mục đích thử nghiệm, tôi đang cố gắng thêm một nhà máy sản xuất ổ cắm vào ứng dụng khách okHttp của tôi tin tưởng mọi thứ" - điều gì khiến bạn nghĩ rằng đây là một ý tưởng hay ngay từ đầu?
CommonsWare

1
Nó chỉ được gọi trên mạng mà tôi hoàn toàn tin tưởng.
seato

Điều này gây rajava.net.SocketTimeoutException: Read timed out
IgorGanapolsky

Câu trả lời:


250

Đề phòng bất kỳ ai rơi vào đây, giải pháp (duy nhất) phù hợp với tôi là tạo ra những thứ OkHttpClienttương tự được giải thích ở đây .

Đây là mã:

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.build();
    return okHttpClient;
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}

15
Tại sao SSLvà không TLS?
IgorGanapolsky

1
Cảm ơn rất nhiều vì chuyện này! Tài liệu về trang bị thêm (sử dụng okhttp) còn thiếu, loại mẫu mã này giúp tôi tiết kiệm rất nhiều thời gian lặp đi lặp lại.
Warpzit

8
Lưu ý rằng phương pháp này không hoạt động nữa với các phiên bản hiện tại của OkHttp. Với 3.1.1 nó dường như hoàn toàn bị hỏng. Từ 3.1.2 trở đi, X509TrustManager.getAcceptedIssuers()phải trả về một mảng trống thay vì null. Để biết thêm thông tin, hãy xem cam kết này (cuộn xuống và xem các ghi chú trong RealTrustRootIndex.java).
jbxbergdev

12
Tôi đã thử điều này, nhưng tôi vẫn nhận được Handshake failedngoại lệ. Bất kỳ đề xuất?
Esteban


13

Phương pháp sau không được dùng nữa

sslSocketFactory(SSLSocketFactory sslSocketFactory)

Cân nhắc cập nhật nó lên

sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)

13

Cập nhật OkHttp 3.0, getAcceptedIssuers()hàm phải trả về một mảng trống thay vì null.


2
@Override public X509Certificate [] getAcceptedIssuers () {return new X509Certificate [] {}; // StackOverflow}
Ervin Zhang

11

SSLSocketFactory không để lộ X509TrustManager của nó, đây là trường mà OkHttp cần để xây dựng một chuỗi chứng chỉ sạch. Phương pháp này thay vào đó phải sử dụng phản chiếu để trích xuất trình quản lý tin cậy. Các ứng dụng nên gọi sslSocketFactory (SSLSocketFactory, X509TrustManager) để tránh phản ánh như vậy.

Nguồn: tài liệu OkHttp

OkHttpClient.Builder builder = new OkHttpClient.Builder();

builder.sslSocketFactory(sslContext.getSocketFactory(),
    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[]{};
        }
    });

Điều này không hoạt động đối với chứng chỉ tự ký. Gây ra lỗi 401 Không được phép .
IgorGanapolsky

5
Từ đâu sslContextđến?
Anonsage

3
Làm thế nào để bạn khởi tạo sslContext?
kiltek

10

Đây là giải pháp của sonxurxo trong Kotlin, nếu ai cần.

private fun getUnsafeOkHttpClient(): OkHttpClient {
    // Create a trust manager that does not validate certificate chains
    val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
        override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        }

        override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        }

        override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
    })

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

    return OkHttpClient.Builder()
        .sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
        .hostnameVerifier { _, _ -> true }.build()
}

7

Tôi đã tạo một chức năng mở rộng cho Kotlin. Dán nó vào bất cứ nơi nào bạn thích và nhập nó trong khi tạo OkHttpClient.

fun OkHttpClient.Builder.ignoreAllSSLErrors(): OkHttpClient.Builder {
    val naiveTrustManager = object : X509TrustManager {
        override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
        override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) = Unit
        override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) = Unit
    }

    val insecureSocketFactory = SSLContext.getInstance("TLSv1.2").apply {
        val trustAllCerts = arrayOf<TrustManager>(naiveTrustManager)
        init(null, trustAllCerts, SecureRandom())
    }.socketFactory

    sslSocketFactory(insecureSocketFactory, naiveTrustManager)
    hostnameVerifier(HostnameVerifier { _, _ -> true })
    return this
}

sử dụng nó như thế này:

val okHttpClient = OkHttpClient.Builder().apply {
    // ...
    if (BuildConfig.DEBUG) //if it is a debug build ignore ssl errors
        ignoreAllSSLErrors()
    //...
}.build()

Bạn thực sự có thể sử dụng nó như thế này: OkHttpClient.Builder () .ignoreAllSSLErrors () .connectTimeout (api.timeout, TimeUnit.SECONDS) .writeTimeout (api.timeout, TimeUnit.SECONDS), v.v., xét cho cùng thì đó là một mẫu xây dựng
Emanuel Moecklin

1

Đây là giải pháp Scala nếu ai cần

def anUnsafeOkHttpClient(): OkHttpClient = {
val manager: TrustManager =
  new X509TrustManager() {
    override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String) = {}

    override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String) = {}

    override def getAcceptedIssuers = Seq.empty[X509Certificate].toArray
  }
val trustAllCertificates =  Seq(manager).toArray

val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCertificates, new java.security.SecureRandom())
val sslSocketFactory = sslContext.getSocketFactory()
val okBuilder = new OkHttpClient.Builder()
okBuilder.sslSocketFactory(sslSocketFactory, trustAllCertificates(0).asInstanceOf[X509TrustManager])
okBuilder.hostnameVerifier(new NoopHostnameVerifier)
okBuilder.build()

}


-12

Bạn không bao giờ nên tìm cách ghi đè xác thực chứng chỉ trong mã! Nếu bạn cần thực hiện kiểm tra, hãy sử dụng CA nội bộ / kiểm tra và cài đặt chứng chỉ gốc CA trên thiết bị hoặc trình giả lập. Bạn có thể sử dụng BurpSuite hoặc Charles Proxy nếu bạn không biết cách thiết lập CA.


37
Thôi nào. Điều gì sẽ xảy ra nếu bạn đang làm việc cho một công ty và họ buộc bạn phải kết nối với máy chủ HTTPS của họ thông qua VPN. Làm thế nào bạn sẽ mô phỏng điều đó trong một thử nghiệm?
IgorGanapolsky

5
Ya đây là nước rửa heo thuần khiết nhất. Chúng tôi chỉ biên dịch điều này trong các bản dựng thử nghiệm, vì vậy không có gì nguy hiểm.
Adam
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.