React-native: HTTPS requests on android to a server with a self signed certificate return SSLHandshakeException after the update to 0.56

Created on 1 Aug 2018  路  9Comments  路  Source: facebook/react-native

After updating to the latest version of RN (0.56.0) I started to get SSLHandshakeException on all of my request to my server. The issue is only in Android.

After overriding the OkHttp client and adding a log interceptor I got the following information in the logcat:

D/OkHttp: --> POST https://<MY-SERVER>/oauth2/token
D/OkHttp: Content-Type: application/x-www-form-urlencoded; charset=utf-8
          Content-Length: 90
          device-id: ED838B30-C355-4361-8C73-B0FD3E0A653A
          user-agent: <MY CUSTOM USER AGENT>
D/OkHttp: <THE REQUEST BODY>  --> END POST (90-byte body)
D/OkHttp: <-- HTTP FAILED: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

My server has a custom certificate, the root certificate is installed on the device, so the connection should be performed seamlessly.

Locked

Most helpful comment

I found the issue. I will leave here the fix to help others that eventually need it.

In Android Nougat Google introduced a set of security enhancements. You can read more about it here.

One of the changes it is related to trust user or admin-added CAs for secure connections. All apps that target API Level 24 and above no longer trust user or admin-added CAs for secure connections, by default. This change will break all apps that update to the newest version of RN and that connect to a server with a self signed certificate. Since RN updated the Android SDK target from 23 to 26.

To fix this you just need to create a Network Security Config.

In the manifest of your app add the network configuration:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config"
                    ... >
        ...
    </application>
</manifest>

And add to your app the network configuration:

Under the res folder create the network configuration file xml/network_security_config.xml.

The content of the network configuration file should be:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>

After this change, clean the app and run it again. Everything should work as expected now.

I also find another configuration that is working better for me:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <!-- For React Native Hot-reloading system -->
        <!-- If you are running on a device insert your computer IP -->
        <domain includeSubdomains="true">localhost</domain>
        <domain includeSubdomains="true">your self signed domain</domain>

        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </domain-config>

    <base-config cleartextTrafficPermitted="false" />
</network-security-config>

All 9 comments

I found the issue. I will leave here the fix to help others that eventually need it.

In Android Nougat Google introduced a set of security enhancements. You can read more about it here.

One of the changes it is related to trust user or admin-added CAs for secure connections. All apps that target API Level 24 and above no longer trust user or admin-added CAs for secure connections, by default. This change will break all apps that update to the newest version of RN and that connect to a server with a self signed certificate. Since RN updated the Android SDK target from 23 to 26.

To fix this you just need to create a Network Security Config.

In the manifest of your app add the network configuration:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config"
                    ... >
        ...
    </application>
</manifest>

And add to your app the network configuration:

Under the res folder create the network configuration file xml/network_security_config.xml.

The content of the network configuration file should be:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>

After this change, clean the app and run it again. Everything should work as expected now.

I also find another configuration that is working better for me:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <!-- For React Native Hot-reloading system -->
        <!-- If you are running on a device insert your computer IP -->
        <domain includeSubdomains="true">localhost</domain>
        <domain includeSubdomains="true">your self signed domain</domain>

        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </domain-config>

    <base-config cleartextTrafficPermitted="false" />
</network-security-config>

I'm having the same issue, and unfortunately the fix didn't work

Just want to chime in that the fix worked great for me. Make sure you actually have the right cert installed in Settings > Security & location > Encryption & credentials > Trusted credentials > User on the device.

@SandroMachado
not worked.
error
E:\react\kchat\android\app\src\mainres\xml\network_security_config.xml:1: AAPT: Error parsing XML: not well-formed (invalid token)

is there anything to add it? can you brief it

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>

Update: What worked for me was to override the default OkHttpClient and attach a custom connectionSpec.
In your MainActivity.java, your onCreate method should look like this:

