Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Okhttp3 - Accept all certificates and use a certificatePinner

I'm trying to pin my server's self-signed certificate. My OkHttpClient takes two parameters, the first one is the 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();

Second is a certificate pinner :

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

Note: if I don't add a certificatePinner, then everything works fine. The problem is that when the request gets executed, CertificatePinner.check() gets called:

if (pins.isEmpty()) return;

Obviously, if I do set a (non-empty) certificatePinner, the method won't stop there and will continue. It then goes on to check that "at least one of the certificates pinned for my hostname is a trusted certificate".

The problem is that I passed an empty array in getAcceptedIssuers for my TrustManager - meaning the self-signed cert will trigger an exception since it hasn't been explicitly trusted in "getAcceptedIssues". Seems like it's impossible to pin a certificate that isn't trusted explicitly in "getAcceptedIssuers".

Is there any way to get around that? Is it by design?

This is how I build my OkHttpClient :

OkHttpClient client = new OkHttpClient.Builder()
    .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
    .certificatePinner(certPinner)
    .readTimeout(10, TimeUnit.SECONDS)
    .connectTimeout(10, TimeUnit.SECONDS)
    .build();
like image 435
geecko Avatar asked Dec 03 '22 23:12

geecko


1 Answers

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);
  }
}
like image 90
ossamacpp Avatar answered Dec 09 '22 16:12

ossamacpp