Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

add public key pinning in alamofire manger class swift

here is my alamofire manager, how I can add public key pinning on it ? please help me, I couldn't know the way to do it in my code, if possible I need explanation step by step on how do that with AFManager that has all the requests

class AFManager : NSObject{


///without headers (post)
//used this to registration
class func requestPOSTURL(_ strURL : String, params : [String : 
AnyObject]?, success:@escaping (JSON) -> Void, failure:@escaping (Error) -> Void){
URLCache.shared.removeAllCachedResponses()
Alamofire.request(strURL, method: .post, parameters: params, encoding: URLEncoding.httpBody).responseJSON { (responseObject) -> Void in

    //print(responseObject)

    if responseObject.result.isSuccess {
        let resJson = JSON(responseObject.result.value!)
        success(resJson)
    }
    if responseObject.result.isFailure {
        let error : Error = responseObject.result.error!
        failure(error)
    }
}
}


///// response string (post)
//used this in login // used in change password
class func strRequestPOSTURL(_ strURL : String, params : [String : String]?, headers : [String : String]?, success:@escaping (JSON) -> Void, failure:@escaping (Error) -> Void){
URLCache.shared.removeAllCachedResponses()
Alamofire.request(strURL, method: .post, parameters: params, encoding: URLEncoding.httpBody, headers: headers).responseJSON { (response) in
    //print(response)

    if response.result.isSuccess {
        let resJson = JSON(response.result.value!)
        success(resJson)
    }
    if response.result.isFailure {
        let error : Error = response.result.error!

        failure(error)
    }

}

  }

}

I saw this sample but didn't know how to do it and where I should put the code see the link below : https://infinum.co/the-capsized-eight/ssl-pinning-revisited

like image 719
Rooh Al-mahaba Avatar asked Dec 13 '22 12:12

Rooh Al-mahaba


2 Answers

Security

Using a secure HTTPS connection when communicating with servers and web services is an important step in securing sensitive data. By default, Alamofire will evaluate the certificate chain provided by the server using Apple's built in validation provided by the Security framework. While this guarantees the certificate chain is valid, it does not prevent man-in-the-middle (MITM) attacks or other potential vulnerabilities. In order to mitigate MITM attacks, applications dealing with sensitive customer data or financial information should use certificate or public key pinning provided by the ServerTrustPolicy.

ServerTrustPolicy

The ServerTrustPolicy enumeration evaluates the server trust generally provided by an URLAuthenticationChallenge when connecting to a server over a secure HTTPS connection.

let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
    certificates: ServerTrustPolicy.certificates(),
    validateCertificateChain: true,
    validateHost: true
)

There are many different cases of server trust evaluation giving you complete control over the validation process:

  • performDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to validate the host provided by the challenge.
  • pinCertificates: Uses the pinned certificates to validate the server trust. The server trust is considered valid if one of the pinned certificates match one of the server certificates.
  • pinPublicKeys: Uses the pinned public keys to validate the server trust. The server trust is considered valid if one of the pinned public keys match one of the server certificate public keys.
  • disableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid.
  • customEvaluation: Uses the associated closure to evaluate the validity of the server trust thus giving you complete control over the validation process. Use with caution.

Server Trust Policy Manager

The ServerTrustPolicyManager is responsible for storing an internal mapping of server trust policies to a particular host. This allows Alamofire to evaluate each host against a different server trust policy.

let serverTrustPolicies: [String: ServerTrustPolicy] = [
    "test.example.com": .pinCertificates(
        certificates: ServerTrustPolicy.certificates(),
        validateCertificateChain: true,
        validateHost: true
    ),
    "insecure.expired-apis.com": .disableEvaluation
]

let sessionManager = SessionManager(
    serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)

Make sure to keep a reference to the new SessionManager instance, otherwise your requests will all get cancelled when your sessionManager is deallocated. These server trust policies will result in the following behavior:

test.example.com will always use certificate pinning with certificate chain and host validation enabled thus requiring the following criteria to be met to allow the TLS handshake to succeed: Certificate chain MUST be valid. Certificate chain MUST include one of the pinned certificates. Challenge host MUST match the host in the certificate chain's leaf certificate. insecure.expired-apis.com will never evaluate the certificate chain and will always allow the TLS handshake to succeed. All other hosts will use the default evaluation provided by Apple. Subclassing Server Trust Policy Manager

If you find yourself needing more flexible server trust policy matching behavior (i.e. wildcarded domains), then subclass the ServerTrustPolicyManager and override the serverTrustPolicyForHost method with your own custom implementation.

class CustomServerTrustPolicyManager: ServerTrustPolicyManager {
    override func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
        var policy: ServerTrustPolicy?

        // Implement your custom domain matching behavior...