import android.os.Build;
import com.<app-name>.CustomClientFactory;  // replace <app-name>
import com.facebook.react.modules.network.OkHttpClientProvider;
import okhttp3.OkHttpClient;

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

            OkHttpClientProvider.setOkHttpClientFactory(new CustomClientFactory());
        }
    }

Then you create a CustomClientFactory.java file:

package com.<app-name>; // replace app-name

import com.facebook.react.modules.network.OkHttpClientFactory;
import com.facebook.react.modules.network.OkHttpClientProvider;
import com.facebook.react.modules.network.ReactCookieJarContainer;

import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.net.*;
import java.io.*;
import okhttp3.OkHttpClient;
import okhttp3.*;

public class CustomClientFactory implements OkHttpClientFactory {

        @Override
        public OkHttpClient createNewNetworkModuleClient() {
                ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                                .tlsVersions(TlsVersion.TLS_1_2)
                                .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                                                CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                                                CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
                                                CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
                                                CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)
                                .build();

                List<ConnectionSpec> specs = new ArrayList<>();
                specs.add(spec);
                specs.add(ConnectionSpec.COMPATIBLE_TLS);
                specs.add(ConnectionSpec.CLEARTEXT);
                OkHttpClient.Builder client = new OkHttpClient.Builder().connectionSpecs(specs)
                                .connectTimeout(0, TimeUnit.MILLISECONDS).readTimeout(0, TimeUnit.MILLISECONDS)
                                .writeTimeout(0, TimeUnit.MILLISECONDS).cookieJar(new ReactCookieJarContainer());
                return OkHttpClientProvider.enableTls12OnPreLollipop(client).build();
        }
}

I tried this code but it doesn't work for me.
I have this message:

09-03 15:56:13.013 4811-5139/com.mweb_dmc D/SettingsInterface:  from settings cache , name = sound_effects_enabled , value = 0
09-03 15:56:13.015 4811-5319/com.mweb_dmc D/libc-netbsd: [getaddrinfo]: hostname=xxx.xxx.xx.xxx; servname=(null); netid=0; mark=0
    [getaddrinfo]: ai_addrlen=0; ai_canonname=(null); ai_flags=4; ai_family=0
09-03 15:56:13.015 4811-5319/com.mweb_dmc I/System.out: [CDS][DNS] getAllByNameImpl netId = 0
09-03 15:56:13.016 4811-5319/com.mweb_dmc D/libc-netbsd: [getaddrinfo]: hostname=xxx.xxx.xx.xxx; servname=(null); netid=0; mark=0
    [getaddrinfo]: ai_addrlen=0; ai_canonname=(null); ai_flags=4; ai_family=0
09-03 15:56:13.016 4811-5319/com.mweb_dmc I/System.out: [CDS]rx timeout:0
09-03 15:56:13.017 4811-5319/com.mweb_dmc I/System.out: [socket][20] connection /xxx.xxx.xx.xxx:1443;LocalPort=43588(0)
    [CDS]connect[/xxx.xxx.xx.xxx:1443]
09-03 15:56:13.017 4811-5319/com.mweb_dmc D/Posix: [Posix_connect Debug]Process com.mweb_dmc :1443 
09-03 15:56:13.113 4811-5319/com.mweb_dmc I/System.out: [CDS]port[43588]
    [socket][/192.168.1.91:43588] connected
09-03 15:56:13.114 4811-5319/com.mweb_dmc D/libc-netbsd: [getaddrinfo]: hostname=xxx.xxx.xx.xxx; servname=(null); netid=0; mark=0
    [getaddrinfo]: ai_addrlen=0; ai_canonname=(null); ai_flags=4; ai_family=0
