connect_secure
A production-ready Dart/Flutter package for SSL certificate pinning. Ensures your application only communicates with trusted servers by validating server certificates or public key fingerprints against predefined values.
This package helps protect against Man-in-the-Middle (MITM) attacks and ensures secure communication channels.
Features
- SSL Pinning with SHA-256 certificate fingerprints
- Works with Dio and http (IOClient)
- Host-based pinning and fingerprint normalization (colon/space-insensitive)
- Easy to configure and integrate
- Lightweight and customizable
- Multiple SSL Pinning Modes - Pin certificates or public keys (more flexible)
- Automatic Fingerprint Generation - Auto-discover fingerprints from servers
- Certificate Chain Validation - Validate leaf, intermediate, or full chain
- Error Tracking & Diagnostics - Intelligent error categorization with actionable fix suggestions
- Server/Client Issue Detection - Automatically identify if errors are server-side or client-side
- Analytics Integration - Built-in support for Sentry, Firebase, and custom analytics
- Certificate Discovery - Automatically extract fingerprints from servers
- Certificate Monitoring - Real-time health monitoring and expiry alerts
- Certificate Validation - Validate certificates against expected fingerprints
- Certificate Rotation - Seamless certificate updates and rotation
- CLI Tools - Command-line interface for certificate management
- Multi-Environment Support - Manage certificates across different environments
Installation
Add the dependency in your pubspec.yaml:
dependencies:
connect_secure: ^1.0.1
Then run:
flutter pub get
Usage
Import the package:
import 'package:connect_secure/connect_secure.dart';
Example: Using SSL Pinning with Dio
import 'package:dio/dio.dart';
import 'package:connect_secure/connect_secure.dart';
void main() async {
final dio = Dio();
// Attach SSL Pinning adapter
dio.httpClientAdapter = DioSslPinning(
allowedFingerprints: [
// Add your server's SHA-256 certificate fingerprint (colon/space format allowed)
"12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF",
],
// Optionally pin different hosts to different fingerprints
fingerprintsByHost: {
"api.your-secure-api.com": [
"12 34 56 78 90 ab cd ef 12 34 56 78 90 ab cd ef 12 34 56 78 90 ab cd ef 12 34 56 78 90 ab cd ef",
],
},
);
try {
final response = await dio.get("https://your-secure-api.com");
print("Response: ${response.data}");
} catch (e) {
print("SSL Pinning validation failed: $e");
}
}
Example: Using SSL Pinning with http (IOClient)
import 'package:http/http.dart' as http;
import 'package:connect_secure/connect_secure.dart';
void main() async {
final client = createPinnedHttpClient(
allowedFingerprints: [
"12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF",
],
fingerprintsByHost: {
"example.com": ["f9b2f8d74c6f5f8e8c0b9e0d12345abcd..."],
},
);
final response = await client.get(Uri.parse('https://example.com'));
print(response.statusCode);
}
Example: Using raw dart:io HttpClient
import 'dart:convert';
import 'package:connect_secure/connect_secure.dart';
void main() async {
final httpClient = SecureHttpClient(
allowedFingerprints: [
"12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF",
],
fingerprintsByHost: {
"example.com": ["f9b2f8d74c6f5f8e8c0b9e0d12345abcd..."],
},
);
final res = await httpClient.get(Uri.parse('https://example.com'));
final body = await res.transform(utf8.decoder).join();
print(body);
}
Security Best Practices
1. Obtaining Certificate Fingerprints
To get the SHA-256 fingerprint of your server's certificate:
# Using OpenSSL
openssl s_client -connect your-domain.com:443 -servername your-domain.com < /dev/null 2>/dev/null | openssl x509 -fingerprint -sha256 -noout
# Using curl
curl -s "https://your-domain.com" | openssl s_client -connect your-domain.com:443 -servername your-domain.com 2>/dev/null | openssl x509 -fingerprint -sha256 -noout
2. Error Handling
Always handle SslPinningException in your application:
try {
final response = await dio.get("https://api.example.com");
print("Secure connection established");
} on SslPinningException catch (e) {
print("SSL Pinning failed: ${e.message}");
print("Host: ${e.host}");
print("Rejected fingerprint: ${e.rejectedFingerprint}");
print("Allowed fingerprints: ${e.allowedFingerprints}");
// Handle the security violation appropriately
} catch (e) {
print("Other error: $e");
}
3. Multiple Fingerprints
Always pin multiple certificates for redundancy:
final allowedFingerprints = [
"current_certificate_fingerprint",
"backup_certificate_fingerprint", // For certificate rotation
"intermediate_certificate_fingerprint",
];
4. Certificate Rotation
Plan for certificate updates by:
- Pinning both current and backup certificates
- Implementing a secure update mechanism
- Monitoring certificate expiration dates
SSL Pinning Error Tracking & Diagnostics
One of the biggest challenges with SSL pinning is debugging failures in production. When SSL pinning fails, it's often unclear whether it's a server misconfiguration, certificate expiration, client error, or a security threat.
connect_secure includes a comprehensive Error Tracking & Diagnostics System that provides:
- Automatic error categorization (fingerprint mismatch, certificate expired, MITM detected, etc.)
- Server-side vs client-side issue detection
- Actionable fix suggestions with specific commands
- Integration with analytics services (Sentry, Firebase, etc.)
- Error statistics and patterns analysis
- Detailed diagnostic reports with all relevant context
Quick Start
import 'package:connect_secure/connect_secure.dart';
import 'package:dio/dio.dart';
void main() async {
// Create error tracker with detailed logging
final errorTracker = SslPinningErrorTracker(
enableDetailedLogging: true,
onError: (diagnostics) {
// Send to your analytics service
print('SSL Failure: ${diagnostics.severity}');
// Integration examples:
// Sentry.captureException(diagnostics.message, extra: diagnostics.toJson());
// FirebaseCrashlytics.instance.recordError(...);
},
);
// Use with Dio
final dio = Dio();
dio.httpClientAdapter = DioSslPinning(
allowedFingerprints: ["your:fingerprint:here"],
errorTracker: errorTracker, // Add error tracker
);
try {
await dio.get("https://your-api.com");
} on SslPinningException catch (e) {
// Detailed diagnostics already logged and sent to analytics
print("SSL pinning failed: ${e.message}");
}
}
Error Categories
The tracker automatically categorizes failures:
| Category | Description | Severity |
|---|---|---|
fingerprintMismatch |
Certificate doesn't match expected fingerprints | HIGH |
certificateExpired |
Server certificate has expired | HIGH |
certificateNotYetValid |
Certificate validity period hasn't started | HIGH |
serverConfigurationError |
Server SSL/TLS misconfiguration | HIGH |
clientConfigurationError |
Invalid SSL pinning configuration | MEDIUM |
possibleMitmDetected |
Potential Man-in-the-Middle attack | CRITICAL |
networkError |
Connection or network issues | MEDIUM |
certificateRevoked |
Certificate has been revoked | CRITICAL |
Integration with Analytics Services
Sentry Integration
final errorTracker = SslPinningErrorTracker(
enableDetailedLogging: false, // Disable console in production
onError: (diagnostics) {
Sentry.captureException(
Exception(diagnostics.message),
stackTrace: diagnostics.stackTrace,
withScope: (scope) {
scope.setContexts('ssl_pinning', diagnostics.toJson());
scope.setLevel(
diagnostics.severity == 'CRITICAL'
? SentryLevel.fatal
: SentryLevel.error,
);
},
);
},
);
Firebase Crashlytics Integration
final errorTracker = SslPinningErrorTracker(
onError: (diagnostics) {
FirebaseCrashlytics.instance.recordError(
Exception(diagnostics.message),
diagnostics.stackTrace,
reason: diagnostics.category.name,
information: [diagnostics.toJson()],
fatal: diagnostics.isPotentialSecurityThreat,
);
},
);
Error Statistics
Track and analyze SSL pinning failures over time:
final errorTracker = SslPinningErrorTracker(
maxStoredErrors: 100, // Keep last 100 errors
);
// After some time...
final stats = errorTracker.getStatistics();
print(stats);
// {
// 'totalErrors': 15,
// 'byCategory': {'fingerprintMismatch': 12, 'networkError': 3},
// 'byHost': {'api.example.com': 10, 'cdn.example.com': 5},
// 'bySeverity': {'HIGH': 12, 'MEDIUM': 3},
// 'oldestError': '2025-10-01T08:00:00.000Z',
// 'newestError': '2025-10-01T10:30:45.000Z',
// }
Production Best Practices
-
Development: Enable detailed logging
SslPinningErrorTracker(enableDetailedLogging: true) -
Staging: Log to console + send to analytics
SslPinningErrorTracker( enableDetailedLogging: true, onError: (d) => sendToAnalytics(d), ) -
Production: Silent logging with analytics only
SslPinningErrorTracker( enableDetailedLogging: false, onError: (d) => sendToAnalytics(d), )
Complete Example
See error_tracking_example.dart for a complete working example.
Multiple SSL Pinning Modes
connect_secure supports two pinning modes for maximum flexibility:
Certificate Pinning (Default)
Pin the entire certificate fingerprint. Most restrictive but provides maximum security.
final dio = Dio();
dio.httpClientAdapter = DioSslPinning(
allowedFingerprints: ["your:certificate:fingerprint"],
pinningMode: SslPinningMode.certificate, // Default
);
Pros:
- Maximum security
- Detects any certificate change
Cons:
- Requires app update when certificate renews
- More maintenance overhead
Public Key Pinning
Pin only the public key fingerprint. More flexible and survives certificate renewals.
final dio = Dio();
dio.httpClientAdapter = DioSslPinning(
allowedFingerprints: ["your:public:key:fingerprint"],
pinningMode: SslPinningMode.publicKey, // More flexible
);
Pros:
- Survives certificate renewals (if same key pair)
- Less maintenance
- Still secure (public key doesn't change)
Cons:
- Slightly less restrictive than certificate pinning
When to Use Each Mode
| Use Case | Recommended Mode |
|---|---|
| High-security apps (banking, healthcare) | Certificate |
| Apps with frequent certificate renewals | Public Key |
| Apps with multiple environments | Public Key |
| Maximum security requirement | Certificate |
| Reduced maintenance overhead | Public Key |
Automatic Fingerprint Generation
No more manual OpenSSL commands! Automatically discover fingerprints from any server.
Quick Start
import 'package:connect_secure/connect_secure.dart';
void main() async {
// Generate fingerprints from a server
final result = await FingerprintGenerator.generateFromHost('api.example.com');
print('Certificate Fingerprint: ${result.certificateFingerprint}');
print('Public Key Fingerprint: ${result.publicKeyFingerprint}');
// Get ready-to-use code
final code = FingerprintGenerator.formatForCode(
result,
mode: SslPinningMode.publicKey,
);
print(code);
}
Generate from Multiple Hosts
// Discover fingerprints from multiple endpoints
final results = await FingerprintGenerator.generateFromHosts([
'api.example.com',
'cdn.example.com',
'auth.example.com',
]);
for (final entry in results.entries) {
print('${entry.key}: ${entry.value.certificateFingerprint}');
}
Use Generated Fingerprints
// 1. Generate fingerprints
final result = await FingerprintGenerator.generateFromHost('api.example.com');
// 2. Use in your SSL pinning configuration
final dio = Dio();
dio.httpClientAdapter = DioSslPinning(
allowedFingerprints: [
result.getFingerprint(SslPinningMode.publicKey),
],
pinningMode: SslPinningMode.publicKey,
);
Certificate Chain Validation
Control which certificates in the chain to validate for enhanced security.
Validation Modes
Leaf Only (Default)
Only validate the server (leaf) certificate. Fastest and most common.
dio.httpClientAdapter = DioSslPinning(
allowedFingerprints: [...],
chainValidation: CertificateChainValidation.leafOnly, // Default
);
Include Intermediate CAs
Validate both leaf and intermediate CA certificates. More secure.
dio.httpClientAdapter = DioSslPinning(
allowedFingerprints: [
'leaf:certificate:fingerprint',
'intermediate:ca:fingerprint',
],
chainValidation: CertificateChainValidation.includeIntermediate,
);
Full Chain
Validate the entire certificate chain including root CA. Maximum security.
dio.httpClientAdapter = DioSslPinning(
allowedFingerprints: [
'leaf:certificate:fingerprint',
'intermediate:ca:fingerprint',
'root:ca:fingerprint',
],
chainValidation: CertificateChainValidation.fullChain,
);
When to Use Each Mode
| Mode | Use Case | Security Level |
|---|---|---|
leafOnly |
Standard apps, fastest validation | High |
includeIntermediate |
Enterprise apps, detect CA compromise | Very High |
fullChain |
Maximum security requirements | Maximum |
Example: Complete Configuration
// 1. Generate fingerprints automatically
final result = await FingerprintGenerator.generateFromHost('api.example.com');
// 2. Configure SSL pinning with public key mode and chain validation
final dio = Dio();
dio.httpClientAdapter = DioSslPinning(
allowedFingerprints: [
result.publicKeyFingerprint,
],
pinningMode: SslPinningMode.publicKey,
chainValidation: CertificateChainValidation.includeIntermediate,
errorTracker: SslPinningErrorTracker(
enableDetailedLogging: true,
),
);
CLI Tools
The package includes powerful CLI tools for certificate management:
Certificate Discovery
# Discover certificates from a server
connect_secure discover --host api.example.com
# Output in different formats
connect_secure discover --host api.example.com --json
connect_secure discover --host api.example.com --yaml
connect_secure discover --host api.example.com --dart --output config.dart
Certificate Monitoring
# Check certificate health once
connect_secure monitor --host api.example.com
# Continuous monitoring
connect_secure monitor --host api.example.com --daemon --interval 30
# Monitor with JSON output
connect_secure monitor --host api.example.com --json --output logs.json
Certificate Validation
# Validate certificate
connect_secure validate --host api.example.com
# Validate against specific fingerprints
connect_secure validate --host api.example.com --fingerprints "abc123,def456"
Certificate Rotation
# Dry run certificate rotation
connect_secure rotate --host api.example.com --dry-run
# Rotate certificates
connect_secure rotate --host api.example.com --new-fingerprints "abc123,def456"
Example Project
See the example for a full working demo.
Advanced Certificate Management
Check out certificate_lifecycle_example.dart for advanced certificate lifecycle management features.
Roadmap
xEnhanced error handling with detailed exception informationxComprehensive test coveragexComplete API documentationxSupport multiple SSL pinning modes (certificate, public key)xAdd automatic fingerprint generation toolxCertificate chain validation optionsxError tracking and diagnostics system
Contributing
Contributions are welcome!
- Fork the repo
- Create your feature branch (
git checkout -b feature/my-feature) - Commit your changes (
git commit -m 'Add some feature') - Push to the branch (
git push origin feature/my-feature) - Create a Pull Request
License
This project is licensed under the MIT License. See the LICENSE file for details.
Author
Neethu KT
- GitLab: connect_secure