Why does okhttp not use TLSv1 when activated in the Socket Factory?

1

On some old Android versions (< API 21), there is only SSLv3 and TLSv1 available:

enter image description here

The server accepts minimum TLSv1, handshakes with SSL are rejected. So for Glide to load images properly, I use a custom okhttp client with a Socket Factory that uses TLSv1.

@GlideModule
public class TlsGlideModule extends AppGlideModule {

    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
        super.registerComponents(context, glide, registry);

        OkHttpClient.Builder client = new OkHttpClient.Builder();
        enableTls1(client);
        
        registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client.build()));
    }
}

private static void enableTls1(OkHttpClient.Builder client) {
    SSLContext sc = SSLContext.getInstance("TLSv1");
    sc.init(null, null, null);
    client.sslSocketFactory(new TlsSocketFactory(sc.getSocketFactory()));

    ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
            .tlsVersions(TlsVersion.TLS_1_0)
            .build();

    List<ConnectionSpec> specs = new ArrayList<>();
    specs.add(cs);
    client.connectionSpecs(specs);
}

public class TlsSocketFactory extends SSLSocketFactory {
    private static final String[] allowedTlsVersions = {"TLSv1"};

    private final SSLSocketFactory delegate;

    public TlsSocketFactory(SSLSocketFactory base) {
        this.delegate = base;
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return delegate.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return patch(delegate.createSocket(s, host, port, autoClose));
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException {
        return patch(delegate.createSocket(host, port));
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
        return patch(delegate.createSocket(host, port, localHost, localPort));
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return patch(delegate.createSocket(host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return patch(delegate.createSocket(address, port, localAddress, localPort));
    }

    private Socket patch(Socket s) {
        if (s instanceof SSLSocket) {
            ((SSLSocket) s).setEnabledProtocols(allowedTlsVersions);
        }
        return s;
    }
}

When I set breakpoints in these methods, I see that they are getting called when Glide initializes, but the images are still not loading, the error is still:

Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb987bb88: Failure in SSL library, usually a protocol error
    error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE (third_party/openssl/boringssl/src/ssl/tls_record.cc:592 0xb980d4d8:0x00000001)
    error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO (third_party/openssl/boringssl/src/ssl/handshake.cc:589 0x8696ec03:0x00000000)
        at com.google.android.gms.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
        ...

If I remove all of the code above and just call this on app launch, the images are loading fine:

ProviderInstaller.installIfNeeded(App.getInstance().getApplicationContext());

But according to the SSLContext, the device already has TLSv1 on the device available, so I prefer to not have a Google Play Services dependency.

Why is that?

android
okhttp
android-glide
sslsocketfactory
sslcontext
asked on Stack Overflow Jun 29, 2020 by Manuel • edited Jun 30, 2020 by Manuel

1 Answer

0

You need to tell OkHttp to use it's less secure ConnectionSpec, COMPATIBLE_TLS.

OkHttpClient client = new OkHttpClient.Builder()
    .connectionSpecs(Arrays.asList(ConnectionSpec.COMPATIBLE_TLS))
    .build();

https://square.github.io/okhttp/https/

answered on Stack Overflow Jun 29, 2020 by Jesse Wilson

User contributions licensed under CC BY-SA 3.0