Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SSL Certificate Pinning not working anymore on Android 9

I'm using the following certificate pinning code which has worked for a while (error handling edited out for brevity's sake):

private static SSLContext _ssl_context = null;

public static SSLSocketFactory get_ssl_socket_factory(Context context)
{
    if (_ssl_context != null) {
        return _ssl_context.getSocketFactory();
    }

    KeyStore keystore = get_keystore(context);
    try
    {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
        tmf.init(keystore);
        _ssl_context = SSLContext.getInstance("TLS");
        _ssl_context.init(null, tmf.getTrustManagers(), null);
        return _ssl_context.getSocketFactory();
    }
    catch (GeneralSecurityException e) {
        // ...
    }
}

This is more or less code provided by the official documentation. The SocketFactory is then used as follows:

if ("https".equals(target.getProtocol()) &&
    "example.com".equals(target.getHost()) &&
    huc instanceof HttpsURLConnection)
{
    ((HttpsURLConnection) huc).setSSLSocketFactory(
            SSLHelper.get_ssl_socket_factory(this));
}

When I run this code on an Android 8 device, things work as expected. On my Android 9 emulator however, an exception is thrown:

E/App: https://example.com/page.html could not be retrieved! (Hostname example.com not verified:
            certificate: sha1/VYMjxowFaRuZpycEoz+srAuXzlU=
            subjectAltNames: [])
        javax.net.ssl.SSLPeerUnverifiedException: Hostname example.com not verified:
            certificate: sha1/VYMjxowFaRuZpycEoz+srAuXzlU=
            subjectAltNames: []
            at com.android.okhttp.internal.io.RealConnection.connectTls(RealConnection.java:201)
            at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:149)
            at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:112)
            at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:184)
            at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:126)
            at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:95)
            at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:281)
            at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:224)
            at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:461)
            at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:127)
            at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:89)
            at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:26)
            at ...

It seems that something has changed in Android 9, but so far I haven't been able to find any information regarding this behavior. My ideas are the following:

  • Maybe this way of doing certificate pinning has been deprecated
  • Maybe Android 9 will no longer verify domains with SHA1 certificates

Anything other ideas?

like image 274
executifs Avatar asked Sep 20 '18 13:09

executifs


People also ask

Why is my SSL not working on mobile?

This is the most common reason behind SSL certificate errors. If there's a mismatch between the clock on your device and the clock of web server that you're trying to access then SSL certificate of website won't be verified. As a result, you'll get an SSL error.

Is SSL pinning applicable to Android?

ssl package and you can use it to implement Android Certificate Pinning. Keep reading for a step-by-step tutorial on how to implement pinning using this component. Load KeyStore with the Certificate file from resources (as InputStream).


1 Answers

I just had the same issue. According to the Android 9 Change-Log this is expected for certificates without SAN:

RFC 2818 describes two methods to match a domain name against a certificate—using the available names within the subjectAltName (SAN) extension, or in the absence of a SAN extension, falling back to the commonName (CN).

However, the fallback to the CN was deprecated in RFC 2818. For this reason, Android no longer falls back to using the CN. To verify a hostname, the server must present a certificate with a matching SAN. Certificates that don't contain a SAN matching the hostname are no longer trusted.

Source: Hostname verification using a certificate

like image 143
Marten Avatar answered Oct 18 '22 03:10

Marten