certificate_pinning_httpclient_plus

pub package license: MIT

An implementation of Dart's HttpClient with certificate pinning against SPKI (Subject Public Key Info) SHA-256 hashes.

Because it implements dart:io's HttpClient interface, it drops into anything that accepts one — package:http's IOClient, Dio's IOHttpClientAdapter, or direct dart:io usage.

Platform support

Platform Supported Native implementation Minimum version
Android HttpsURLConnection API 21
iOS NSURLSession 12.0
macOS NSURLSession 10.14
Linux OpenSSL
Windows WinHTTP + CryptoAPI 10

Linux build requirement: the plugin links against OpenSSL, so the development headers must be present on the build machine: sudo apt-get install libssl-dev (Debian/Ubuntu) or sudo dnf install openssl-devel (Fedora).

macOS entitlement: sandboxed macOS apps cannot make outbound connections unless com.apple.security.network.client is set. Add it to both macos/Runner/DebugProfile.entitlements and macos/Runner/Release.entitlementsflutter create does not add it for you.

<key>com.apple.security.network.client</key>
<true/>

Android: the plugin's manifest already declares INTERNET and ACCESS_NETWORK_STATE, which are merged into your app.

Installation

dependencies:
  certificate_pinning_httpclient_plus: ^1.0.0

Getting your pin

Pins are the base64-encoded SHA-256 digest of a certificate's SPKI. The client logs the SPKI hash of every certificate in the chain, so the simplest way to get yours is to make one request and read the log.

You can also compute it ahead of time with GnuTLS — look for the Public Key PIN line:

gnutls-cli --print-cert example.com

Or with OpenSSL:

openssl s_client -connect example.com:443 </dev/null 2>/dev/null \
  | openssl x509 -pubkey -noout \
  | openssl pkey -pubin -outform der \
  | openssl dgst -sha256 -binary \
  | openssl enc -base64

Usage

import 'package:certificate_pinning_httpclient_plus/certificate_pinning_httpclient_plus.dart';

const pins = ["S4kZuhQQ1DPcMOCYFQXD0gG+UW0zmyVx6roNWpRl65I="];

With dart:io

final client = CertificatePinningHttpClient(pins);
final request = await client.getUrl(Uri.parse("https://example.com"));
final response = await request.close();

With package:http

import 'package:http/io_client.dart';

final client = IOClient(CertificatePinningHttpClient(pins));
final response = await client.get(Uri.parse("https://example.com"));

With Dio

import 'package:dio/dio.dart';
import 'package:dio/io.dart';

final dio = Dio();
dio.httpClientAdapter = IOHttpClientAdapter(
  createHttpClient: () => CertificatePinningHttpClient(pins),
);

Disabling logs in release builds

import 'package:flutter/foundation.dart';
import 'package:logger/logger.dart';

Logger.level = kDebugMode ? Level.debug : Level.nothing;

How it works

  1. On the first request to a host, the certificate chain is fetched natively via a MethodChannel and cached per host.
  2. The SHA-256 digest of each certificate's SPKI is compared against your pins.
  3. Only the matching certificates are installed into a SecurityContext as trusted certificates, and the request is made through an HttpClient built on that context.

If no certificate matches, the security context trusts nothing and the connection fails. On a pinning failure the cached chain for that host is discarded, so a legitimate certificate rotation recovers on the next request while a MitM attempt keeps failing.

Pinning to a leaf certificate means requests break the moment that certificate rotates. Pin the intermediate, or supply several pins (current plus a backup), if you need rotation headroom.

Credits

This package is a maintained continuation of sebkoller/certificate_pinning_httpclient, which is no longer updated. It adds macOS, Linux and Windows support on top of the original Android and iOS implementations.

The Android and Apple native implementations originate from approov/approov-service-flutter-httpclient by CriticalBlue Ltd.

License

MIT — see LICENSE. Copyright notices for all upstream authors are retained there.

Libraries

certificate_pinning_httpclient_plus
An implementation of Dart's HttpClient with certificate pinning against SPKI (Subject Public Key Info) SHA-256 hashes.