Skip to content

TrustPin is a modern certificate pinning platform. Implement certificate pinning without the pain of force updates or certificates management

License

Notifications You must be signed in to change notification settings

trustpin-cloud/TrustPin-Swift.binary

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

32 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

TrustPin iOS SDK

Swift iOS License

TrustPin is a modern, lightweight, and secure iOS/macOS library designed to enforce SSL Certificate Pinning for native applications. Built with Swift Concurrency and following OWASP security recommendations, TrustPin prevents man-in-the-middle (MITM) attacks by ensuring server authenticity at the TLS level.

πŸš€ Key Features

  • βœ… Modern Swift Concurrency - Built with async/await for seamless integration
  • βœ… Flexible Pinning Modes - Strict validation or permissive mode for development
  • βœ… Multiple Hash Algorithms - SHA-256 and SHA-512 certificate validation
  • βœ… Signed Configuration - Cryptographically signed pinning configurations
  • βœ… Multiple Integration Options - System-wide URLProtocol, URLSessionDelegate, or static helper methods
  • βœ… Intelligent Caching - 10-minute configuration cache with stale fallback
  • βœ… Comprehensive Logging - Configurable log levels for debugging and monitoring
  • βœ… Cross-Platform - iOS, macOS, watchOS, tvOS, and Mac Catalyst support
  • βœ… Enhanced Security - Advanced signature verification with multiple authentication methods

πŸ“‹ Platform Requirements

Platform Minimum Version URLProtocol System-Wide Pinning
iOS 13.0+ βœ… Supported
macOS 13.0+ βœ… Supported
watchOS 7.0+ βœ… Supported
tvOS 13.0+ βœ… Supported
Mac Catalyst 13.0+ βœ… Supported
visionOS 2.0+ βœ… Supported

Required: Swift 5.5+ for async/await support Note: URLProtocol-based features require iOS 13.0+ (available on all supported platforms)


πŸ“¦ Installation

Swift Package Manager (Recommended)

Add TrustPin to your project using Xcode:

  1. File β†’ Add Package Dependencies
  2. Enter repository URL:
    https://github.com/trustpin-cloud/TrustPin-Swift.binary
    
  3. Select version: 1.2.0 or later

Manual Package.swift

dependencies: [
    .package(url: "https://github.com/trustpin-cloud/TrustPin-Swift.binary", from: "1.2.0")
],
targets: [
    .target(
        name: "YourApp",
        dependencies: [
            .product(name: "TrustPinKit", package: "TrustPin-Swift")
        ]
    )
]

CocoaPods

Add TrustPin to your Podfile:

pod 'TrustPinKit'

Then run:

pod install

The podspec is hosted at TrustPin-Swift.binary and published to the CocoaPods trunk.


πŸ”§ Quick Setup

1. Import and Configure

import TrustPinKit

// Configure TrustPin with your project credentials
try await TrustPin.setup(
    organizationId: "your-org-id",
    projectId: "your-project-id", 
    publicKey: "your-base64-public-key",
    mode: .strict  // Recommended for production
)

πŸ’‘ Find your credentials in the TrustPin Dashboard

⚠️ Important: TrustPin.setup() must be called only once during your app's lifecycle. Concurrent setup calls are not supported and will throw TrustPinErrors.invalidProjectConfig. If already initialized, subsequent calls will return immediately.

2. Choose Your Pinning Mode

TrustPin offers two validation modes:

Strict Mode (Recommended for Production)

try await TrustPin.setup(
    // ... your credentials
    mode: .strict  // Throws error for unregistered domains
)

Permissive Mode (Development & Testing)

try await TrustPin.setup(
    // ... your credentials  
    mode: .permissive  // Allows unregistered domains to bypass pinning
)

3. System-Wide Protection Control

By default, TrustPin automatically enables certificate pinning for all URLSession requests in your app. This provides the broadest security coverage with zero additional configuration.

Automatic Protection (Default & Recommended)

try await TrustPin.setup(
    organizationId: "your-org-id",
    projectId: "your-project-id",
    publicKey: "your-base64-public-key",
    mode: .strict
    // autoRegisterURLProtocol: true (default)
)

// All URLSession instances now automatically use certificate pinning
// Including URLSession.shared and third-party networking libraries

Manual Control (Advanced)

For advanced scenarios where you need to control when system-wide pinning is active:

