TrustPin SDK for Flutter
A comprehensive Flutter plugin for TrustPin SSL certificate pinning that provides robust security against man-in-the-middle (MITM) attacks by validating server certificates against pre-configured public key pins.
๐ Get started at TrustPin.cloud | ๐ฏ Manage your certificates in the Cloud Console
๐ Table of Contents
- Features
- Installation
- Platform Setup
- Quick Start
- Advanced Usage
- API Documentation
- Example
- Security Considerations
- Platform-Specific Implementation
- Security Best Practices
- Performance Considerations
- Testing
- Example App
- Migration Guide
- Contributing
- License
- Support & Community
๐ Features
- ๐ SSL Certificate Pinning: Advanced certificate validation using SHA-256/SHA-512 public key pins
- ๐ JWS-based Configuration: Securely fetch signed pinning configurations from TrustPin CDN
- ๐ Cross-platform Support: Native implementations for iOS (Swift), Android (Kotlin), and macOS (Swift)
- โ๏ธ Flexible Pinning Modes: Support for strict (production) and permissive (development) validation modes
- ๐ง Comprehensive Error Handling: Detailed error types with programmatic checking capabilities
- ๐ Configurable Logging: Multiple log levels for debugging, monitoring, and production use
- ๐ก๏ธ Thread Safety: Built with Flutter's async/await pattern and native concurrency models
- โก Intelligent Caching: 10-minute configuration caching with stale fallback for performance
- ๐ ECDSA P-256 Signature Verification: Cryptographic validation of configuration integrity
- ๐ HTTP Client Integration: Built-in interceptors for popular HTTP clients (http, Dio)
๐ฆ Installation
Using pub.dev (Recommended)
Add TrustPin SDK to your pubspec.yaml:
dependencies:
trustpin_sdk: ^latest
Then install the package:
flutter pub get
Using Git (Development)
For the latest development version:
dependencies:
trustpin_sdk:
git:
url: https://github.com/trustpin-cloud/trustpin-libraries.git
path: flutter/trustpin_sdk
๐ ๏ธ Platform Setup
iOS Requirements
- Minimum iOS Version: 13.0+
- Xcode: 15.0+
- Swift: 5.0+
- Native Dependencies: TrustPin Swift SDK (automatically configured)
The iOS implementation uses the native TrustPin Swift SDK which is automatically linked via CocoaPods. No additional configuration required.
macOS Requirements
- Minimum macOS Version: 13.0+
- Xcode: 15.0+
- Swift: 5.0+
- Native Dependencies: TrustPin Swift SDK (automatically configured)
The macOS implementation uses the same native TrustPin Swift SDK as iOS, automatically linked via CocoaPods. Requires network client entitlement for sandbox apps.
Android Requirements
- Minimum SDK: API 25 (Android 5.0)+
- Target SDK: API 34+ (recommended)
- Kotlin: 1.9.0+
- Native Dependencies: TrustPin Kotlin SDK (automatically configured)
The Android implementation uses the native TrustPin Kotlin SDK which is automatically included via Gradle. No additional configuration required.
Network Permissions
The SDK requires network access to fetch pinning configurations. Ensure your app has proper network permissions:
Android
The plugin automatically includes the required network permission in its AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
iOS
Network access is enabled by default. For apps targeting iOS 14+, ensure your Info.plist allows network access to cdn.trustpin.cloud:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>cdn.trustpin.cloud</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<false/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
</dict>
</dict>
</dict>
macOS
For sandboxed macOS apps, add the network client entitlement to your entitlements files:
<!-- In DebugProfile.entitlements and Release.entitlements -->
<key>com.apple.security.network.client</key>
<true/>
For non-sandboxed apps, network access is enabled by default.
๐ Quick Start
1. Get Your Credentials
First, sign up at TrustPin Cloud Console and create a project to get your:
- Organization ID
- Project ID
- Public Key (ECDSA P-256, Base64-encoded)
2. Initialize the SDK
import 'package:trustpin_sdk/trustpin_sdk.dart';
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
_initializeTrustPin();
}
Future<void> _initializeTrustPin() async {
try {
// Set debug logging for development
await TrustPin.setLogLevel(TrustPinLogLevel.debug);
// Initialize with your credentials
await TrustPin.setup(
organizationId: 'your-org-id',
projectId: 'your-project-id',
publicKey: 'LS0tLS1CRUdJTi...', // Your Base64 public key
mode: TrustPinMode.strict, // Use strict mode for production
);
print('TrustPin SDK initialized successfully!');
} catch (e) {
print('Failed to initialize TrustPin: $e');
}
}
3. Verify Certificates
Future<void> verifyServerCertificate() async {
// Example PEM certificate (in practice, you'd get this from your HTTP client)
const pemCertificate = '''
-----BEGIN CERTIFICATE-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7Q1jx8...
-----END CERTIFICATE-----
''';
try {
await TrustPin.verify('api.example.com', pemCertificate);
print('โ
Certificate is valid and matches configured pins!');
} on TrustPinException catch (e) {
print('โ Certificate verification failed: ${e.code} - ${e.message}');
// Handle specific error types
if (e.isPinsMismatch) {
print('The certificate doesn\'t match any configured pins');
} else if (e.isDomainNotRegistered) {
print('Domain not configured for pinning (strict mode)');
} else if (e.isAllPinsExpired) {
print('All configured pins have expired');
}
} catch (e) {
print('Unexpected error: $e');
}
}
๐ผ Advanced Usage
Integration with HTTP Clients
Using with Dio
The SDK provides a built-in TrustPinDioInterceptor for seamless Dio integration:
import 'package:dio/dio.dart';
import 'package:trustpin_sdk/trustpin_sdk.dart';
// Create Dio instance with TrustPin certificate validation
final dio = Dio();
dio.interceptors.add(TrustPinDioInterceptor());
// All HTTPS requests will now have certificate pinning validation
try {
final response = await dio.get('https://api.example.com/data');
print('Request successful: ${response.statusCode}');
} on DioException catch (e) {
if (e.error is TrustPinException) {
final trustPinError = e.error as TrustPinException;
print('Certificate pinning failed: ${trustPinError.code} - ${trustPinError.message}');
} else {
print('Request failed: ${e.message}');
}
}
// The interceptor automatically:
// 1. Validates standard TLS certificates (OS-level validation)
// 2. Performs TrustPin certificate pinning validation
// 3. Caches certificates for performance
// 4. Prevents requests with invalid certificates
// Manage certificate cache if needed
final interceptor = TrustPinDioInterceptor();
dio.interceptors.add(interceptor);
// Clear all cached certificates
interceptor.clearCertificateCache();
Using with http package
The SDK provides a built-in TrustPinHttpClient that wraps the standard http.Client:
import 'package:http/http.dart' as http;
import 'package:trustpin_sdk/trustpin_sdk.dart';
// Create a TrustPin-enabled HTTP client
final httpClient = TrustPinHttpClient.create();
// Or wrap an existing client
final customClient = http.Client();
final httpClient = TrustPinHttpClient(customClient);
// Use it like a normal http.Client
final response = await httpClient.get(Uri.parse('https://api.example.com/data'));
// The client automatically:
// 1. Validates standard TLS certificates
// 2. Performs TrustPin certificate pinning
// 3. Caches certificates for performance
// Clear certificate cache if needed
httpClient.clearCertificateCache();
// Clean up when done
httpClient.close();
Environment-Specific Configuration
class TrustPinConfig {
static Future<void> initializeForEnvironment() async {
const environment = String.fromEnvironment('ENVIRONMENT', defaultValue: 'development');
switch (environment) {
case 'production':
await _initializeProduction();
break;
case 'staging':
await _initializeStaging();
break;
default:
await _initializeDevelopment();
}
}
static Future<void> _initializeProduction() async {
await TrustPin.setLogLevel(TrustPinLogLevel.error);
await TrustPin.setup(
organizationId: 'prod-org-123',
projectId: 'prod-project-456',
publicKey: 'LS0tLS1CRUdJTi...', // Production public key
mode: TrustPinMode.strict,
);
}
static Future<void> _initializeStaging() async {
await TrustPin.setLogLevel(TrustPinLogLevel.info);
await TrustPin.setup(
organizationId: 'staging-org-123',
projectId: 'staging-project-456',
publicKey: 'LS0tLS1CRUdJTi...', // Staging public key
mode: TrustPinMode.strict,
);
}
static Future<void> _initializeDevelopment() async {
await TrustPin.setLogLevel(TrustPinLogLevel.debug);
await TrustPin.setup(
organizationId: 'dev-org-123',
projectId: 'dev-project-456',
publicKey: 'LS0tLS1CRUdJTi...', // Development public key
mode: TrustPinMode.permissive, // Allow unpinned domains in development
);
}
}
// Usage in main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await TrustPinConfig.initializeForEnvironment();
runApp(MyApp());
}
Error Handling Patterns
class TrustPinErrorHandler {
static void handleVerificationError(TrustPinException error, String domain) {
// Log error for monitoring
_logSecurityEvent(error, domain);
switch (error.code) {
case 'PINS_MISMATCH':
_handlePinsMismatch(error, domain);
break;
case 'DOMAIN_NOT_REGISTERED':
_handleDomainNotRegistered(error, domain);
break;
case 'ALL_PINS_EXPIRED':
_handleAllPinsExpired(error, domain);
break;
case 'INVALID_SERVER_CERT':
_handleInvalidCertificate(error, domain);
break;
case 'ERROR_FETCHING_PINNING_INFO':
_handleNetworkError(error, domain);
break;
default:
_handleUnknownError(error, domain);
}
}
static void _handlePinsMismatch(TrustPinException error, String domain) {
// Critical security issue - potential MITM attack
print('๐จ SECURITY ALERT: Certificate mismatch for $domain');
// Consider blocking the request and alerting the user
}
static void _handleDomainNotRegistered(TrustPinException error, String domain) {
print('โ ๏ธ Domain $domain not configured for pinning');
// In strict mode, this might be intentional or an oversight
}
static void _handleAllPinsExpired(TrustPinException error, String domain) {
print('โฐ All pins expired for $domain - update needed');
// Consider allowing the request but log for monitoring
}
static void _handleInvalidCertificate(TrustPinException error, String domain) {
print('โ Invalid certificate format for $domain');
// This might indicate a parsing issue
}
static void _handleNetworkError(TrustPinException error, String domain) {
print('๐ Network error fetching pinning configuration');
// Consider using cached configuration or fallback behavior
}
static void _handleUnknownError(TrustPinException error, String domain) {
print('โ Unknown error for $domain: ${error.message}');
// Log for investigation
}
static void _logSecurityEvent(TrustPinException error, String domain) {
// Send to your security monitoring system
final event = {
'timestamp': DateTime.now().toIso8601String(),
'domain': domain,
'error_code': error.code,
'error_message': error.message,
'app_version': 'your-app-version',
};
// Send to your logging/monitoring service
print('Security event logged: $event');
}
}
// Usage
try {
await TrustPin.verify('api.example.com', certificate);
} on TrustPinException catch (e) {
TrustPinErrorHandler.handleVerificationError(e, 'api.example.com');
// Decide whether to proceed with the request or abort
rethrow; // Re-throw if you want to abort the request
}
๐ API Documentation
For complete API documentation, visit our GitHub Pages Documentation.
TrustPin Class (Static Methods)
TrustPin.setup()
Initializes the TrustPin SDK with your project credentials.
static Future<void> setup({
required String organizationId,
required String projectId,
required String publicKey,
Uri? configurationURL, // Optional custom URL for self-hosted configurations
TrustPinMode mode = TrustPinMode.strict,
})
Parameters:
organizationId: Your organization identifier from the TrustPin dashboardprojectId: Your project identifier from the TrustPin dashboardpublicKey: Base64-encoded ECDSA P-256 public key for JWS verificationconfigurationURL: Optional custom URL for self-hosted configurations (null for CDN-managed)mode: Pinning mode (strict or permissive)
Throws: TrustPinException if setup fails
TrustPin.verify()
Verifies a certificate against the configured pins for a domain.
static Future<void> verify(String domain, String certificate)
Parameters:
domain: The domain name to verify (e.g., "api.example.com")certificate: PEM-encoded certificate string with BEGIN/END markers
Throws: TrustPinException if verification fails
TrustPin.setLogLevel()
Sets the logging level for TrustPin SDK.
static Future<void> setLogLevel(TrustPinLogLevel level)
TrustPinHttpClient Class
A certificate pinning interceptor for the http package that wraps any http.Client.
Constructor
TrustPinHttpClient(http.Client inner)
Factory Constructor
factory TrustPinHttpClient.create()
Creates a TrustPinHttpClient with a default http.Client.
Methods
send(BaseRequest request): Sends HTTP request with certificate validationclearCertificateCache(): Clears the internal certificate cacheclose(): Closes the client and clears resources
TrustPinDioInterceptor Class
A certificate pinning interceptor for the Dio HTTP client.
Constructor
TrustPinDioInterceptor()
Creates a new interceptor that validates certificates for all HTTPS requests made through Dio.
Methods
onRequest(RequestOptions, RequestInterceptorHandler): Validates certificates before sending requestsclearCertificateCache(): Clears the internal certificate cache
Enums
TrustPinMode
Pinning modes that control behavior for unregistered domains:
TrustPinMode.strict: Throws errors for unregistered domains (recommended for production)TrustPinMode.permissive: Allows unregistered domains to bypass pinning (development/testing)
TrustPinLogLevel
Log levels for controlling SDK output verbosity:
TrustPinLogLevel.none: No logging outputTrustPinLogLevel.error: Only error messagesTrustPinLogLevel.info: Error and informational messagesTrustPinLogLevel.debug: All messages including debug information
Exception Handling
TrustPinException
Exception thrown by TrustPin operations with detailed error information:
Properties:
code: Error code identifying the type of errormessage: Human-readable error messagedetails: Additional error details (may be null)
Error Types:
INVALID_PROJECT_CONFIG: Invalid setup parametersERROR_FETCHING_PINNING_INFO: CDN fetch failureINVALID_SERVER_CERT: Invalid certificate formatPINS_MISMATCH: Certificate doesn't match configured pinsALL_PINS_EXPIRED: All configured pins have expiredJWS_VALIDATION_FAILED: JWS signature validation failedDOMAIN_NOT_REGISTERED: Domain not configured (strict mode only)CONFIGURATION_VALIDATION_FAILED: Configuration validation failed
Helper Methods:
// Check specific error types
if (exception.isPinsMismatch) {
// Handle certificate mismatch
}
if (exception.isDomainNotRegistered) {
// Handle unregistered domain in strict mode
}
Example
See the example/ directory for a complete sample application that demonstrates:
- SDK initialization with credentials
- Certificate verification
- Error handling
- Log level configuration
Security Considerations
Production Deployment
- Use Strict Mode: Always use
TrustPinMode.strictin production - Secure Credentials: Never commit credentials to version control
- Minimal Logging: Use
TrustPinLogLevel.errorornonein production - Regular Updates: Keep pinning configurations up to date
Development and Testing
- Permissive Mode: Use
TrustPinMode.permissivefor development - Debug Logging: Enable
TrustPinLogLevel.debugfor troubleshooting - Test Environment: Use separate credentials for testing
Platform-Specific Implementation
iOS
- Uses TrustPin Swift SDK from https://github.com/trustpin-cloud/TrustPin-Swift.binary
- Supports iOS 13.0 and later
- Built with Swift 5.0+
- Async/await support for modern Swift concurrency
macOS
- Uses TrustPin Swift SDK from https://github.com/trustpin-cloud/TrustPin-Swift.binary
- Supports macOS 13.0 and later
- Built with Swift 5.0+
- Async/await support for modern Swift concurrency
- Sandboxing compatible with proper entitlements
Android
- Uses TrustPin Kotlin SDK from Maven (
cloud.trustpin:kotlin-sdk:<<version>>) - Supports Android API 21 and later
- Built with Kotlin coroutines
- Multiplatform support (Android/JVM)
๐ Security Best Practices
Production Checklist
- โ
Use
TrustPinMode.strictmode - โ
Set log level to
TrustPinLogLevel.errorornone - โ Store credentials securely (not in source code)
- โ Implement proper error handling for certificate failures
- โ Monitor certificate validation errors
- โ Keep pinning configurations up to date
- โ Test certificate validation in staging environment
Development Tips
- ๐ง Use
TrustPinMode.permissivefor development - ๐ง Enable
TrustPinLogLevel.debugfor troubleshooting - ๐ง Use separate credentials for different environments
- ๐ง Test with both valid and invalid certificates
- ๐ง Verify error handling works correctly
๐ Performance Considerations
- Configuration Caching: Pinning configurations are cached for 10 minutes
- Network Optimization: Initial setup requires one CDN request
- Memory Usage: Minimal memory footprint with efficient native implementations
- CPU Impact: Certificate validation is highly optimized in native code
- Battery Life: Negligible impact on battery consumption
๐งช Testing
The plugin includes comprehensive test coverage:
# Run unit tests
flutter test
# Run integration tests (requires device/emulator)
cd example
flutter test integration_test/plugin_integration_test.dart
# Run with coverage
flutter test --coverage
๐ฑ Example App
The example/ directory contains a complete sample application demonstrating:
- SDK initialization with different configurations
- Certificate verification with various scenarios
- Error handling patterns
- Integration with HTTP clients
- Environment-specific setup
To run the example:
cd example
flutter pub get
flutter run
๐ Migration Guide
From Direct Native SDK Usage
If you're currently using the native TrustPin SDKs directly:
-
Replace native SDK imports:
// Remove native imports // iOS: import TrustPinKit // Android: import cloud.trustpin.kotlin.sdk.TrustPin // Add Flutter plugin import 'package:trustpin_sdk/trustpin_sdk.dart'; -
Update initialization:
// Old (iOS) // try await TrustPin.setup(organizationId: "...", projectId: "...", publicKey: "...", mode: .strict) // Old (Android) // val trustPin = TrustPin.create() // trustPin.setup("...", "...", "...") // New (Flutter) - Using static methods await TrustPin.setup( organizationId: 'your-org-id', projectId: 'your-project-id', publicKey: 'your-public-key', mode: TrustPinMode.strict, ); -
Update certificate verification:
// Old (iOS) // try await TrustPin.verify(domain: "api.example.com", certificate: pemCert) // Old (Android) // trustPin.verify("api.example.com", x509Certificate) // New (Flutter) - Using static methods await TrustPin.verify('api.example.com', pemCertificate);
๐ค Contributing
We welcome contributions! Please see our Contributing Guide for detailed information about:
- Development environment setup
- Code style guidelines
- Testing requirements
- Pull request process
Quick Contribution Steps
- Fork the repository
- Create feature branch:
git checkout -b feature/amazing-feature - Make changes with tests
- Run quality checks:
dart analyze --fatal-infos dart format --set-exit-if-changed . flutter test - Create pull request with clear description
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Support & Community
Getting Help
- ๐ Documentation: GitHub Pages
- ๐ API Reference: pub.dev/documentation/trustpin_sdk
- ๐ Issues: GitHub Issues
- ๐ฌ Discussions: GitHub Discussions
Professional Support
- ๐ง Email: [email protected]
- ๐ Website: trustpin.cloud
- ๐ Documentation: docs.trustpin.cloud
- ๐ฏ Cloud Console: app.trustpin.cloud
Stay Updated
- โญ Star this repository to stay updated with releases
- ๐ Watch for important security updates
- ๐ข Follow @TrustPinCloud on Twitter
๐ Secure your Flutter apps with TrustPin SSL Certificate Pinning ๐
Get Started โข Documentation โข Support