Невозможно общаться по HTTPS в приложении iOS с включенной ATS и собственным корневым центром сертификации. - PullRequest
0 голосов
/ 29 ноября 2018

Мы пытаемся создать приложение, которое будет взаимодействовать с несколькими серверами по протоколу HTTPS с использованием NSURLSession, поскольку мы используем наш собственный корневой центр сертификации, который входит в наше приложение, и ATS полностью включен (для NSAllowsArbitraryLoads установлено значение False).

При вызове делегата аутентификации NSURLSession, т.е. «URLAuthenticationChallenge», мы устанавливаем сертификат привязки через «SecTrustSetAnchorCertificates», который проверяет сертификат путем проверки его подписи плюс подписи сертификатов в его цепочке сертификатов, вплоть до сертификата привязки.Также используется SecTrustSetAnchorCertificatesOnly для исключения других якорей.

После успешного выполнения SecTrustEvaluate получена ошибка в завершении SessionDataTask дополнения, которая упоминается ниже:

[4704:1445043] ATS failed system trust
[4704:1445043] System Trust failed for [1:0x1c41678c0]
[4704:1445043] TIC SSL Trust Error [1:0x1c41678c0]: 3:0
[4704:1445043] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
(Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." 

Примечание:

Когда Root CA Certificate установлен и доверен на устройстве, тогда связь HTTPS с включенной ATS работает без каких-либо ошибок. Но я не хочу, чтобы пользователь вручную устанавливал и доверял Root CA на устройстве.

Фрагмент кода:

func getRequest(){  
  let requestUrl = “https://server.test.com/hi.php” //url modified
        let configuration = URLSessionConfiguration.default  
        var request = try! URLRequest(url: requestUrl, method: .get)  

        let session = URLSession(configuration: configuration,  
                                 delegate: self,  
                                 delegateQueue: OperationQueue.main)  
        let task = session.dataTask(with: request, completionHandler: { (data, response, error) in  
            print("Data = \(data)")  
            print("Response = \(response)")  
            print("Error = \(error)")  
        })  
          task.resume()  
    }

func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping(URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void){

if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {

    let trust = challenge.protectionSpace.serverTrust
    let rootCa = “root"
    if let rootCaPath = NSBundle.mainBundle().pathForResource(rootCa, ofType: "der") {
      if let rootCaData = NSData(contentsOfFile: rootCaPath) {
        let rootCert = SecCertificateCreateWithData(nil, rootCaData).takeRetainedValue()
        SecTrustSetAnchorCertificates(trust, [rootCert])
        SecTrustSetAnchorCertificatesOnly(trust, true) 
    }

    var trustResult: SecTrustResultType = 0
    SecTrustEvaluate(trust, &trustResult)
    if (Int(trustResult) == kSecTrustResultUnspecified ||
      Int(trustResult) == kSecTrustResultProceed) {
        // Trust certificate.
    } else {
      NSLog("Invalid server certificate.")
    }  
  } else {
    challenge.sender.cancelAuthenticationChallenge(challenge)
  }
}

1 Ответ

0 голосов
/ 02 декабря 2018

Я думаю, что настоящий криптографический код выглядит правильно, по крайней мере, на первый взгляд, но сторона NSURLSession не делает правильные вещи.

Несколько проблем:

  • Всегда вызывать что-либо на challenge.sender через NSURLSession.Вот как вы делаете это для NSURLConnection.Вместо этого вы должны указать поведение, вызвав предоставленный метод обратного вызова с соответствующим расположением (и, в некоторых случаях, с учетными данными).
  • Отмена вызовов, которые вам не нужны, является ошибкой.явно обрабатывать.Если вы отменяете запрос, вы говорите, что лучше не устанавливать соединение, а продолжать.Как правило, вместо этого следует использовать обработку по умолчанию, чтобы другие типы проблем не вызывали сбоев соединения при их возникновении.Таким образом, в случае else в конце вы должны сделать что-то вроде:

    completionHandler(URLSession.AuthChallengeDisposition.performDefaultHandling, nil)
    

    Если вы этого не сделаете, вы сломаете такие вещи, как аутентификация через прокси-сервер, и это, вероятно, сломает другиеситуации также.

  • Вы должны отменить запрос, когда получите недействительный сертификат.Где у вас есть:

    NSLog("Invalid server certificate.")
    

    добавить что-то вроде:

    completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
    
  • Вы должны принять учетные данные, когда успешно подтвердите их.Где у вас // Трастовый сертификат

    добавить что-то вроде:

    let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust)
    completionHandler(URLSession.AuthChallengeDisposition.useCredential, credential);
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...