Mempercayai semua sertifikat dengan okHttp

111

Untuk tujuan pengujian, saya mencoba menambahkan pabrik soket ke klien okHttp saya yang mempercayai segalanya saat proxy disetel. Ini telah dilakukan berkali-kali, tetapi implementasi saya dari pabrik soket tepercaya tampaknya kehilangan sesuatu:

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);

Tidak ada permintaan yang dikirim dari aplikasi saya dan tidak ada pengecualian yang dicatat sehingga tampaknya gagal secara diam-diam dalam okHttp. Setelah penyelidikan lebih lanjut, tampaknya ada Pengecualian yang ditelan di okHttp Connection.upgradeToTls()saat jabat tangan dipaksa. Pengecualian yang diberikan kepada saya adalah:javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.

Kode berikut menghasilkan SSLContextyang berfungsi seperti pesona dalam membuat SSLSocketFactory yang tidak menampilkan pengecualian apa pun:

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();
}

Masalahnya adalah saya mencoba menghapus semua dependensi Apache HttpClient dari aplikasi saya sepenuhnya. Kode yang mendasari dengan Apache HttpClient untuk menghasilkan SSLContexttampaknya cukup mudah, tetapi saya jelas kehilangan sesuatu karena saya tidak dapat mengkonfigurasi saya SSLContextuntuk mencocokkan ini.

Adakah yang bisa menghasilkan implementasi SSLContext yang melakukan apa yang saya inginkan tanpa menggunakan Apache HttpClient?

seato
sumber
2
"Untuk tujuan pengujian, saya mencoba menambahkan pabrik soket ke klien okHttp saya yang mempercayai segalanya" - apa yang membuat Anda berpikir bahwa ini adalah ide yang bagus?
CommonsWare
1
Itu hanya dipanggil di jaringan yang saya percayai sepenuhnya.
seato
Hal ini menyebabkanjava.net.SocketTimeoutException: Read timed out
IgorGanapolsky

Jawaban:

250

Untuk berjaga-jaga jika ada yang jatuh di sini, satu-satunya solusi yang berhasil untuk saya adalah membuat hal OkHttpClientserupa yang dijelaskan di sini .

Ini kodenya:

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);
  }
}
sonxurxo
sumber
15
Kenapa SSLtidak TLS?
IgorGanapolsky
1
Terima kasih banyak untuk ini! Dokumentasi tentang retrofit (menggunakan okhttp) kurang, contoh kode semacam ini menghemat banyak waktu saya lagi dan lagi.
Warpzit
8
Perhatikan bahwa pendekatan ini tidak berfungsi lagi dengan versi OkHttp saat ini. Dengan 3.1.1 tampaknya benar-benar rusak. Dari 3.1.2 dan seterusnya, X509TrustManager.getAcceptedIssuers()harus mengembalikan larik kosong, bukan null. Untuk informasi lebih lanjut, lihat komit ini (gulir ke bawah dan lihat catatan di bawah RealTrustRootIndex.java).
jbxbergdev
12
Saya sudah mencoba ini, tetapi saya masih mendapatkan Handshake failedpengecualian. Ada saran?
Esteban
1
contoh dari OkHttpClient: github.com/square/okhttp/blob/master/samples/guide/src/main/…
ilyamuromets
13

Metode berikut tidak digunakan lagi

sslSocketFactory(SSLSocketFactory sslSocketFactory)

Pertimbangkan untuk memperbaruinya menjadi

sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)
Jakub N
sumber
13

Perbarui OkHttp 3.0, getAcceptedIssuers()fungsinya harus mengembalikan array kosong, bukan null.

Ervin Zhang
sumber
2
@Override public X509Certificate [] getAcceptedIssuers () {kembalikan X509Certificate baru [] {}; // StackOverflow}
Ervin Zhang
11

SSLSocketFactory tidak mengekspos X509TrustManager-nya, yang merupakan bidang yang dibutuhkan OkHttp untuk membuat rantai sertifikat yang bersih. Metode ini malah harus menggunakan refleksi untuk mengekstrak manajer kepercayaan. Aplikasi sebaiknya memilih untuk memanggil sslSocketFactory (SSLSocketFactory, X509TrustManager), yang menghindari refleksi seperti itu.

Sumber: Dokumentasi 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[]{};
        }
    });
Tharindu Bandara
sumber
Ini tidak berfungsi untuk sertifikat yang ditandatangani sendiri. Menyebabkan kesalahan 401 Unauthorized .
IgorGanapolsky
5
Dari mana sslContextasalnya
Anonsage
3
Bagaimana Anda menginisialisasi sslContext?
kiltek
10

Ini adalah solusi sonxurxo di Kotlin, jika ada yang membutuhkannya.

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()
}

doodla
sumber
7

Saya membuat fungsi ekstensi untuk Kotlin. Tempel di mana pun Anda suka dan impor saat membuat 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
}

gunakan seperti ini:

val okHttpClient = OkHttpClient.Builder().apply {
    // ...
    if (BuildConfig.DEBUG) //if it is a debug build ignore ssl errors
        ignoreAllSSLErrors()
    //...
}.build()
George Shalvashvili
sumber
Anda sebenarnya dapat menggunakannya seperti ini: OkHttpClient.Builder () .ignoreAllSSLErrors () .connectTimeout (api.timeout, TimeUnit.SECONDS) .writeTimeout (api.timeout, TimeUnit.SECONDS) dll, bagaimanapun juga itu adalah pola pembangun
Emanuel Moecklin
1

Ini adalah solusi Scala jika ada yang membutuhkannya

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()

}

netta
sumber
-12

Anda tidak boleh mencoba menimpa validasi sertifikat dalam kode! Jika Anda perlu melakukan pengujian, gunakan CA internal / pengujian dan instal sertifikat root CA di perangkat atau emulator. Anda dapat menggunakan BurpSuite atau Charles Proxy jika tidak tahu cara menyiapkan CA.

Tony Trummer
sumber
37
Oh ayolah. Bagaimana jika Anda bekerja untuk perusahaan dan mereka memaksa Anda untuk terhubung ke server HTTPS mereka melalui VPN. Bagaimana Anda akan mensimulasikan itu dalam tes?
IgorGanapolsky
5
Ya, ini pencucian babi yang paling murni. Kami hanya mengkompilasi ini dalam versi uji coba, jadi tidak ada bahaya.
Adam