flutterdartssl-certificatehttp2dio

Flutter Dio HTTPS Certificate Validation Ignoring onBadCertificate - CA Cert Problem?


I've run into a bit of a quandary. I'm writing a client/server application. The frontend is in Flutter and uses the Dio http package, the backend is Java. The backend REST API is secured via TLS certificate.

As many other questions have pointed out, Flutter doesn't seem to have access to the system CA Certificate store on all platforms. This is problematic because I intend to allow for self hosting of the server application, meaning certificates from all different CAs could be utilized server-side, so my HTTP client will need to support all of the CAs that a typical web browser supports.

Dio apparently allows you to set a trusted cert chain, but I'm wondering how best to leverage that.

Has anyone encountered this problem before. What solution did you implement to fix this?

These are the solutions I've thought of so far:

  1. Allow user to "upload" ca cert bundle and store bytes in shared_preferences (difficult for users)
  2. Find another way to validate the certificate e.g. with user entered thumbprint? (less difficult, let all certs fail original validation, then do custom validatation with onBadCertificate against stored thumbprint)
  3. Find a package which offers access to system certificate store
  4. Ship inside the application a majority of big name CA certs and trust them with Dio somehow

The other issue I came here about is that Dio appears to be ignoring my onBadCertificate method. I declared this inside a ConnectionManager, should I not do that?

Here is the code that is being ignored:

var dio = Dio()
  ..options.baseUrl = server
  ..interceptors.add(LogInterceptor())
  ..interceptors.add(CookieManager(cookieJar))
  ..httpClientAdapter = Http2Adapter(
    ConnectionManager(
      idleTimeout: 10000,
      // Ignore bad certificate
      onClientCreate: (_, config) => {
        //config.context?.setTrustedCertificatesBytes(File("/assets/certs/wildcard.pem").readAsBytesSync()),
        config.onBadCertificate = (_) => true, // <-- ignored, should bypass check
      }
    ),
  );

EDIT:

As mentioned in the comments, Flutter was in fact capable of using the system CA Certificate store. As I test other platforms, I'll update if I run into any problems with certificates. But this one is solved!


Solution

  • The core issue ended up being that the server did not provide the full certificate chain but only the leaf certificate. Some browsers hides this kinds of issues if the browser have seen the intermediate certificate somewhere else. So just because things works in the browser, it does not means the server is actually providing enough information for other TLS clients to verify the certificate chain.

    The solution was therefore to configure the server to provide the full certificate chain.