try await TrustPin.setup(
    organizationId: "your-org-id",
    projectId: "your-project-id",
    publicKey: "your-base64-public-key",
    mode: .strict,
    autoRegisterURLProtocol: false  // Disable automatic system-wide pinning
)

// Manually enable/disable system-wide pinning when needed
TrustPin.registerURLProtocol()    // Enable
TrustPin.unregisterURLProtocol()  // Disable

πŸ’‘ Recommendation: Use the default automatic protection unless you have specific requirements for controlling URLProtocol registration timing.


πŸ›  Integration Approaches

TrustPin offers three different ways to integrate certificate pinning into your application:

Approach Best For Setup Complexity Coverage
System-Wide URLProtocol (Recommended) Most applications, zero-config after setup 🟒 Minimal All URLSession requests
URLSessionDelegate Custom URLSession setups, granular control 🟑 Medium Specific URLSession instances
Helper Methods Explicit control, static method preference 🟠 Per-request Individual requests

When to Choose Each Approach

System-Wide URLProtocol (Default & Recommended)

  • βœ… Broad protection: Automatically secures all URLSession requests
  • βœ… Zero configuration: Works with existing networking code
  • βœ… Third-party compatibility: Protects libraries using URLSession
  • βœ… Maintainability: Single setup call for entire app

URLSessionDelegate

  • βœ… Granular control: Only specific URLSession instances use pinning
  • βœ… Legacy compatibility: Works with older networking patterns
  • βœ… Custom delegation: Integrate with existing URLSessionDelegate code
  • βœ… Selective pinning: Mix pinned and non-pinned sessions in same app

Helper Methods

  • βœ… Explicit requests: Clear intent for which requests use pinning
  • βœ… Static methods: Functional programming style
  • βœ… Migration friendly: Easy drop-in replacements for existing URLSession calls
  • βœ… Testing isolation: Test pinned vs non-pinned requests separately

πŸ›  Usage Examples

System-Wide Certificate Pinning (Recommended)

The simplest approach - TrustPin automatically protects all HTTPS requests across your entire application:

import TrustPinKit

// In your AppDelegate or App struct
func configureApp() async throws {
    // Setup TrustPin - URLProtocol is automatically registered
    try await TrustPin.setup(
        organizationId: "your-org-id",
        projectId: "your-project-id", 
        publicKey: "your-base64-public-key",
        mode: .strict
    )
    
    // That's it! All URLSession requests now use certificate pinning
}

// Anywhere in your app - pinning works automatically
class NetworkManager {
    func fetchData() async throws -> Data {
        // URLSession.shared automatically uses certificate pinning
        let url = URL(string: "https://api.example.com/data")!
        let (data, _) = try await URLSession.shared.data(from: url)
        return data
    }
    
    func fetchWithCustomSession() async throws -> Data {
        // Custom URLSessions also automatically use certificate pinning
        let session = URLSession(configuration: .ephemeral)
        let url = URL(string: "https://api.example.com/secure")!
        let (data, _) = try await session.data(from: url)
        return data
    }
}

// Third-party libraries using URLSession are also protected!
// Alamofire, URLSession-based HTTP clients, etc. automatically get pinning

🎯 Benefits:

  • Zero configuration after setup
  • Protects all URLSession requests system-wide
  • Works with third-party networking libraries
  • Automatically secures URLSession.shared and custom sessions

Manual Control (Advanced)

For advanced scenarios where you need control over URLProtocol registration:

// Setup without auto-registration
try await TrustPin.setup(
    organizationId: "your-org-id",
    projectId: "your-project-id",
    publicKey: "your-base64-public-key",
    mode: .strict,
    autoRegisterURLProtocol: false  // Disable auto-registration
)

// Manually register when needed
TrustPin.registerURLProtocol()

// Unregister when no longer needed
TrustPin.unregisterURLProtocol()

URLProtocol Helper Methods (Alternative API)

For scenarios where you prefer explicit control or want to use static helper methods:

import TrustPinKit

class NetworkManager {
    
    // Async/await examples
    func fetchDataWithHelpers() async throws -> Data {
        let url = URL(string: "https://api.example.com/data")!
        let (data, _) = try await TrustPinURLProtocol.data(from: url)
        return data
    }
    
    func downloadFileWithHelpers() async throws -> URL {
        let request = URLRequest(url: URL(string: "https://api.example.com/file.pdf")!)
        let (fileURL, _) = try await TrustPinURLProtocol.download(for: request)
        return fileURL
    }
    
