5

I have read Android 8: Cleartext HTTP traffic not permitted, but none of the answers appear to allow me to do what I want.

I have an Android application where another client can identify itself with a certificate. The application wants to verify that certificate. Part of verifying the certificate is fetching the certificate revocation list (CRL) from the certificate issuer. The distribution point(s) for the CRL is(are) listed in the certificate, and is inevitably an HTTP URL (the CRL itself is signed by the issuer so there is no security issue, and if it was an HTTPS URL, one would want to verify the certificate protecting the CRL distribution point, and check if it had been revoked ...)

Possible solutions, and why they don't work for me:

  • Don't worry about it - let the TLS library worry about validating the certificate. Unfortunately, there is no direct TLS connection between the two clients; it is all mediated through a server (which is connected to by TLS).
  • Create network_security_config.xml which lists the domains to which HTTP is allowed. Sadly, I don't know the URLs when I build the application - it depends on what the CA decides to put in their certificates.
  • Put android:usesCleartextTraffic="true" in the manifest. This means that any traffic can be HTTP, and I would rather avoid that if possible. (As an example, communication with the server absolutely must be HTTPS, and I would like an error if I do HTTP by accident.)

Is there any way for the code to say "this connection is allowed to be HTTP" (but default to HTTPS only)?

  • 1
    Note: I have added comments to the current answers indicating why they don't work for me, but I don't want to edit those restrictions into the question because a) they may be good answers for other people; b) editing the question to invalidate answers is rude. – Martin Bonner supports Monica Nov 28 '18 at 13:02
  • can you access any of those certificates before hand? if yes, then you might be able to set them as trust anchors. If you want your url to support https only then add it as sub-domain in `domain-config` and set `cleartextTrafficPermitted="false"` – karan Nov 29 '18 at 07:08

3 Answers3

1

If you're using OkHttp, you can construct a client as such:

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

This will only allow connections through HTTPS. So then, you can use your third option (android:usesCleartextTraffic="true") and when you make a cleartext connection through this client, it will fail.

Finally, you can create a standard OkHttp client:

OkHttpClient client = new OkHttpClient.Builder().build()

when you want to use the cleartext connection.

EDIT: Using HttpUrlConnection, you can simply check if the returned connection is a HttpsUrlConnection, like:

try {
    URL my_url = new URL(path);
    HttpUrlConnection urlConnection = (HttpURLConnection) my_url.openConnection();
    if(!(urlConnection instanceof HttpsURLConnection)) {
        // cleartext connection, throw error
        throw new NotHttpsException();
    }
    // the connection is secure, do normal stuff here
    urlConnection.setRequestMethod("POST");
    urlConnection.setConnectTimeout(1500);
    urlConnection.setReadTimeout(1500);
    result = IOUtil.readFully(urlConnection.getInputStream());
} catch(Exception e) {
    e.printStackTrace()
} finally {
    if(urlConnection != null) urlConnection.disconnect();
}
qualverse
  • 866
  • 4
  • 12
1

The documentation on NetworkSecurityPolicy.isCleartextTrafficPermitted() says

This flag is honored on a best effort basis because it's impossible to prevent all cleartext traffic from Android applications given the level of access provided to them. For example, there's no expectation that the Socket API will honor this flag because it cannot determine whether its traffic is in cleartext.

So maybe this is an option for you: fetch the CRL using a Socket. There is a post by Daniel Nugent describing how to set up a simple TCP client

Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
0

Tha app has a TLS connection with the server.
You can ask the server to hand off those CRL urls to you (It is handing off the certificate in the first place right?).
It could even do it before hand and provide the Certificate together with the CRLs. In this way you get the CRLs without loosing the https lock nor having to make exceptions.

Juan
  • 5,525
  • 2
  • 15
  • 26
  • Hmm. I'd rather avoid trusting the server if I can. This is a secure comms application. – Martin Bonner supports Monica Nov 28 '18 at 12:59
  • 1
    If you have no issues in getting the CRL over HTTP because it is signed by the CA (I think I understood that from the question) why would trusting the server be less secure? – Juan Nov 29 '18 at 12:43
  • "The server" in this context is a server run specifically to provide services for this application. It is entirely distinct from the CA. – Martin Bonner supports Monica Nov 29 '18 at 13:33
  • I wasn't impying that. I actually understood what you are clarifying. In those terms, the server can fetch the CRLs and hand them over to the app. What I refferd to is that given that the connection you are trying to make using the app is over http (insecure), there is no additional risk if you have your server get the lists (from the issuer of the cerificate), and hand them over to you app. For your app it would be one service more it calls on your server. From the connecton point of view your server would be one more machine in the middle between the app and the CA Issuer providing the CRL. – Juan Nov 29 '18 at 14:35