1

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
Wietbot
  • 77
  • 6
sumaca9
  • 11
  • 1
  • CWAC-NetSecurity isn't really for use with arbitrary sockets. I can't rule out it working, but that's outside my supported scenarios. CWAC-NetSecurity should be just a pass-through on 7.0, so try your code without the `CompositeTrustManager` bits, and see if it works. If it does, then I have some problem in CWAC-NetSecurity related to use with plain sockets. If it does not work, then the problem lies somewhere in Android proper, or in the device firmware of your test device (assuming you're testing with hardware). – CommonsWare Apr 11 '19 at 21:59
  • @CommonsWare - I did try it without initially. With Android 8, I was able to use the defualt SSLSocketFactory and create a SSLSocket directly from that. The rest was added for support of older android versions and it works with 8 as well. Neither way seemed to work on 7 though. I get nothing in the CertChainListener, which tells me that I have issues prior to cert chain validation. I didn't really think it had anything to do with CWAC-NetSecurity, but was hoping someone on your team might have more knowledge about how to track this down. – sumaca9 Apr 12 '19 at 21:07
  • My... team? Do dust bunnies count? :-) I'm afraid that I don't have a team, and I'm also afraid that I have no idea what might be going on here. Sorry! – CommonsWare Apr 12 '19 at 21:20
  • Dust bunnies absolutely count! :-) I wouldn't be where I am today without the army of them that live behind my monitors. – sumaca9 Apr 15 '19 at 15:47

0 Answers0