09-03 15:56:13.115 4811-5319/com.mweb_dmc D/NativeCrypto: ssl=0xaf53cd00 NativeCrypto_SSL_do_handshake fd=0x96f1cd20 shc=0x96f1cd24 timeout_millis=0 client_mode=1 npn=0x0
    doing handshake ++
    ssl=0xaf53cd00 info_callback where=0x10 ret=1
    ssl=0xaf53cd00 handshake start in CINIT  before connect initialization
    ssl=0xaf53cd00 info_callback calling handshakeCompleted
    ssl=0xaf53cd00 info_callback completed
    ssl=0xaf53cd00 info_callback where=0x1001 ret=1
    ssl=0xaf53cd00 SSL_connect:CINIT  before connect initialization
    ssl=0xaf53cd00 info_callback ignored
    ssl=0xaf53cd00 info_callback where=0x1001 ret=1
    ssl=0xaf53cd00 SSL_connect:3WCH_A SSLv3 write client hello A
    ssl=0xaf53cd00 info_callback ignored
    ssl=0xaf53cd00 info_callback where=0x1002 ret=-1
    ssl=0xaf53cd00 SSL_connect:error exit in 3RSH_A SSLv3 read server hello A
    ssl=0xaf53cd00 info_callback ignored
    doing handshake -- ret=-1
    ssl=0xaf53cd00 NativeCrypto_SSL_do_handshake ret=-1 errno=11 sslError=2 timeout_millis=0
09-03 15:56:13.194 4811-5319/com.mweb_dmc D/NativeCrypto: doing handshake ++
    ssl=0xaf53cd00 info_callback where=0x1001 ret=1
    ssl=0xaf53cd00 SSL_connect:3RSH_A SSLv3 read server hello A
    ssl=0xaf53cd00 info_callback ignored
09-03 15:56:13.194 4811-5319/com.mweb_dmc E/NativeCrypto: ssl=0xaf53cd00 cert_verify_callback x509_store_ctx=0x96f1cb4c arg=0x0
    ssl=0xaf53cd00 cert_verify_callback calling verifyCertificateChain authMethod=ECDHE_RSA
09-03 15:56:13.196 4811-5319/com.mweb_dmc D/NativeCrypto: ssl=0xaf53cd00 cert_verify_callback => 0
09-03 15:56:13.196 4811-5319/com.mweb_dmc D/OpenSSLLib: OpensslErr:Module:16(190:126); file:external/boringssl/src/ssl/s3_clnt.c ;Line:984;Function:ssl3_get_server_certificate
09-03 15:56:13.196 4811-5319/com.mweb_dmc D/NativeCrypto: ssl=0xaf53cd00 info_callback where=0x4008 ret=558
    ssl=0xaf53cd00 SSL3 alert write:F:CU fatal certificate unknown
    ssl=0xaf53cd00 info_callback ignored
    ssl=0xaf53cd00 info_callback where=0x1002 ret=-1
    ssl=0xaf53cd00 SSL_connect:error exit in 3RSC_B SSLv3 read server certificate B
    ssl=0xaf53cd00 info_callback ignored
    doing handshake -- ret=-1
    ssl=0xaf53cd00 NativeCrypto_SSL_do_handshake exception => 0
09-03 15:56:13.197 4811-5319/com.mweb_dmc I/System.out: close [socket][/192.168.1.91:43588]
    close [socket][/:::43588]
09-03 15:56:13.304 4811-5138/com.mweb_dmc I/ReactNativeJS: { [Error: Error Connection]
      line: 119541,
      column: 24,
      sourceURL: 'http://localhost:8081/index.delta?platform=android&dev=true&minify=false' }

@totofe Does this error show on different Android versions and also are you using a self signed certificate?

I am testing this app only on android 6.0 version. and yes I use a self signed certificate.
I posted my problem here: https://stackoverflow.com/questions/52160396/implement-ssl-certificate-pinning-while-using-react-native

edit:

for information, I had the same problem with IOS. I solved the problem by adding in the file RCTHTTPRequestHandler.m this code (like this example https://stackoverflow.com/questions/36289125/react-native-fetch-from-https-server-with-self-signed-certificate):

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
  completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}

@samie820 The fix you posted worked well only it was missing to import android.os.Bundle into MainActivity.java and java.util.List + java.util.util.ArrayList inCustomClientFactory.java for me 馃檪

Was this page helpful?
0 / 5 - 0 ratings