flutter_ssl_pinning_client
An enterprise-grade SSL/TLS Subject Public Key Info (SPKI) and X.509 Certificate Pinning HTTP client for Flutter and Dart applications. Built to prevent Man-In-The-Middle (MITM) proxy attacks, rogue Certificate Authority (CA) interception, and network traffic tampering in mobile banking, fintech, and security-critical mobile applications.
Overview
Traditional HTTPS connections rely on trusted Certificate Authorities (CAs) pre-installed on user devices. If an attacker installs a custom root CA (such as via Charles Proxy, Fiddler, Burp Suite, or device compromise), standard network requests can be intercepted and decrypted in plaintext.
flutter_ssl_pinning_client extends Dart's standard http.BaseClient and enforces cryptographic verification during the TLS handshake. It compares the server's presented SHA-256 Public Key (SPKI) or certificate fingerprint against a set of pinned hashes before allowing network traffic to proceed.
Key Capabilities
- SPKI & Certificate SHA-256 Pinning: Supports both Subject Public Key Info (SPKI) hashes and X.509 DER certificate SHA-256 fingerprints.
- Backup Pin Support: Configure primary and backup pins to support seamless certificate rotation without breaking deployed app builds.
- Wildcard Domain Matching: Support exact host matching (
api.bank.com) or wildcard domain rules (*.bank.com). - Dynamic Over-The-Air (OTA) Pin Rotation: Programmatically update pinned hashes at runtime from secure remote configuration services.
- Security Audit Event Telemetry: Intercept structured security events for real-time audit logging to Sentry, Datadog, or Firebase Crashlytics.
- Developer Fingerprint Extraction: Helper utilities to extract and verify server SHA-256 fingerprints during local development.
- Enforcement Modes: Toggle between strict termination (
strict) and passive reporting (reportOnly).
Installation
Add flutter_ssl_pinning_client as a dependency in your pubspec.yaml:
dependencies:
flutter_ssl_pinning_client: ^1.1.1
Run flutter pub get to install the package.
Usage Guide
1. Basic Configuration
To protect network requests to your API domain, instantiate SslPinningHttpClient with a SslPinningConfig:
import 'package:flutter_ssl_pinning_client/flutter_ssl_pinning_client.dart';
final client = SslPinningHttpClient(
config: SslPinningConfig(
mode: EnforceMode.strict,
domainConfigs: {
'api.mycompany.com': const DomainPinConfig(
domain: 'api.mycompany.com',
allowedSHA256Pins: {
'sha256/7130325d70678f564757c2a71f008801d9f67822998f45a49c362947d95393c5',
'sha256/backup_pin_hash_here...',
},
),
},
),
);
// Perform standard HTTP requests
final response = await client.get(Uri.parse('https://api.mycompany.com/v1/user'));
2. Wildcard Domain Matching
You can apply pinning rules across subdomains using wildcard patterns:
final config = SslPinningConfig(
domainConfigs: {
'*.mycompany.com': const DomainPinConfig(
domain: '*.mycompany.com',
allowedSHA256Pins: {
'sha256/7130325d70678f564757c2a71f008801d9f67822998f45a49c362947d95393c5',
},
bypassPathPrefixes: ['/public-cdn/'], // Bypass static media assets if needed
),
},
);
3. Extracting Server Fingerprints
During development, you can extract a server's SHA-256 fingerprint programmatically:
void main() async {
final fingerprint = await SslPinningHelper.extractServerFingerprint('https://api.mycompany.com');
print('Hex Hash: ${fingerprint["hex"]}');
print('Base64 Hash: ${fingerprint["base64"]}');
print('Formatted Pin: ${fingerprint["formattedPin"]}');
}
4. Over-The-Air (OTA) Pin Rotation
Update pinned hashes dynamically at runtime (for example, after fetching signed remote configuration):
// Update pins for an existing domain without re-instantiating the client
config.updatePinsForDomain('api.mycompany.com', {
'sha256/new_primary_fingerprint...',
'sha256/new_backup_fingerprint...',
});
5. Security Audit Logging & Expiration Alerts
Intercept TLS handshake results for compliance monitoring or telemetry:
final config = SslPinningConfig(
onSecurityAuditEvent: (SecurityAuditEvent event) {
print('Domain: ${event.domain}');
print('Presented Fingerprint: ${event.presentedFingerprint}');
print('Passed: ${event.isPassed}');
// Forward event payload to Datadog, Sentry, or backend SIEM
},
onCertificateNearExpiry: (String domain, int daysRemaining) {
print('Warning: Certificate for $domain expires in $daysRemaining days.');
},
domainConfigs: { ... },
);
License
MIT License. Developed by Olamilekan Adeyemi (@lekthedeveloper).