63
votes

I am sending a post request in Dart. It is giving a response when I test it on API testing tools such as Postman. But when I run the app. It gives me the following error:-

E/flutter ( 6264): HandshakeException: Handshake error in client (OS Error: E/flutter ( 6264):  CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(handshake.cc:363))

Here is my code of the function -

Future getAccessToken(String url) async {

    try {
      http.post('url',
          body: {
            "email": "[email protected]",
            "password": "1234"
          }).then((response) {
        print("Reponse status : ${response.statusCode}");
        print("Response body : ${response.body}");
        var myresponse = jsonDecode(response.body);
        String token = myresponse["token"];
      });
    } catch (e) {
      print(e.toString());
    }

Here's the full error body:

E/flutter ( 6264): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception: E/flutter ( 6264): HandshakeException: Handshake error in client (OS Error: E/flutter ( 6264):   CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(handshake.cc:363)) E/flutter ( 6264): #0      IOClient.send (package:http/src/io_client.dart:33:23) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #1      BaseClient._sendUnstreamed (package:http/src/base_client.dart:169:38) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #2     BaseClient.post (package:http/src/base_client.dart:54:7) E/flutter ( 6264): #3      post.<anonymous closure> (package:http/http.dart:70:16) E/flutter ( 6264): #4      _withClient (package:http/http.dart:166:20) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #5     post (package:http/http.dart:69:5) E/flutter ( 6264): #6     
_MyLoginFormState.getAccessToken (package:chart/main.dart:74:7) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #7    
_MyLoginFormState.build.<anonymous closure> (package:chart/main.dart:64:29)
17
Do you user a self signed certificate ? - Muldec
I just want to accept all certificates - Adil Maqusood
well, if it a self signed one, it won't work. Dart does not allow self signed certificates. One solution (a bad one imho) is to allow certificates, even invalid ones, but it removes the core principle of using certificates. - Muldec
Check this answer It worked perfectly for me - Vlad Hondru
define a class import 'dart:io'; import 'package:http/http.dart' as http; import 'package:http/io_client.dart'; class TrustAllCertificates { static http.Client sslClient() { var ioClient = new HttpClient() ..badCertificateCallback = (X509Certificate cert, String host, int port) { return (host.compareTo("domain-name.com") == 0); }; http.Client _client = IOClient(ioClient); return _client; } } - Frank Gue

17 Answers

82
votes

Just for the sake of clarity specially for the new comers to flutter/dart, here is what you need to do in order to enable this option globally in your project:

  1. In your main.dart file, add or import the following class:
 class MyHttpOverrides extends HttpOverrides{
  @override
  HttpClient createHttpClient(SecurityContext context){
    return super.createHttpClient(context)
      ..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
  }
}
  1. In your main function, add the following line after function definition:

HttpOverrides.global = new MyHttpOverrides();

This comment was very helpful to pass through this matter, and please note that...

This should be used while in development mode, do NOT do this when you want to release to production, the aim of this answer is to make the development a bit easier for you, for production, you need to fix your certificate issue and use it properly, look at the other answers for this as it might be helpful for your case.

33
votes

Edit & Update Feb 2021: When this question was earlier asked there were not enough docs and developers to answer. The following answers may be more helpful than this one: Ma'moon Al-Akash Answer, Pedro Massango's Answer & Ken's Answer

If you have not found the solution in these 3 answers, you can try the solution below.

Originally Answered Jan 2019: The correct(but a bad) way to do it ,as I found out, is to allow all certificates.

HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);

String url ='[email protected]';

Map map = { 
     "email" : "email" , 
     "password" : "password"
};

HttpClientRequest request = await client.postUrl(Uri.parse(url));

request.headers.set('content-type', 'application/json');

request.add(utf8.encode(json.encode(map)));

HttpClientResponse response = await request.close();

String reply = await response.transform(utf8.decoder).join();

print(reply);
20
votes

If you are using Dio library, just do this:

Dio dio = new Dio();
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
    (HttpClient client) {
  client.badCertificateCallback =
      (X509Certificate cert, String host, int port) => true;
  return client;
};
11
votes

This Code work for me

class MyHttpOverrides extends HttpOverrides{
  @override
  HttpClient createHttpClient(SecurityContext context){
    return super.createHttpClient(context)
      ..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
  }
}

void main(){
  HttpOverrides.global = new MyHttpOverrides();
  runApp(MyApp());
}

I think it will the same for you...

9
votes

The best approach (I think) is to allow certificate for trusted hosts, so if your API host is "api.my_app" you can allow certificates from this host only:

HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) {
 final isValidHost = host == "api.my_app";
 return isValidHost;
});

If you have more hosts you can just add a new check there.

9
votes
import 'package:http/io_client.dart';
import 'dart:io';
import 'package:http/http.dart';
import 'dart:async';
import 'dart:convert';

    Future getAccessToken(String url) async {
      try {
        final ioc = new HttpClient();
        ioc.badCertificateCallback =
            (X509Certificate cert, String host, int port) => true;
        final http = new IOClient(ioc);
        http.post('url', body: {"email": "[email protected]", "password": "1234"}).then(
            (response) {
          print("Reponse status : ${response.statusCode}");
          print("Response body : ${response.body}");
          var myresponse = jsonDecode(response.body);
          String token = myresponse["token"];
        });
      } catch (e) {
        print(e.toString());
      }
    }
3
votes

For everyone landing here with a need to solve the problem and not just bypass it allowing everything.

