Can't establish SSLSession with self signed cert on Nougat only

401 Views Asked by At

Attempting to establish an SSLSession using a self-signed certificate and the new-ish Network Security Configuration method as well as cwac-netsecurity library for backward compatibility works on all other versions of android (that I have tried) except Android 7.0.

I have an android app that connects to a device to provide a UI for that device. The connection uses a TLSv1 SSLSocket and a self-signed server certificate on the device side. Prior to Nougat, I had the server certs embedded as a BKS key store and loaded at run-time to create a custom TrustManager in order to intialize the SSL context and create the socket. This no longer works under Android 7+. Following some other questions on SO (Cannot connect via SSL using self signed certificate on Android 7 and above), I was able to use the Network Security Configuration method to establish the SSLSocket connection on an Android 8 device (https://developer.android.com/training/articles/security-config#ConfigCustom). For backward compatibility, I am using the cwac-netsecurity library from CommonsWare (https://github.com/commonsguy/cwac-netsecurity). I built a small test application and am testing against openssl s_server. What I have written establishes an SSLSession successfully from android devices running 4.4, 6.0, and 8.0. For some reason however, it does not do so on an android device running 7.0. Here are code snippets that show the test. Any help would be appreciated.

Test code in android app

{
    // createSocketFactory
    SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault(); //null;
    TrustManagerBuilder tmb = new TrustManagerBuilder();
    tmb.withManifestConfig(MainActivity.this);
    tmb.withCertChainListener(
            new CertChainListener()
            {
                @Override
                public void onChain(X509Certificate[] chain, String domain)
                {
                    if (domain == null)
                    {
                        Log.d(TAG, "onChain: Certificate chain for request to unknown domain");
                    }
                    else
                    {
                        Log.d(TAG, "onChain: Certificate chain for request to: " + domain);
                    }

                    for (X509Certificate cert : chain)
                    {
                        Log.d(TAG, "onChain: Subject: " + cert.getSubjectX500Principal().getName());
                        Log.d(TAG, "onChain: Issuer: " + cert.getIssuerX500Principal().getName());
                    }
                }
            });
    CompositeTrustManager ctm = tmb.build();
    try
    {
        SSLContext sc = SSLContext.getInstance("TLSv1");
        sc.init(null, new TrustManager[] { ctm }, new SecureRandom());
        factory = sc.getSocketFactory();
    }
    catch (NoSuchAlgorithmException e)
    {
        Log.e(TAG, "createSocketFactory: failed to get SSLContext", e);
    }
    catch (KeyManagementException e)
    {
        Log.e(TAG, "createSocketFactory: failed to init SSLContext from CompositeTrustManager");
    }

    // createSslSocket
    SSLSocket socket = null;
    String addr = "172.31.106.60";
    int port = 50001;

    if (factory != null)
    {
        Log.d(TAG, "createSocketFactory - SUCCESS");
        try
        {
            socket = (SSLSocket)factory.createSocket(addr, port);
            Log.d(TAG, "createAltSocket - SUCCESS");

            socket.setEnabledProtocols(new String[] { "TLSv1" });
            socket.setEnabledCipherSuites(new String[] { "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" });
            socket.setTcpNoDelay(true);
            socket.setKeepAlive(true);
        }
        catch (IOException e)
        {
            Log.e(TAG, "createSslSocket - Couldn't create SSLSocket", e);
            try
            {
                socket.close();
            }
            catch (IOException e1)
            {
                e1.printStackTrace();
            }
            finally
            {
                socket = null;
            }
        }
    }
    else
    {
        Log.d(TAG, "createSocketFactory - FAILED");
    }

    // testSslSocket
    if (socket != null && socket.isConnected())
    {
        SSLSession session = socket.getSession();
        if (session != null && session.isValid())
            Log.d(TAG, "testSslSocket: SESSION SUCCESS");
        else
            Log.d(TAG, "testSslSocket: SESSION FAILED");

        try
        {
            socket.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

network_securiity_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="@raw/test_cert_chain"/>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.ssltesting">

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:networkSecurityConfig="@xml/network_security_config">
        <meta-data
            android:name="android.security.net.config"
            android:resource="@xml/network_security_config" />

test server

openssl s_server -tls1 -WWW -accept 50001 -cert test.crt -key mcv.key -state

In all working cases, the SSLSession is valid at the end of the test routine, and I get logging from the CertChainListener. However, when running this on my Android 7.0 device, all is quiet. I get no logging from the CertChainListener, the SSLSession is not valid, and I get the following on the server side:

Using default temp DH parameters
ACCEPT
SSL_accept:before/accept initialization
SSL3 alert write:fatal:handshake failure
SSL_accept:error in error
140386525526784:error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher:s3_srvr.c:1417:
ACCEPT
0

There are 0 best solutions below