connect_secure 1.1.0
connect_secure: ^1.1.0 copied to clipboard
Production-ready SSL pinning for Dart/Flutter with Dio, http (IOClient) and dart:io. Protects against MITM attacks with SHA-256 certificate fingerprint validation.
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 #
- ✅ Enhanced error handling with detailed exception information
- ✅ Comprehensive test coverage
- ✅ Complete API documentation
- ✅ Support multiple SSL pinning modes (certificate, public key)
- ✅ Add automatic fingerprint generation tool
- ✅ Certificate chain validation options
- ✅ Error 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