For me the problem solved on the server side (as it should be) with no change in the code. Everything is valid now. On all the other solutions the problem still exists (eg The Postman runs but it displays a configuration error on the globe next to response status) The configuration is Centos/Apache/LetsEncrypt/Python3.8/Django3.1.5/Mod_wsgi/ but I guess that the solution is valid for most installations of Apache/LetsEncrypt

The steps to resolve are

  1. Locate the line "SSLCACertificateFile" on the Virtual Host you wish to config. For example:

SSLCACertificateFile /etc/httpd/conf/ssl.crt/my_ca.crt

  1. Download https://letsencrypt.org/certs/lets-encrypt-r3-cross-signed.txt At the end of /etc/httpd/conf/ssl.crt/my_ca.crt (after the -----END CERTIFICATE-----) start a new line and paste from lets-encrypt-r3-cross-signed.txt everything bellow -----BEGIN CERTIFICATE----- (including -----BEGIN CERTIFICATE-----)
  2. Save /etc/httpd/conf/ssl.crt/my_ca.crt
  3. Restart Apache httpd

References: https://access.redhat.com/solutions/43575 https://letsencrypt.org/certs

Also you can check the validity of your cert in https://www.digicert.com/help/.

2
votes

I specifically needed to use lib/client.dart Client interface for http calls (i.e. http.Client instead of HttpClient) . This was required by ChopperClient (link).

So I could not pass HttpClient from lib/_http/http.dart directly to Chopper. ChopperClient can receive HttpClient in the constructor wrapped in ioclient.IOClient.

HttpClient webHttpClient = new HttpClient();
webHttpClient.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
dynamic ioClient = new ioclient.IOClient(webHttpClient);
final chopper = ChopperClient(
  baseUrl: "https://example.com",
  client: ioClient,
  services: [
    MfService.create()
  ],
  converter: JsonConverter(),
);
final mfService = MfService.create(chopper);

This way you can temporarily ignore CERTIFICATE_VERIFY_FAILED error in your calls. Remember - that's only for development purposes. Don't use this in production environment!

2
votes

Update on January 30, 2021: I know the reason, because nginx is configured with some encryption algorithms that flutter does not support! , The specific need to try.

Use tls 1.3 request URL, no problem.

Example

import 'dart:io';

main() async {
  HttpClient client = new HttpClient();
  // tls 1.2 error
//  var request = await client.getUrl(Uri.parse('https://shop.io.mi-img.com/app/shop/img?id=shop_88f929c5731967cbc8339cfae1f5f0ec.jpeg')); 
  // tls 1.3 normal
  var request = await client.getUrl(Uri.parse('https://ae01.alicdn.com/kf/Ud7cd28ffdf6e475c8dc382380d5d1976o.jpg'));
  var response = await request.close();
  print(response.headers);
  client.close(force: true);
}
2
votes

This issue happened to us as we are not using the fullchain.pem generated using let's encrypt on nginx. Once changed that it fixes this issue.

server {
    listen 443 ssl;

    ssl_certificate /var/www/letsencrypt/fullchain.pem;

For Apache, you might need to configure SSLCertificateChainFile. More discussion about the issue https://github.com/flutter/flutter/issues/50699

2
votes

For those who need to ignore certificate errors only for certain calls, you could use the HttpOverrides solution already mentioned by numerous answers.

However, there is no need to use it globally. You can use it only for certain calls that you know experience handshake errors by wrapping the call in HttpOverrides.runWithHttpOverrides().

class IgnoreCertificateErrorOverrides extends HttpOverrides{
  @override
  HttpClient createHttpClient(SecurityContext context){
    return super.createHttpClient(context)
      ..badCertificateCallback = ((X509Certificate cert, String host, int port) {
      return true;
    });
  }
}


Future<void> myNonSecurityCriticalApiCall() async {
  await HttpOverrides.runWithHttpOverrides(() async {
    String url = 'https://api.example.com/non/security/critical/service';
    Response response = await get(url);

    // ... do something with the response ...
  }, IgnoreCertificateErrorOverrides());
}

In my case it is an external API which does have a valid SSL certificate and works in the browser but for some reason won't work in my Flutter app.

2
votes

Check the device date and time in device settings. The device date and time is set to previous date.

1
votes

Using Dio package for request on my local server with self signed certificat, i prefer to allow a specific host rather than all domains.

//import 'package:get/get.dart' hide Response;  //<-- if you use get package
import 'package:dio/dio.dart';

void main(){
  HttpOverrides.global = new MyHttpOverrides();
  runApp(MyApp());
}

class MyHttpOverrides extends HttpOverrides{
  @override
  HttpClient createHttpClient(SecurityContext context){
    return super.createHttpClient(context)
      ..badCertificateCallback = ((X509Certificate cert, String host, int port) {
        final isValidHost = ["192.168.1.67"].contains(host); // <-- allow only hosts in array
        return isValidHost;
      });
  }
}

// more example: https://github.com/flutterchina/dio/tree/master/example
void getHttp() async {
  Dio dio = new Dio();
  Response response;
  response = await dio.get("https://192.168.1.67");
  print(response.data);
}
1
votes

For me, it was because I am using HTTPS and the API uses HTTP so I just changed it to HTTP and it works.

1
votes

Actually in my case it fixed me after i update the date and time on my pc. Might help someone i guess

0
votes

Well, I figured out that the actual root of the problem was out-of-sync clocks on the test device...

-1
votes

Normally this problem occur when you unable to use external packages and it happens when there are strict firewall settings in your PC. Most of the time when you are using PC from company where they install special programs to block these services. So, disable them and hopefully it will work.