20
votes

I get the below error while connecting to self signed server.

Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “maskeddomain.com” which could put your confidential information at risk." UserInfo=0x7fb6dec259e0 {NSURLErrorFailingURLPeerTrustErrorKey=, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorCodeKey=-9813, NSUnderlyingError=0x7fb6dbe0dd90 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1202.)"

Looks like Alamofire 1.3 (https://github.com/Alamofire/Alamofire#security) allows disabling this validation. Has anyone implemented this? I'm using Alamofire API's on my swift project, not sure where exactly "Server Trust Policy Manager" needs to be implemented. Please advice.

5
could you post some code, so I could take a look?Dennis Weidmann

5 Answers

22
votes

There is a way to change the Server Trust Policy of the Alamofire manager shared instance, but it's not recommended. Instead you should create your own customised instance of the manager. Here is the recommended solution, code is Swift 2.0 with Alamofire from swift-2.0 branch, compiled in Xcode7 beta 5.

Creating customised instance of the manager

Because you will not use the request method on the Alamofire, but use the one on your custom manager instead, you need to think of where to store the manager. What I do is to store it as static in my networking wrapper (the class that utilizes Alamofire and deals with my application networking needs). I set it up like this:

private static var Manager : Alamofire.Manager = {
        // Create the server trust policies
        let serverTrustPolicies: [String: ServerTrustPolicy] = [
            "maskeddomain.com": .DisableEvaluation
        ]
        // Create custom manager
        let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
        configuration.HTTPAdditionalHeaders = Alamofire.Manager.defaultHTTPHeaders
        let man = Alamofire.Manager(
            configuration: NSURLSessionConfiguration.defaultSessionConfiguration(),
            serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
        )
        return man
    }()

Next step is to switch all your calls that use Alamofire.request() with Manager.request(), so you should have something like this:

Manager.request(.GET, "http://stackoverflow.com").responseJSON(
    completionHandler: { (_, respose, result) -> Void in
            if result.isSuccess {
                // enjoy your success
            } else if result.isFailure {
                // deal with your failure
            }
    })

If you want to change the shared instance of the manager anyway, go here for more info.

40
votes

Manager configuration for Swift 3 or Swift 4 and Alamofire 4:

private static var manager: Alamofire.SessionManager = {

    // Create the server trust policies
    let serverTrustPolicies: [String: ServerTrustPolicy] = [
        "test.example.com": .disableEvaluation
    ]

    // Create custom manager
    let configuration = URLSessionConfiguration.default
    configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
    let manager = Alamofire.SessionManager(
        configuration: URLSessionConfiguration.default,
        serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
    )

    return manager
}()
12
votes

An example is posted right in the README demonstrating exactly how to disable evaluation if you need to do so.

Since you are going to need to create your own Manager instance as well, you'll want to do something like the following:

class NetworkManager {
    static let sharedInstance = NetworkManager()

    let defaultManager: Alamofire.Manager = {
        let serverTrustPolicies: [String: ServerTrustPolicy] = [
            "test.example.com": .PinCertificates(
                certificates: ServerTrustPolicy.certificatesInBundle(),
                validateCertificateChain: true,
                validateHost: true
            ),
            "insecure.expired-apis.com": .DisableEvaluation
        ]

        let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
        configuration.HTTPAdditionalHeaders = Alamofire.Manager.defaultHTTPHeaders

        return Alamofire.Manager(
            configuration: configuration,
            serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
        )
    }()
}

This will then allow you to make requests with the NetworkManager.sharedInstance.defaultManager object.

6
votes

Another approach for my project. The ServerTrustPolicyManager is an open class, and it's serverTrustPolicy function is open too. So it can be override.

// For Swift 3 and Alamofire 4.0
open class MyServerTrustPolicyManager: ServerTrustPolicyManager {

    // Override this function in order to trust any self-signed https
    open override func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
        return ServerTrustPolicy.disableEvaluation

        // or, if `host` contains substring, return `disableEvaluation`
        // Ex: host contains `my_company.com`, then trust it.
    }
}

Then,

    let trustPolicies = MyServerTrustPolicyManager(policies: [:])
    let manager = Alamofire.SessionManager(configuration: sessionConfig, delegate: SessionDelegate(), serverTrustPolicyManager: trustPolicies)

UPDATE @2018,01

In order to trigger the ServerTrustPolicyManager, the project's Info.plist needs to be configured. I found the solution, detail at this post, cnoon's comment @ 1 Nov 2015.

enter image description here

For example, if there are urls named site1.foo.com, site2.foo.com, .... Then add App Transport Security Settings -> Exception Domains -> foo.com dictionary, with following entries.

  • NSExceptionRequiresForwardSecrecy : NO
  • NSExceptionAllowsInsecureHTTPLoads : YES
  • NSIncludesSubdomains : YES

More detail you can refer the post.

1
votes

Anyway, the answer of @cnoon almost full. But I met another trouble of the ssl validation, so I want to put my code here if someone can get help from it.The manager init as:

private var manager: Manager?
// Import the certificates like xxx.cer to your project(anywhere will be fine), then the ServerTrustPolicy.certificatesInBundle() can find them
let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
            certificates: ServerTrustPolicy.certificatesInBundle(),
            validateCertificateChain: false,
            validateHost: true
        )
let serverTrustPolicies: [String : ServerTrustPolicy] = [
            "sub.server.com": .DisableEvaluation, // because the certificates only add the main domain, so disable evaluation for subdomain
            "192.168.0.2:8090": .DisableEvaluation, // the IP address for request data
            "www.server.com": serverTrustPolicy
        ]
manager = Manager(serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))

Then use the manager to request:

// this function in the class which for manager the Alamofire request
public func request(method: Alamofire.Method, _ URLString: URLStringConvertible,
                        parameters: [String : AnyObject]?) -> Alamofire.Request
    {
        // we do not need use Alamofire.request now, just use the manager you have initialized
        return manager!.request(method, URLString, parameters: parameters,
                                 headers: ["tokenId": UserManager_Inst.tokenID])
    }

p.s.: it is the swift 2.3 sample