cfug/dio

Dio (HttpClient) cannot set a self-signed trust certificate

ac169 opened this issue · 4 comments

Package

dio

Version

4.0.6

Operating-System

Android, iOS, Windows

Adapter

Default Dio

Output of flutter doctor -v

[√] Flutter (Channel stable, 3.3.10, on Microsoft Windows [版本 10.0.17763.5830], locale zh-CN)
    • Flutter version 3.3.10 on channel stable at X:\FlutterSDK
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 135454af32 (1 year, 5 months ago), 2022-12-15 07:36:55 -0800
    • Engine revision 3316dd8728
    • Dart version 2.18.6
    • DevTools version 2.15.0
    • Pub download mirror https://pub.flutter-io.cn
    • Flutter download mirror https://storage.flutter-io.cn

[√] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at X:\Android\SDK
    • Platform android-33, build-tools 34.0.0
    • Java binary at: X:\Java\jdk-11.0.16.1\bin\java
    • Java version Java(TM) SE Runtime Environment 18.9 (build 11.0.16.1+1-LTS-1)
    • All Android licenses accepted.

[X] Chrome - develop for the web (Cannot find Chrome executable at .\Google\Chrome\Application\chrome.exe)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.

[√] Visual Studio - develop for Windows (Visual Studio Professional 2022 17.6.5)
    • Visual Studio at X:\Microsoft Visual Studio\2022\Professional
    • Visual Studio Professional 2022 version 17.6.33829.357
    • Windows 10 SDK version 10.0.20348.0

[!] Android Studio (version 2023.1)
    • Android Studio at X:\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    X Unable to find bundled Java version.
    • Try updating or re-installing Android Studio.

[√] IntelliJ IDEA Ultimate Edition (version 2023.1)
    • IntelliJ at X:\JetBrains\ideaIU-2023.1.6
    • Flutter plugin version 78.2.1
    • Dart plugin version 231.9414.10

[√] Connected device (3 available)
    • AOSP on IA Emulator (mobile) • emulator-5554 • android-x86    • Android 9 (API 28) (emulator)
    • Windows (desktop)            • windows       • windows-x64    • Microsoft Windows [版本 10.0.17763.5830]
    • Edge (web)                   • edge          • web-javascript • Microsoft Edge 109.0.1518.78

[√] HTTP Host Availability
    • All required HTTP hosts are available

! Doctor found issues in 2 categories.

Dart Version

2.18.6

Steps to Reproduce

Code snippet:

  (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
      (client) {
    SecurityContext securityContext = SecurityContext(withTrustedRoots: true);
    // securityContext.setTrustedCertificatesBytes(certBytes);
    securityContext.setTrustedCertificates('assets/ca-cert.crt');
    HttpClient httpClient = HttpClient(context: securityContext);
    httpClient.badCertificateCallback =
        (X509Certificate cert, String host, int port) {
      return true;
    };
  };

Expected Result

In addition to the system's own trust root certificate, I want to add my own trust certificate! Certificate verification is not ignored blindly.

Actual Result

Failed to load "...\dio_ca_trust_test.dart":
DioError [DioErrorType.other]: TlsException: Failure trusting builtin roots (OS Error: 
    CERT_ALREADY_IN_HASH_TABLE(../../third_party/boringssl/src/crypto/x509/x509_lu.c:357), errno = 184549481)
  1. We no longer provide support to .dio: <5.0.0
  2. Do some search: https://www.mongodb.com/docs/atlas/device-sdks/sdk/flutter/troubleshooting/#connect-to-app-services-using-android-7-or-older

dio: ^5.0.0 was also tested and the problem remains! I have doubts that the signature algorithm for the certificate format does not support IP certificates, and the support is not perfect! But there is no documentation

According to the example, the following error is displayed:

DioException [unknown]: null
Error: HandshakeException: Handshake error in client (OS Error: 
	CERTIFICATE_VERIFY_FAILED: Hostname mismatch(../../third_party/boringssl/src/ssl/handshake.cc:393))

Previously, the IP certificate was deployed on the HTTP server, now replace the certificate with the domain name certificate! Create a new test code in the test directory.

dio.httpClientAdapter = IOHttpClientAdapter(
      createHttpClient: () {
        SecurityContext securityContext =
            SecurityContext(withTrustedRoots: true);
        **securityContext.setTrustedCertificates('assets/ca-cert.crt'); or
        securityContext.useCertificateChainBytes(File('assets/ca-cert.crt').readAsBytesSync());**
        final client = HttpClient(
          context: securityContext,
        );
        client.badCertificateCallback = (cert, host, port) => true;

        return client;
      },
    );

This code will work in a separate test file with setTrustedCertificates and useCertificateChainBytes Settings. But placing the code in the project indicates that the certificate file is not found

I/flutter ( 6689): Error: FileSystemException: Cannot open file, path = 'assets/ca-cert.crt' (OS Error: No such file or directory, errno = 2)
  1. Dio (HttpClient) certificate authentication The support for IP certificate authentication is not perfect.

  2. Using File('assets/ca.crt').readAsBytesSync() or await rootBundle.load("assets/ca.crt") in the Widget neither of these methods work properly FileSystemException: Cannot open file. But it works fine until the main function calls runApp(const App()), I don't know why!

  3. In the main () function configuration HttpOverrides. The method of HttpOverrides.global = _HttpOverrides _HttpOverrides. createHttpClient will be multiple calls even if I just call the HTTP request at a time, Setting the certificate is also invalid, and this method is only suitable for ignoring the certificate globally.

The solution:
Adds a global variable, initialized before runApp(const App()).Use this global variable where http requests are required

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  var chainBytes = await rootBundle.load("assets/ca.crt");

  Global.dio.httpClientAdapter = IOHttpClientAdapter(
    createHttpClient: () {
      SecurityContext securityContext = SecurityContext(withTrustedRoots: true);
      securityContext.setTrustedCertificatesBytes(chainBytes.buffer.asUint8List());
      // securityContext.useCertificateChainBytes(chainBytes.buffer.asUint8List());
      final client = HttpClient(
        context: securityContext,
      );
      // client.badCertificateCallback = (cert, host, port) => true;

      return client;
    },
  );

  runApp(const BiliApp());
}

This may not be the best way but it's the one I can think of right now.