SSL Pinning
Secure network communication in Flutter
🔐 What is SSL Pinning?
SSL Pinning validates server certificates to prevent man-in-the-middle attacks. Flutter apps use certificate pinning to ensure they only communicate with trusted servers, protecting sensitive data during network transmission from interception and tampering.
// Basic SSL pinning setup
final client = HttpClient()
..badCertificateCallback =
(cert, host, port) => cert.sha1 == expectedSha1;
Key SSL Pinning Concepts
Certificate Pinning
Validate server's SSL certificate against a known trusted certificate stored in app
SecurityContext context =
SecurityContext()
..setTrustedCertificates('cert.pem');
Public Key Pinning
Pin the server's public key instead of entire certificate for flexible security
cert.sha1 ==
expectedPublicKeyHash
MITM Protection
Prevents man-in-the-middle attacks by rejecting untrusted or fake certificates
badCertificateCallback =
(cert, host, port) =>
validateCert(cert);
HTTPS Security
Ensures all network requests use secure HTTPS protocol with verified certificates
final response =
await http.get(
Uri.parse('https://api.example.com')
);
🔹 Setup SSL Pinning Package
Add http_certificate_pinning package to your project:
# pubspec.yaml
dependencies:
http_certificate_pinning: ^2.1.1
// Import the package
import 'package:http_certificate_pinning/http_certificate_pinning.dart';
Result:
SSL pinning library is ready to secure your network communications.
🔹 Basic Certificate Pinning
Implement certificate pinning for API calls:
import 'package:http_certificate_pinning/http_certificate_pinning.dart';
class ApiService {
Future checkCertificate() async {
List allowedSHAFingerprints = [
'E6:3C:50:6D:75:A1:5E:5C:8B:9F:3D:2A:1B:4C:7E:8F:9A:2D:3E:4F',
];
try {
await HttpCertificatePinning.check(
serverURL: 'https://api.example.com',
headerHttp: {'Content-Type': 'application/json'},
sha: SHA.SHA256,
allowedSHAFingerprints: allowedSHAFingerprints,
timeout: 60,
);
print('Certificate is valid');
} catch (e) {
print('Certificate validation failed: $e');
}
}
}
Result:
API calls only succeed if server certificate matches the pinned fingerprint.
🔹 Custom HttpClient with Pinning
Create a custom HTTP client with certificate validation:
import 'dart:io';
class SecureHttpClient {
final String expectedSha256 =
'E63C506D75A15E5C8B9F3D2A1B4C7E8F9A2D3E4F...';
HttpClient createSecureClient() {
final client = HttpClient();
client.badCertificateCallback =
(X509Certificate cert, String host, int port) {
// Get certificate SHA-256 fingerprint
final certSha256 = cert.sha1.toString();
// Compare with expected fingerprint
return certSha256 == expectedSha256;
};
return client;
}
Future makeSecureRequest(String url) async {
final client = createSecureClient();
try {
final request = await client.getUrl(Uri.parse(url));
final response = await request.close();
final responseBody = await response.transform(utf8.decoder).join();
return responseBody;
} finally {
client.close();
}
}
}
Result:
HTTP client validates certificate before making any network request.
🔹 Get Certificate Fingerprint
Extract certificate fingerprint from your server:
# Using OpenSSL command line
openssl s_client -connect api.example.com:443 < /dev/null | \
openssl x509 -fingerprint -sha256 -noout
# Output example:
# SHA256 Fingerprint=E6:3C:50:6D:75:A1:5E:5C:8B:9F:3D:2A:1B:4C:7E:8F
// Use this fingerprint in your Flutter app
final allowedFingerprints = [
'E6:3C:50:6D:75:A1:5E:5C:8B:9F:3D:2A:1B:4C:7E:8F',
];
Result:
Server certificate fingerprint obtained and ready to use in your app.
🔹 Multiple Certificate Pinning
Pin multiple certificates for backup and rotation:
class MultiCertPinning {
final List allowedFingerprints = [
// Primary certificate
'E6:3C:50:6D:75:A1:5E:5C:8B:9F:3D:2A:1B:4C:7E:8F',
// Backup certificate
'A1:B2:C3:D4:E5:F6:07:18:29:3A:4B:5C:6D:7E:8F:90',
];
Future validateCertificate(String url) async {
try {
await HttpCertificatePinning.check(
serverURL: url,
headerHttp: {},
sha: SHA.SHA256,
allowedSHAFingerprints: allowedFingerprints,
timeout: 60,
);
return true;
} catch (e) {
return false;
}
}
}
// Usage
final pinning = MultiCertPinning();
bool isValid = await pinning.validateCertificate('https://api.example.com');
Result:
App accepts multiple valid certificates, allowing for certificate rotation without app updates.
🔹 Dio with SSL Pinning
Use SSL pinning with Dio HTTP client:
# Add dependencies
dependencies:
dio: ^5.3.3
dio_http_certificate_pinning: ^1.0.0
import 'package:dio/dio.dart';
import 'package:dio_http_certificate_pinning/dio_http_certificate_pinning.dart';
class SecureDioClient {
Dio createDioWithPinning() {
final dio = Dio();
dio.interceptors.add(
CertificatePinningInterceptor(
allowedSHAFingerprints: [
'E6:3C:50:6D:75:A1:5E:5C:8B:9F:3D:2A:1B:4C:7E:8F',
],
),
);
return dio;
}
Future makeRequest() async {
final dio = createDioWithPinning();
return await dio.get('https://api.example.com/data');
}
}
Result:
Dio client automatically validates certificates for all requests.
🔹 Handle Pinning Failures
Gracefully handle certificate validation failures:
class SecureApiClient {
Future
Result:
App detects and handles certificate validation failures with appropriate error messages.