    // Completion handler examples
    func fetchDataWithCompletionHandler() {
        let url = URL(string: "https://api.example.com/data")!
        let task = TrustPinURLProtocol.dataTask(with: url) { data, response, error in
            if let error = error {
                print("Error: \(error)")
                return
            }
            if let data = data {
                print("Received \(data.count) bytes")
            }
        }
        task.resume()
    }
    
    // Custom session with pinning
    func useCustomTrustPinSession() async throws -> Data {
        let session = URLSession.trustPinSession(
            configuration: .ephemeral
        )
        
        let url = URL(string: "https://api.example.com/data")!
        let (data, _) = try await session.data(from: url)
        return data
    }
}

πŸ’‘ When to use helper methods:

  • When you need explicit control over individual requests
  • For codebases that prefer static method calls
  • When migrating from other networking libraries
  • For testing scenarios where you want to isolate pinned requests

Automatic URLSession Integration

The traditional delegate-based approach (still fully supported):

import TrustPinKit

class NetworkManager {
    private let trustPinDelegate = TrustPinURLSessionDelegate()
    private lazy var session = URLSession(
        configuration: .default,
        delegate: trustPinDelegate,
        delegateQueue: nil
    )
    
    func fetchData() async throws -> Data {
        let url = URL(string: "https://api.example.com/data")!
        let (data, _) = try await session.data(from: url)
        return data
    }
}

Manual Certificate Verification

For custom networking stacks or certificate inspection:

import TrustPinKit

// Verify a PEM-encoded certificate for a specific domain
let domain = "api.example.com"
let pemCertificate = """
-----BEGIN CERTIFICATE-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END CERTIFICATE-----
"""

do {
    try await TrustPin.verify(domain: domain, certificate: pemCertificate)
    print("βœ… Certificate is valid and matches configured pins")
} catch TrustPinErrors.domainNotRegistered {
    print("⚠️ Domain not configured for pinning")
} catch TrustPinErrors.pinsMismatch {
    print("❌ Certificate doesn't match any configured pins")
} catch {
    print("πŸ’₯ Verification failed: \(error)")
}

Setup and Initialization

import TrustPinKit

class AppDelegate: UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        Task {
            do {
                // Setup TrustPin once during app launch
                try await TrustPin.setup(
                    organizationId: "your-org-id",
                    projectId: "your-project-id",
                    publicKey: "your-public-key",
                    mode: .strict
                )
                print("βœ… TrustPin initialized successfully")
            } catch {
                print("❌ TrustPin setup failed: \(error)")
            }
        }
        
        return true
    }
}

πŸ”§ Integration Examples

With Alamofire

import Alamofire
import TrustPinKit

// Create a custom SessionDelegate that extends TrustPinURLSessionDelegate
class TrustPinAlamofireDelegate: TrustPinURLSessionDelegate, SessionDelegate {
    // TrustPinURLSessionDelegate handles the certificate validation
}

let trustPinDelegate = TrustPinAlamofireDelegate()
let session = Session(
    configuration: .default,
    delegate: trustPinDelegate,
    rootQueue: DispatchQueue(label: "com.trustpin.alamofire.queue"),
    startRequestsImmediately: true
)

// Use the session for your requests
let response = try await session.request("https://api.example.com/data")
    .validate()
    .serializingData()
    .value

With Custom URLSession

import Foundation
import TrustPinKit

class SecureNetworkClient {
    private let trustPinDelegate = TrustPinURLSessionDelegate()
    private lazy var urlSession: URLSession = {
        let config = URLSessionConfiguration.default
        config.timeoutIntervalForRequest = 30
        return URLSession(configuration: config, delegate: trustPinDelegate, delegateQueue: nil)
    }()
    
    func performSecureRequest(to url: URL) async throws -> (Data, URLResponse) {
        return try await urlSession.data(from: url)
    }
}

🎯 Pinning Modes Explained

Mode Behavior Use Case
.strict ❌ Throws TrustPinErrors.domainNotRegistered for unregistered domains Production environments where all connections should be validated
.permissive βœ… Allows unregistered domains to bypass pinning Development/Testing or apps connecting to dynamic domains

When to Use Each Mode

Strict Mode (.strict)

  • βœ… Production applications
  • βœ… High-security environments
  • βœ… Known, fixed set of API endpoints
  • βœ… Compliance requirements

