Search code examples
iosswiftalamofireurlrequest

Alamofire fail request because certificate but work on Postman


I am having an issue with one request that I am doing.

Using the Postman this request works with one warning "Self signed certificate in certificate chain"

Here is the curl if you want try out:

curl --location --request GET 'https://app-server.iot.i.tplinknbu.com/v1/server-info' \
--header 'Content-Type: application/json' \
--header 'app-cid: app:TP-Link_Tapo_Android:98-3B-16-96-48-EB' \
--data-raw ''

But using Almofire or the default request system from iOS it fail:

2020-11-22 23:47:50.192188+0000 App [4483:1574605] Task <E6235AC9-2246-4F32-BB17-CB969F244030>.<2> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9802])
2020-11-22 23:47:50.195117+0000 App[4483:1574592] Task <E6235AC9-2246-4F32-BB17-CB969F244030>.<2> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
    "<cert(0x151093800) s: *.tplinknbu.com i: TP-LINK CA P1>",
    "<cert(0x151094000) s: TP-LINK CA P1 i: tp-link-CA>",
    "<cert(0x15106e200) s: tp-link-CA i: tp-link-CA>"
), NSErrorClientCertificateStateKey=0, NSErrorFailingURLKey=https://app-server.iot.i.tplinknbu.com/v1/server-info, NSErrorFailingURLStringKey=https://app-server.iot.i.tplinknbu.com/v1/server-info, NSUnderlyingError=0x282b23e10 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x281760f30>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
    "<cert(0x151093800) s: *.tplinknbu.com i: TP-LINK CA P1>",
    "<cert(0x151094000) s: TP-LINK CA P1 i: tp-link-CA>",
    "<cert(0x15106e200) s: tp-link-CA i: tp-link-CA>"
)}}, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <E6235AC9-2246-4F32-BB17-CB969F244030>.<2>"
), _kCFStreamErrorCodeKey=-9802, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <E6235AC9-2246-4F32-BB17-CB969F244030>.<2>, NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x281760f30>, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made.}

This the code that I am using:

info.plist:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    <key>NSExceptionMinimumTLSVersion</key>
    <string>TLSv1.2</string>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.2</string>
    <key>NSAllowsLocalNetworking</key>
    <true/>
</dict>

Code:

let defaultManager: ServerTrustManager = {
    let serverTrustPolicies: [String: ServerTrustEvaluating] = [
        "tplinknbu.com": DisabledTrustEvaluator(),
        "tplinknbu.com:443": DisabledTrustEvaluator()
    ]

    return Alamofire.ServerTrustManager(evaluators: serverTrustPolicies)
}()

    let pathURL = URL(string: "https://app-server.iot.i.tplinknbu.com/v1/server-info")
    var request = URLRequest(url: pathURL!)
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpMethod = HTTPMethod.get.rawValue
    
    
    let dataRequest = Session(serverTrustManager: defaultManager, eventMonitors: [AlamofireLogger()]).request(request).responseJSON {
        (response) in
        
        
        switch response.result {
        case .failure(let error):
            
            print(error)
        default: break

        }
    }

What I am doing wrong?

P.S. if I use the proxy tool Proxyman and activate the Proxyman certificate they have on the iPhone everything works well on the iOS app


Solution

  • You must match the hosts you're using exactly. That is tplinknbu.com does NOT match app-server.iot.i.tplinknbu.com. You should also investigate the errors you're seeing directly, as they may contain useful information.

    And like @Shivam Gaur said, you shouldn't be creating Session instances inline like that. You need to keep their reference alive in something like a singleton so that requests can complete.