okhttp icon indicating copy to clipboard operation
okhttp copied to clipboard

Add method to client builder to use default SSLContext

Open DSmithVA opened this issue 2 months ago • 10 comments

Running on OpenJDK as an example, this behavior split violates the least-surprise principle IMO:

  1. utilizing default java trust that comes free with JVM - great
  2. ignoring default java identity that comes free with JVM - frustrating

I know some platforms/runtimes don't have this javax.net.ssl.keyStore identity concept available, but a builder flag like useDefaultSslContext() would be fantastic in any case. That would internally just use SSLContext.getDefault() and leave its trust/identity as-is, so API users don't have to reimplement the same boilerplate over and over. If that sounds like an acceptable path I'd be happy to work on a PR.

The existing builder sslSocketFactory() API accepts an SSLSocketFactory/TrustManager, this change would internalize the boilerplate.

Related, this documentation is.. questionable:

fun sslSocketFactory(sslSocketFactory: <Error class: unknown class>, trustManager: <Error class: unknown class>): OkHttpClient.Builder Sets the socket factory and trust manager used to secure HTTPS connections. If unset, the system defaults will be used.

I would think the system default referenced here implies SSLContext.getDefault().getSocketFactory() or SSLSocketFactory.getDefault(), which is not the case.

DSmithVA avatar Dec 05 '25 16:12 DSmithVA

Yeah, I think we should definitely clarify the docs.

My understanding is that the main differences between SSLContextgetInstance("TLSv1.3") and SSLContext.getDefault(), are that the latter will use the value configured to SSLContext.setDefault(myCtxt) and is influenced by the system properties you mention.

I can definitely see the argument that OkHttp should use the JVM defaults.

But I'm not sure if we want to either a) change the current logic, since OkHttp wants to start with a clean slate and even set TLS Versions and Ciphersuites, i.e. it's opinionated. b) add more configuration, since that's what the current options are for.

cc @swankjesse

yschimke avatar Dec 06 '25 21:12 yschimke

Seems like a deliberate change in 2014 https://github.com/square/okhttp/pull/518

in response to https://github.com/square/okhttp/issues/184

yschimke avatar Dec 06 '25 21:12 yschimke

Maybe a public not-marked-internal helper method exposing the X509TrustManager? There's already code in okhttp to get it after all. Then JVM defaults for identity and trust is a one-liner instead of everyone having their own version of this old chestnut.

I don't know where such a public helper should live, since all the Platform stuff is internal, and okhttp-tls doesn't feel like a place for platform specific code.

Or i suppose one could just build a client and use the trust it found:

OkHttpClient.Builder builderWithDefaults() throws NoSuchAlgorithmException {
    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    return builder.sslSocketFactory(
        SSLContext.getDefault().getSocketFactory(),
        Objects.requireNonNull(builder.build().x509TrustManager())
    );
}

DSmithVA avatar Dec 06 '25 23:12 DSmithVA

~Maybe we could hide the sslSocketFactory(sslSocketFactory) and add a default for trust manager?~

This won't work as the SslContext should be initialised with the trust manager.

yschimke avatar Dec 07 '25 08:12 yschimke

We can definitely add helpers in okhttp-tls to make this a two liner

yschimke avatar Dec 07 '25 08:12 yschimke

I don't think it's appropriate for OkHttp to ever use the default SSLContext instance, because OkHttp mutates that thing and we don't want to interfere with other users of the same object. (And we don't want other users to interfere with us.)

We should change our docs to say this:

If unset, OkHttp will use an implementation from the host platform.

swankjesse avatar Dec 07 '25 12:12 swankjesse

As for a more concise way to fetch the system default trust manager, we've got an API in okhttp-tls that gets CAs but not held certificates.

HandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()
    .addPlatformTrustedCertificates()
    .build();
OkHttpClient client = new OkHttpClient.Builder()
    .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())
    .build();

Do you want it to collect held certificates also?

swankjesse avatar Dec 07 '25 12:12 swankjesse

I think we only mutate things when we create a SslSocketFactory ourselves.

https://github.com/square/okhttp/blob/5aaf6446ce041f4728b6066730517132e2622904/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/platform/Platform.kt#L190

  open fun newSslSocketFactory(trustManager: X509TrustManager): SSLSocketFactory {
    try {
      return newSSLContext()
        .apply {
          init(null, arrayOf<TrustManager>(trustManager), null)
        }.socketFactory
    } catch (e: GeneralSecurityException) {
      throw AssertionError("No System TLS: $e", e) // The system has no TLS. Just give up.
    }
  }

I wonder whether we can make a nice api for this in okhttp-tls without re-loading all the certificates, which I believe has a memory cost.

yschimke avatar Dec 07 '25 13:12 yschimke

As for a more concise way to fetch the system default trust manager, we've got an API in okhttp-tls that gets CAs but not held certificates.

Ah I didn't see that. That works for me as it would preserve our code scans that detect and flag any code referencing standard javax TrustManager or KeyManager/Factory. We've burned thousands of hours troubleshooting SSL trust/identity over the years (hundreds of apps all rolling SSL trust/identity in different ways, often multiple keystores/truststores/hand-parsed certs per app). Mandating default java trust and identity has been a tremendous improvement.

I think we only mutate things when we create a SslSocketFactory ourselves.

Mutation would be fine if documented. Though I would naively expect an SSLSocketFactory passed in to be accepted as-is.

We should change our docs to say this:

If unset, OkHttp will use an implementation from the host platform.

I'd go more explicit like "If unset, OkHttp will create a new SSLContext that honors system trust, but not necessarily other system settings like identity, cipher suites or proxy settings." or whatever other wiggle room may be desired.

DSmithVA avatar Dec 07 '25 16:12 DSmithVA

Yeah we should explicitly document what we take from the system and what we decide ourselves!

swankjesse avatar Dec 07 '25 19:12 swankjesse