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:
Anything other ideas?
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.
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).
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With