TrustKit Documentation

repository·master·Indexed 24 days ago

https://github.com/datatheorem/trustkit

An open source framework for deploying SSL public key pinning and reporting in iOS, macOS, tvOS, and watchOS applications. TrustKit allows developers to enforce certificate pinning based on Subject Public Key Info (SPKI) using either programmatic initialization in Swift and Objective-C or static configuration via Info.plist. It provides the TrustKit and TSKPinningValidator classes for policy configuration and manual validation, and supports auto-swizzling for NSURLSession and NSURLConnection delegates.

Tokens
3.4K
Snippets
7
Records
11
Agent score
80%

What's inside TrustKit

  1. Use auto-swizzling for simple Apps

    master

    For simple applications, you can deploy TrustKit without modifying source code by using auto-swizzling.

    1. Add configuration keys to your app's Info.plist under a TSKConfiguration dictionary.
    2. Set the kTSKSwizzleNetworkDelegates key to YES.

    TrustKit will then automatically perform method swizzling on NSURLSession and NSURLConnection delegates to validate connections.

    Limitations: Do not use auto-swizzling if:

    • Your app has complex connection delegates (e.g., for client certificate authentication).
    • Another module/library is already swizzling connection delegates.
    • Your app does not use NSURLSession or NSURLConnection.
  2. Core classes in TrustKit

    master

    TrustKit provides two primary classes to implement SSL public key pinning in iOS, macOS, tvOS, or watchOS applications:

    • TrustKit: Used to configure the SSL pinning policy and initialize the framework.
    • TSKPinningValidator: Used to validate a server's certificate chain against an established SSL pinning policy.
  3. Configure TrustKit pinning policy via Info.plist

    master
    You can configure the TrustKit SSL pinning policy without modifying your application's source code by adding the configuration to your Info.plist file. This is useful for static configurations that do not require runtime logic.
  4. Add TrustKit as a dependency

    master

    You can integrate TrustKit into your Xcode project using CocoaPods, Carthage, or Swift Package Manager.

    CocoaPods

    Add to your Podfile:

    pod 'TrustKit'

    Then run pod install.

    Carthage

    Add to your Cartfile:

    github "datatheorem/TrustKit"

    Then follow standard Carthage installation steps.

    Swift Package Manager

    Use the following Git URL:

    git@github.com:datatheorem/TrustKit.git
  5. Generate SSL pins from certificates

    master

    TrustKit uses base64-encoded SHA-256 hashes of a certificate's Subject Public Key Info (SPKI) as SSL pins. You can use the Python helper script provided in the repository to generate these values from PEM or DER certificates.

    Warning: Incorrectly choosing pins can cause your app to lose connectivity to its servers if certificates are rotated. Always include at least one backup pin.

    $ python get_pin_from_certificate.py ca.pem
    $ python get_pin_from_certificate.py --type DER ca.der
  6. Configure a TrustKit pinning policy

    master

    A pinning policy is a dictionary mapping domain names to configuration settings. You initialize TrustKit by calling initSharedInstanceWithConfiguration: with this dictionary.

    Key configuration options include:

    • kTSKPinnedDomains: The main dictionary containing domain configurations.
    • kTSKIncludeSubdomains: Set to @YES to include subdomains of the specified domain.
    • kTSKEnforcePinning: Set to @YES to block connections if validation fails. Recommendation: Start with @NO to monitor failures without breaking the app.
    • kTSKReportUris: An array of URLs where TrustKit will send reports for pin validation failures.
    • kTSKPublicKeyHashes: An array of the base64-encoded SHA-256 hashes of the pinned public keys.
    #import <TrustKit/TrustKit.h>
    
    NSDictionary *trustKitConfig = 
    @{
      kTSKPinnedDomains: @{
        @"yahoo.com" : @{
          kTSKIncludeSubdomains: @YES,
          kTSKEnforcePinning: @NO,
          kTSKReportUris: @[@"https://some-reporting-server.com/log_report"],
          kTSKPublicKeyHashes: @[
            @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=",
            @"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=",
          ],
        },
      },
    };
    [TrustKit initSharedInstanceWithConfiguration:trustKitConfig];
  7. Debug network connections with TrustKit

    master

    SSL pinning in TrustKit will reject SSL connections from tools like Charles Proxy because they use self-signed certificates to perform man-in-the-middle decryption. To allow debugging while using TrustKit, you must ensure the certificate used by the proxy is trusted by the pinning policy.

    For small development teams

    Create a custom self-signed SSL certificate authority (CA) for Charles to use.

    1. Generate a team CA certificate and private key.
    2. Share this CA with teammates and configure Charles to use it as the custom root CA.
    3. Install and trust this root CA certificate on every iOS Simulator and physical device used for development.

    Note: For production debugging, the user must manually trust this self-signed root CA on the device, which requires entering the device passcode.

    For large or enterprise development teams

    Use intermediate CA certificates to avoid sharing a single root CA private key.

    1. Maintain a self-signed root CA in a production-secure environment.
    2. Use the root CA to issue intermediate CA certificates for tools like Charles.
    3. Pin the self-signed root CA in TrustKit. Since TrustKit pins the root, it will trust the intermediate certificates issued by that root.
    4. Best Practice: Give intermediate CAs a short validity window and include a personal identifier (like an employee email) in the intermediate CA's email field. This allows you to trace potential security bypasses back to a specific user.
  8. Initialize TrustKit programmatically in Objective-C

    master

    To initialize TrustKit at runtime, use [TrustKit initSharedInstanceWithConfiguration:]. You must provide a configuration dictionary containing kTSKPinnedDomains and other policy settings.

    Key configuration keys include:

    • kTSKSwizzleNetworkDelegates: A boolean indicating whether to automatically swizzle NSURLConnection and NSURLSession delegates to enforce pinning.
    • kTSKPinnedDomains: A dictionary mapping domain names to their specific pinning settings.
    • kTSKPublicKeyHashes: An array of Subject Public Key Info (SPKI) hashes for the domain.
    • kTSKExpirationDate: A string representing the date when the pins expire.
    • kTSKEnforcePinning: A boolean to determine if pinning failures should block the connection.
    • kTSKIncludeSubdomains: A boolean to apply the pins to subdomains.
        NSDictionary *trustKitConfig =
      @{
        kTSKSwizzleNetworkDelegates: @NO,
        kTSKPinnedDomains : @{
                @"www.datatheorem.com" : @{
                        kTSKExpirationDate: @"2017-12-01",
                        kTSKPublicKeyHashes : @[
                                @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=",
                                @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8="
                                ],
                        kTSKEnforcePinning : @NO,
                        },
                @"yahoo.com" : @{
                        kTSKPublicKeyHashes : @[
                                @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=",
                                @"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=",
                                ],
                        kTSKIncludeSubdomains : @YES
                        }
                }];
        
        [TrustKit initSharedInstanceWithConfiguration:trustKitConfig];
  9. Initialize TrustKit programmatically in Swift

    master

    In Swift applications, initialize TrustKit using TrustKit.initSharedInstance(withConfiguration:). The configuration is passed as a [String: Any] dictionary using the same keys as the Objective-C implementation.

            let trustKitConfig = [
                kTSKSwizzleNetworkDelegates: false,
                kTSKPinnedDomains: [
                    "yahoo.com": [
                        kTSKExpirationDate: "2017-12-01",
                        kTSKPublicKeyHashes: [
                            "JbQbUG5JMJUoI6brnx0x3vZF6jilxsapbXGVfjhN8Fg=",
                            "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="
                        ],]
                ]
            ] as [String : Any]
            
            TrustKit.initSharedInstance(withConfiguration: trustKitConfig)
  10. Perform manual SSL pinning validation with TSKPinningValidator

    master

    If you are not using auto-swizzling, you can manually perform SSL pinning validation within your network delegate methods (such as NSURLSessionDelegate).

    1. Retrieve the TSKPinningValidator instance from the TrustKit singleton.
    2. Call handleChallenge:completionHandler: with the current NSURLAuthenticationChallenge.
    3. If handleChallenge:completionHandler: returns false, TrustKit did not handle the challenge (e.g., the domain is not pinned), and you should fall back to default handling.
    - (void)URLSession:(NSURLSession *)session 
                  task:(NSURLSessionTask *)task 
    didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 
     completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
    {
        TSKPinningValidator *pinningValidator = [[TrustKit sharedInstance] pinningValidator];
        // Pass the authentication challenge to the validator; if the validation fails, the connection is blocked
        if (![pinningValidator handleChallenge:challenge completionHandler:completionHandler])
        {
            // TrustKit did not handle this challenge: perhaps it was not for server trust
            // or the domain was not pinned. Fall back to the default behavior
            completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
        }
    }
  11. Implement manual pinning validation

    master

    To perform SSL pinning validation in your app's network delegates (e.g., NSURLSessionDelegate), retrieve a TSKPinningValidator instance from the TrustKit singleton and use its handleChallenge:completionHandler: method.

    If the validator returns NO, it means the challenge was not handled by TrustKit (e.g., the domain is not pinned), and you should fall back to default handling.

    - (void)URLSession:(NSURLSession *)session 
                  task:(NSURLSessionTask *)task 
     didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 
     completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
    {
        TSKPinningValidator *pinningValidator = [[TrustKit sharedInstance] pinningValidator];
        // Pass the authentication challenge to the validator; if the validation fails, the connection will be blocked
        if (![pinningValidator handleChallenge:challenge completionHandler:completionHandler])
        {
            // TrustKit did not handle this challenge: perhaps it was not for server trust
            // or the domain was not pinned. Fall back to the default behavior
            completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
        }
    }