24

I want to use Alamofire to communicate with my server over a https connection with a self signed certificate. My environment runs on localhost. I've tried to connect, but the response all the time looks like this:

Success: false
Response String: nil

I've done it with the following code:

import Foundation
import UIKit
import Alamofire

class MessageView: UITableViewController {

    let defaultManager: Alamofire.Manager = {
        let serverTrustPolicies: [String: ServerTrustPolicy] = [
            "localhost": .DisableEvaluation
        ]

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

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

    override func viewDidLoad() {
        super.viewDidLoad()

        defaultManager
            .request(.GET, "https://localhost:3443/message")
            .responseJSON { _, _, result in
                print("Success: \(result.isSuccess)")
                print("Response String: \(result.value)")
            }
    }

}

I've created the server side certificates with this line of bash:

openssl req -x509 -nodes -days 999 -newkey rsa:2048 -keyout server.key -out server.crt

I don't know what am I doing wrong. Help would be great.

### Update ###

Here is the cURL request. In my opinion, there is no problem, or am I wrong?

curl -X GET https://localhost:3443/message -k -v

*   Trying ::1...
* Connected to localhost (::1) port 3443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
* Server certificate: teawithfruit
> GET /message HTTP/1.1
> Host: localhost:3443
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Content-Length: 1073
< Date: Tue, 15 Sep 2015 06:20:45 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact

[{"_id":"55f3ed2d81a334558241e2f4","email":"abc@def.com","password":"abc","name":"teawithfruit","language":"en","__v":0,"timestamp":1442049325159,"messages":[{"_id":"55f40553e568236589772c61","user":"55f3ed2d81a334558241e2f4","language":"en","message":"hello world","__v":0,"timestamp":1442055507301,"id":"55f40553e568236589772c61"},{"_id":"55f48b2b02e7b059b54e99f6","user":"55f3ed2d81a334558241e2f4","language":"en","message":"hello world","__v":0,"timestamp":1442089771312,"id":"55f48b2b02e7b059b54e99f6"}],"id":"55f3ed2d81a334558241e2f4"}]

### Update 2 ###

Sorry for the late answer. Here are the two debugPrints:

Request debugPrint:

$ curl -i \
  -H "Accept-Language: en-US;q=1.0" \
  -H "Accept-Encoding: gzip;q=1.0,compress;q=0.5" \
  -H "User-Agent: Message/com.teawithfruit.Message (1; OS Version 9.0 (Build 13A340))" \
  "https://localhost:3443/message"

Result debugPrint:

FAILURE: Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLKey=https://localhost:3443/message, NSLocalizedDescription=cancelled, NSErrorFailingURLStringKey=https://localhost:3443/message}

### Update 3 ###

Here is the complete error with maybe an ATS problem?

nil
$ curl -i \
  -H "Accept-Language: en-US;q=1.0" \
  -H "Accept-Encoding: gzip;q=1.0,compress;q=0.5" \
  -H "User-Agent: Message/com.teawithfruit.Message (1; OS Version 9.0 (Build 13A340))" \
  "https://localhost:3443/message"
2015-10-17 15:10:48.346 Message[25531:1001269] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
FAILURE: Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x7fdc3044b740>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=<CFArray 0x7fdc2a7ca300 [0x10f7037b0]>{type = immutable, count = 1, values = (
  0 : <cert(0x7fdc31d31670) s: teawithfruit i: teawithfruit>
)}, NSUnderlyingError=0x7fdc30064bd0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x7fdc3044b740>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=<CFArray 0x7fdc2a7ca300 [0x10f7037b0]>{type = immutable, count = 1, values = (
  0 : <cert(0x7fdc31d31670) s: teawithfruit i: teawithfruit>
)}}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://localhost:3443/message, NSErrorFailingURLStringKey=https://localhost:3443/message, NSErrorClientCertificateStateKey=0} 
Success: false
Response String: nil
teawithfruit
  • 767
  • 1
  • 11
  • 22

4 Answers4

16

You need to add the port domain when you create your ServerTrustPolicy dictionary.

let defaultManager: Alamofire.Manager = {
    let serverTrustPolicies: [String: ServerTrustPolicy] = [
        "localhost:3443": .DisableEvaluation
    ]

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

    return Alamofire.Manager(
        configuration: configuration,
        serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
    )
}()
cnoon
  • 16,575
  • 7
  • 58
  • 66
  • 2
    Thank you for the answer. You're right. I forgot to add the port. But if I add the port, I get the following error: `Message[4401:95209] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)` – teawithfruit Sep 20 '15 at 16:54
  • Do you maybe have another idea? – teawithfruit Oct 05 '15 at 10:33
  • Now it appears you are running into ATS. More info [here](https://forums.developer.apple.com/thread/13472). Could you post the full details of the new error in an update in the question? – cnoon Oct 05 '15 at 14:58
  • Hey, I've updated the question. It looks like an SSL error. Maybe you know something more? Thanks for your help. – teawithfruit Oct 17 '15 at 13:20
  • If I use this solution at AppDelegate, the variable defaultManager will be available to my all code? Is it possible configure de default SessionManager and still continue using the Alamofire.request instead of defaultManager.request? – Luciano Borges Sep 14 '17 at 17:37
  • Hey @teawithfruit did you find a solution for that SSL error ? – Sankar Dec 30 '17 at 12:56
  • @teawithfruit Did you find any solution? If so, pleas post it – M. Wojcik Jan 18 '18 at 08:10
  • You can remove configuration from the initializer and it will work fine, because it has a default value – Lachezar Todorov Mar 14 '18 at 14:28
  • I am getting this error with alamofire 5 Type 'ServerTrustManager' has no member 'DisableEvaluation' – Abhishek Aug 17 '18 at 08:27
8

For swift 4:

private static var Manager : Alamofire.SessionManager = {
    // Create the server trust policies
    let serverTrustPolicies: [String: ServerTrustPolicy] = [
        "your domain goes here": .disableEvaluation
    ]
    // Create custom manager
    let configuration = URLSessionConfiguration.default
    configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
    let man = Alamofire.SessionManager(
        configuration: URLSessionConfiguration.default,
        serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
    )
    return man
}()

Then you call it like this:

Manager.upload(body.data(using: .utf8)!, to: url, method: .post, headers: headers)

Credits to Cnoon

William Kinaan
  • 28,059
  • 20
  • 85
  • 118
3

My approach for self-signed https. The ServerTrustPolicyManager is an open class, and it's serverTrustPolicy function is open too. So it can be override.

In my case, the server list will grow in future. If I hard-code the https list, I will need to maintain the list when adding new https server. So, I decide to override the ServerTrustPolicyManager class in order to meet my needs.

// 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
    }
}

Then,

    let trustPolicies = MyServerTrustPolicyManager(policies: [:])
    let manager = Alamofire.SessionManager(configuration: sessionConfig, delegate: SessionDelegate(), serverTrustPolicyManager: trustPolicies)
AechoLiu
  • 17,522
  • 9
  • 100
  • 118
3

So I know some time has passed, but I had exactly the same problem. And I found a solution with above answers. I had to add 2 things to trustPolicies:

let defaultManager: Alamofire.Manager = {
    let serverTrustPolicies: [String: ServerTrustPolicy] = [
         // Here host with port (trustPolicy is my var where I pin my certificates)
        "localhost:3443": trustPolicy
         //Here without port
         "localhost": .disableEvaluation
    ]

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

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

Also in Info.plist had to add:

<key>AppTransportSecurity</key>
<dict>
  <key>AllowsArbitraryLoads</key>
      <true/>
</dict>
M. Wojcik
  • 2,301
  • 3
  • 23
  • 31