        return policy
    }
}

Validating the Host

The .performDefaultEvaluation, .pinCertificates and .pinPublicKeys server trust policies all take a validateHost parameter. Setting the value to true will cause the server trust evaluation to verify that hostname in the certificate matches the hostname of the challenge. If they do not match, evaluation will fail. A validateHost value of false will still evaluate the full certificate chain, but will not validate the hostname of the leaf certificate.

It is recommended that validateHost always be set to true in production environments. Validating the Certificate Chain

Pinning certificates and public keys both have the option of validating the certificate chain using the validateCertificateChain parameter. By setting this value to true, the full certificate chain will be evaluated in addition to performing a byte equality check against the pinned certificates or public keys. A value of false will skip the certificate chain validation, but will still perform the byte equality check.

There are several cases where it may make sense to disable certificate chain validation. The most common use cases for disabling validation are self-signed and expired certificates. The evaluation would always fail in both of these cases, but the byte equality check will still ensure you are receiving the certificate you expect from the server.

It is recommended that validateCertificateChain always be set to true in production environments. App Transport Security

With the addition of App Transport Security (ATS) in iOS 9, it is possible that using a custom ServerTrustPolicyManager with several ServerTrustPolicy objects will have no effect. If you continuously see CFNetwork SSLHandshake failed (-9806) errors, you have probably run into this problem. Apple's ATS system overrides the entire challenge system unless you configure the ATS settings in your app's plist to disable enough of it to allow your app to evaluate the server trust.

If you run into this problem (high probability with self-signed certificates), you can work around this issue by adding the following to your Info.plist.

<dict>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>example.com</key>
            <dict>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSExceptionRequiresForwardSecrecy</key>
                <false/>
                <key>NSIncludesSubdomains</key>
                <true/>
                <!-- Optional: Specify minimum TLS version -->
                <key>NSTemporaryExceptionMinimumTLSVersion</key>
                <string>TLSv1.2</string>
            </dict>
        </dict>
    </dict>
</dict>

Whether you need to set the NSExceptionRequiresForwardSecrecy to NO depends on whether your TLS connection is using an allowed cipher suite. In certain cases, it will need to be set to NO. The NSExceptionAllowsInsecureHTTPLoads MUST be set to YES in order to allow the SessionDelegate to receive challenge callbacks. Once the challenge callbacks are being called, the ServerTrustPolicyManager will take over the server trust evaluation. You may also need to specify the NSTemporaryExceptionMinimumTLSVersion if you're trying to connect to a host that only supports TLS versions less than 1.2.

It is recommended to always use valid certificates in production environments. Using Self-Signed Certificates with Local Networking

If you are attempting to connect to a server running on your localhost, and you are using self-signed certificates, you will need to add the following to your Info.plist.

<dict>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsLocalNetworking</key>
        <true/>
    </dict>
</dict>

According to Apple documentation, setting NSAllowsLocalNetworking to YES allows loading of local resources without disabling ATS for the rest of your app.

Reference:- https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md

For implementation details refer the tests. https://github.com/Alamofire/Alamofire/blob/master/Tests/TLSEvaluationTests.swift#L290-L450

like image 80
arango_86 Avatar answered Dec 31 '22 12:12

arango_86


SSL pinning using TrustKit with Alamofire. Here I have included API Manager class. This will help you solve using Alamofire with TrustKit.

class ApiManager: SessionDelegate{

  var sessionManager: SessionManager? 

  override init(){
        super.init()
        initReachibility()
        sessionManager = SessionManager.init(configuration: URLSessionConfiguration.ephemeral, delegate: self)
    }

  override func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        // Call into TrustKit here to do pinning validation
        if TrustKit.sharedInstance().pinningValidator.handle(challenge, completionHandler: completionHandler) == false {
            // 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(.cancelAuthenticationChallenge, nil)
        }
    }

  func makeRequestAlamofire(route:URL, method:HTTPMethod, autherized:Bool, parameter:Parameters,header:[String:String], callback: @escaping (APIResult<Data>) -> Void){

        sessionManager?.request(route,method: method,parameters:parameter, encoding: JSONEncoding.default,headers:headers ).validate(statusCode: 200..<300)
            .validate(contentType: ["application/json"]).responseData { response in
                //Pin Validtion returner
                guard response.error == nil else {
                    // Display Error Alert
                    print("Result Pinning validation failed for \(route.absoluteString)\n\n\(response.error.debugDescription)")
                    return
                }
                switch response.result {
                  case .success(let val):
                    print("Success")
                  case .failure(let error):
                    print("Faild")
                }
        }
    }
}

For the full tutorial refer this link.

like image 32
Pranavan SP Avatar answered Dec 31 '22 14:12

Pranavan SP