Permissive Mode (.permissive)

  • βœ… Development and staging
  • βœ… Applications with dynamic/unknown endpoints
  • βœ… Gradual migration to certificate pinning
  • βœ… Third-party SDK integrations

πŸ“Š Error Handling

TrustPin provides detailed error types for proper handling:

do {
    try await TrustPin.verify(domain: "api.example.com", certificate: pemCert)
} catch TrustPinErrors.domainNotRegistered {
    // Domain not configured in TrustPin (only in strict mode)
    handleUnregisteredDomain()
} catch TrustPinErrors.pinsMismatch {
    // Certificate doesn't match configured pins - possible MITM
    handleSecurityThreat()
} catch TrustPinErrors.allPinsExpired {
    // All pins for domain have expired
    handleExpiredPins()
} catch TrustPinErrors.invalidServerCert {
    // Certificate format is invalid
    handleInvalidCertificate()
} catch TrustPinErrors.invalidProjectConfig {
    // Setup parameters are invalid
    handleConfigurationError()
} catch TrustPinErrors.errorFetchingPinningInfo {
    // Network error fetching configuration
    handleNetworkError()
} catch TrustPinErrors.configurationValidationFailed {
    // configuration signature validation failed
    handleSignatureError()
}

πŸ” Logging and Debugging

TrustPin provides comprehensive logging for debugging and monitoring:

// Set log level before setup
await TrustPin.set(logLevel: .debug)

// Available log levels:
// .none     - No logging
// .error    - Errors only  
// .info     - Errors and informational messages
// .debug    - All messages including debug information

πŸ— Best Practices

Setup and Initialization

  1. Call TrustPin.setup() only once during app launch (typically in AppDelegate)
  2. Handle setup errors gracefully - don't block app launch if TrustPin fails
  3. Set log level before setup for complete logging coverage
  4. Never call setup concurrently - it's not supported and will throw errors
  5. Use Task/async context for setup in synchronous app lifecycle methods

Security Recommendations

  1. Always use .strict mode in production
  2. Rotate pins before expiration
  3. Monitor pin validation failures
  4. Use HTTPS for all pinned domains
  5. Keep public keys secure and version-controlled

Performance Optimization

  1. Cache TrustPin configuration (handled automatically)
  2. Reuse URLSession instances with TrustPin delegate
  3. Use appropriate log levels (.error or .none in production)
  4. Initialize early to avoid setup delays during first network requests

Development Workflow

  1. Start with .permissive mode during development
  2. Test all endpoints with pinning enabled
  3. Validate pin configurations in staging
  4. Switch to .strict mode for production releases
  5. Use debug logging to troubleshoot pinning issues

πŸ”§ Advanced Configuration

Custom URLSession Configuration

let trustPinDelegate = TrustPinURLSessionDelegate()

let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
configuration.timeoutIntervalForResource = 300
configuration.httpMaximumConnectionsPerHost = 4

let session = URLSession(
    configuration: configuration,
    delegate: trustPinDelegate,
    delegateQueue: OperationQueue()
)

Error Recovery Strategies

func performNetworkRequest() async -> Data? {
    do {
        return try await secureNetworkRequest()
    } catch TrustPinErrors.domainNotRegistered {
        // Log security event but continue in permissive mode
        logger.warning("Unregistered domain accessed")
        return try await fallbackNetworkRequest()
    } catch TrustPinErrors.pinsMismatch {
        // This is a serious security issue - do not retry
        logger.critical("Certificate pinning failed - possible MITM attack")
        throw SecurityError.potentialMITMAttack
    }
}

πŸ“š API Reference

Core Classes

  • TrustPin - Main SDK interface for setup and verification
  • TrustPinMode - Enum defining pinning behavior modes (.strict, .permissive)
  • TrustPinURLSessionDelegate - URLSession delegate for automatic validation
  • TrustPinURLProtocol - URLProtocol implementation for system-wide pinning (iOS 13.0+)
  • TrustPinErrors - Error types for detailed error handling
  • TrustPinLogLevel - Logging configuration options (.none, .error, .info, .debug)

Key Methods

Core TrustPin API

// Setup and configuration (standard)
static func setup(organizationId: String, 
                  projectId: String, 
                  publicKey: String, 
                  mode: TrustPinMode = .strict,
                  autoRegisterURLProtocol: Bool = true) async throws

// Setup and configuration with custom CDN
static func setup(organizationId: String, 
                  projectId: String, 
                  publicKey: String, 
                  configurationURL: URL,
                  mode: TrustPinMode = .strict,
                  autoRegisterURLProtocol: Bool = true) async throws

// Manual verification  
static func verify(domain: String, certificate: String) async throws

// System-wide URLProtocol control
static func registerURLProtocol()    // Enable system-wide pinning
static func unregisterURLProtocol()  // Disable system-wide pinning

// Logging configuration
static func set(logLevel: TrustPinLogLevel) async

URLProtocol Helper Methods (iOS 13.0+)

// Async/await data methods with automatic pinning
TrustPinURLProtocol.data(for: URLRequest, using: URLSession? = nil) async throws -> (Data, URLResponse)
TrustPinURLProtocol.data(from: URL, using: URLSession? = nil) async throws -> (Data, URLResponse)

// Async/await download methods with automatic pinning
TrustPinURLProtocol.download(for: URLRequest, using: URLSession? = nil) async throws -> (URL, URLResponse)
TrustPinURLProtocol.download(from: URL, using: URLSession? = nil) async throws -> (URL, URLResponse)

// Completion handler methods with automatic pinning
TrustPinURLProtocol.dataTask(with: URLRequest, using: URLSession? = nil, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
TrustPinURLProtocol.dataTask(with: URL, using: URLSession? = nil, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask

TrustPinURLProtocol.downloadTask(with: URLRequest, using: URLSession? = nil, completionHandler: @escaping @Sendable (URL?, URLResponse?, Error?) -> Void) -> URLSessionDownloadTask
TrustPinURLProtocol.downloadTask(with: URL, using: URLSession? = nil, completionHandler: @escaping @Sendable (URL?, URLResponse?, Error?) -> Void) -> URLSessionDownloadTask

// Create URLSession with pinning enabled
URLSession.trustPinSession(configuration: URLSessionConfiguration = .default, 
                          delegate: URLSessionDelegate? = nil, 
                          delegateQueue: OperationQueue? = nil) -> URLSession

πŸ› Troubleshooting

Common Issues

Setup Fails with invalidProjectConfig

  • βœ… Verify organization ID, project ID, and public key are correct
  • βœ… Check for extra whitespace or newlines in credentials
  • βœ… Ensure public key is properly base64-encoded
  • βœ… Avoid concurrent setup calls - only call TrustPin.setup() once per app lifecycle
  • βœ… Check for multiple setup attempts - if already initialized, subsequent calls return immediately

Certificate Verification Fails

  • βœ… Confirm domain is registered in TrustPin dashboard
  • βœ… Check certificate format (must be PEM-encoded)
  • βœ… Verify pins haven't expired
  • βœ… Test with .permissive mode first

Network Requests Hang

  • βœ… Ensure you're using the correct URLSession delegate
  • βœ… Check for retain cycles with URLSession
  • βœ… Verify network connectivity
  • βœ… Check if URLProtocol is properly registered (when using system-wide pinning)

System-Wide Pinning Not Working

  • βœ… Verify autoRegisterURLProtocol: true was used during setup (default)
  • βœ… Check that you're testing with HTTPS URLs (HTTP is ignored)
  • βœ… Ensure URLProtocol hasn't been unregistered elsewhere in the app
  • βœ… Test with TrustPin.registerURLProtocol() to manually re-register

URLProtocol Helper Methods Not Found

  • βœ… Ensure you're targeting iOS 13.0+ or equivalent platform versions
  • βœ… Check that TrustPin has been set up before using helper methods
  • βœ… Use TrustPinURLProtocol. prefix for static helper methods
  • βœ… Import TrustPinKit module

Debug Steps

  1. Enable debug logging: await TrustPin.set(logLevel: .debug)
  2. Test with permissive mode first
  3. Verify credentials in TrustPin dashboard
  4. Check certificate expiration dates

πŸ“– Documentation


πŸ“ License

This project is licensed under the TrustPin Binary License Agreement - see the LICENSE file for details.

Commercial License: For enterprise licensing or custom agreements, contact [email protected]

Attribution Required: When using this software, you must display "Uses TrustPinβ„’ technology – https://trustpin.cloud" in your application.


🀝 Support & Feedback

We welcome your feedback and questions!


Built with ❀️ by the TrustPin team

About

TrustPin is a modern certificate pinning platform. Implement certificate pinning without the pain of force updates or certificates management

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •