Добавить закрепление с помощью открытого ключа в классе alamofire manger swift

Вот мой менеджер alamofire, как я могу добавить на него закрепление открытого ключа? Пожалуйста, помогите мне, я не мог знать, как это сделать в своем коде, если возможно, мне нужно пошаговое объяснение того, как это сделать с AFManager, который имеет все запросы

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

}

  }

}

Я видел этот пример, но не знал, как это сделать, и где я должен разместить код, см. Ссылку ниже: https://infinum.co/the-capsized-eight/ssl-pinning-revisited

6 ответов

Безопасность

Использование безопасного HTTPS-соединения при обмене данными с серверами и веб-службами является важным шагом в защите конфиденциальных данных. По умолчанию Alamofire будет оценивать цепочку сертификатов, предоставленную сервером, с помощью встроенной проверки Apple, предоставляемой платформой безопасности. Это гарантирует, что цепочка сертификатов действительна, но не предотвращает атаки типа "злоумышленник в середине" (MITM) или другие потенциальные уязвимости. Чтобы смягчить атаки MITM, приложения, работающие с конфиденциальными данными клиентов или финансовой информацией, должны использовать привязку сертификатов или открытых ключей, предоставляемую ServerTrustPolicy.

ServerTrustPolicy

Перечисление ServerTrustPolicy оценивает доверие к серверу, обычно обеспечиваемое URLAuthenticationChallenge при подключении к серверу через безопасное соединение HTTPS.

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

Существует множество различных случаев оценки доверия к серверу, дающих вам полный контроль над процессом проверки:

  • performDefaultEvaluation: использует оценку доверия к серверу по умолчанию, позволяя вам контролировать, нужно ли проверять хост, предоставленный задачей.
  • pinCertificates: использует закрепленные сертификаты для проверки доверия сервера. Доверие сервера считается действительным, если один из закрепленных сертификатов совпадает с одним из сертификатов сервера.
  • pinPublicKeys: использует закрепленные открытые ключи для проверки доверия сервера. Доверие сервера считается действительным, если один из закрепленных открытых ключей соответствует одному из открытых ключей сертификата сервера.
  • disableEvaluation: отключает все оценки, которые, в свою очередь, всегда будут считать любое доверие сервера действительным.
  • customEvaluation: использует связанное закрытие для оценки достоверности доверия сервера, что дает вам полный контроль над процессом проверки. Используйте с осторожностью.

Диспетчер политик доверия к серверу

Server TrustPolicyManager отвечает за хранение внутреннего сопоставления политик доверия сервера конкретному узлу. Это позволяет Alamofire оценивать каждый хост на соответствие другой политике доверия к серверу.

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)
)

Обязательно сохраните ссылку на новый экземпляр SessionManager, иначе все ваши запросы будут отменены, когда ваш sessionManager будет освобожден. Эти политики доверия к серверу приведут к следующему поведению:

test.example.com всегда будет использовать закрепление сертификата с включенной цепочкой сертификатов и включенной проверкой хоста, что требует выполнения следующих критериев для успешного установления связи TLS: Цепочка сертификатов ДОЛЖНА быть действительной. Цепочка сертификатов ДОЛЖНА включать один из закрепленных сертификатов. Узел запроса ДОЛЖЕН соответствовать узлу в листовом сертификате цепочки сертификатов. insecure.expired-apis.com никогда не будет оценивать цепочку сертификатов и всегда позволяет успешно выполнить рукопожатие TLS. Все остальные хосты будут использовать оценку по умолчанию, предоставленную Apple. Создание подкласса диспетчера политики доверия сервера

Если вам требуется более гибкое поведение сопоставления политик доверия сервера (например, домены с подстановочными знаками), создайте подкласс Server TrustPolicyManager и замените метод serverTrustPolicyForHost своей собственной реализацией.

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

        // Implement your custom domain matching behavior...

        return policy
    }
}

Проверка хоста

Все политики доверия сервера.performDefaultEvaluation, .pinCertificates и.pinPublicKeys принимают параметр validateHost. Установка значения true приведет к тому, что оценка доверия сервера будет проверять, что имя хоста в сертификате совпадает с именем хоста запроса. Если они не совпадают, оценка не удастся. Значение validateHost, равное false, по-прежнему будет оценивать полную цепочку сертификатов, но не будет проверять имя хоста конечного сертификата.

В производственных средах рекомендуется всегда устанавливать для параметра validateHost значение true. Проверка цепочки сертификатов

Как закрепленные сертификаты, так и открытые ключи имеют возможность проверки цепочки сертификатов с помощью параметра validateCertificateChain. Если для этого значения установлено значение true, будет оцениваться полная цепочка сертификатов в дополнение к проверке равенства байтов для закрепленных сертификатов или открытых ключей. Значение false пропустит проверку цепочки сертификатов, но по-прежнему будет выполнять проверку равенства байтов.

Есть несколько случаев, когда имеет смысл отключить проверку цепочки сертификатов. Чаще всего для отключения проверки используются самозаверяющие сертификаты и сертификаты с истекшим сроком действия. В обоих случаях оценка всегда завершалась ошибкой, но проверка равенства байтов по-прежнему гарантирует, что вы получаете сертификат, который ожидаете от сервера.

В производственных средах рекомендуется всегда устанавливать для validateCertificateChain значение true.Безопасность транспорта приложений

С добавлением App Transport Security (ATS) в iOS 9 возможно, что использование настраиваемого Server TrustPolicyManager с несколькими объектами ServerTrustPolicy не даст никакого эффекта. Если вы постоянно видите ошибки CFNetwork SSLHandshake failed (-9806), вы, вероятно, столкнулись с этой проблемой. Система ATS от Apple переопределяет всю систему задач, если вы не настроите параметры ATS в списке приложений, чтобы отключить ее в достаточном количестве, чтобы ваше приложение могло оценить доверие сервера.

Если вы столкнетесь с этой проблемой (высокая вероятность с самоподписанными сертификатами), вы можете обойти эту проблему, добавив следующее в свой 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>

Если вам нужно установить для NSExceptionRequiresForwardSecrecy значение NO, зависит от того, использует ли ваше TLS-соединение разрешенный набор шифров. В некоторых случаях необходимо установить значение NO. NSExceptionAllowsInsecureHTTPLoads ДОЛЖЕН быть установлен в YES, чтобы позволить SessionDelegate получать обратные вызовы вызова. Как только обратные вызовы вызова вызываются, Server TrustPolicyManager возьмет на себя оценку доверия сервера. Вам также может потребоваться указать NSTemporaryExceptionMinimumTLSVersion, если вы пытаетесь подключиться к хосту, который поддерживает только версии TLS ниже 1.2.

В производственных средах рекомендуется всегда использовать действительные сертификаты. Использование самозаверяющих сертификатов в локальной сети

Если вы пытаетесь подключиться к серверу, работающему на вашем локальном хосте, и используете самозаверяющие сертификаты, вам нужно будет добавить следующее в свой Info.plist.

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

Согласно документации Apple, установка NSAllowsLocalNetworking на YES позволяет загружать локальные ресурсы без отключения ATS для остальной части вашего приложения.

Ссылка:-https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md

Подробности реализации см. В тестах. https://github.com/Alamofire/Alamofire/blob/master/Tests/TLSEvaluationTests.swift

Закрепление SSL с использованием TrustKit с Alamofire. Здесь я включил класс API Manager. Это поможет вам решить, используя Alamofire с 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")
                }
        }
    }
}

Полный текст руководства можно найти по этой ссылке.

Alamofire изменил код закрепления sll, вырезанный в новой версии (Alamofire 5.0).

Вы должны использовать ServerTrustManager, как показано ниже,

let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = timeoutIntervalForRequest
        let trustManager = ServerTrustManager(evaluators: [
                     "dev.ehliyetcepte.com": PublicKeysTrustEvaluator(),
                     "uat.ehliyetcepte.com": DisabledEvaluator(),
                     "pilot.ehliyetcepte.com": DisabledEvaluator(),
                     "prod.ehliyetcepte.com": DisabledEvaluator()])


        self.session = Session(startRequestsImmediately: true,
                               configuration: configuration,
                               delegate: self,
                               serverTrustManager: trustManager)

Я бы порекомендовал использовать TrustKit. Это специальная библиотека, которая работает со всем, что основано на NSURLSession, включая Alamofire. В зависимости от вашего варианта использования это может быть так же просто, как добавить несколько значений в Info.plist.

Закрепление сертификатов, как и любая мера безопасности, это не то, что вы должны реализовать самостоятельно, но вы должны использовать проверенную библиотеку.

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

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

Я нашел это решение

      let session = Session(delegate:CustomSessionDelegate())
session.request.... 


class CustomSessionDelegate: SessionDelegate {
  private static let publicKeyHash = "your_public_key"
  let rsa2048Asn1Header:[UInt8] = [
    0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
    0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00
]

override func urlSession(_ session: URLSession,
                         task: URLSessionTask,
                         didReceive challenge: URLAuthenticationChallenge,
                         completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    guard let serverTrust = challenge.protectionSpace.serverTrust else {
        completionHandler(.cancelAuthenticationChallenge, nil);
        return
    }
    if let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
        // Server public key
        guard let serverPublicKey = SecCertificateCopyKey(serverCertificate) else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
        guard let serverPublicKeyData = SecKeyCopyExternalRepresentation(serverPublicKey, nil) else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
        let data:Data = serverPublicKeyData as Data
        // Server Hash key
        let serverHashKey = sha256(data: data)
        // Local Hash Key
        let publickKeyLocal = type(of: self).publicKeyHash
        if (serverHashKey == publickKeyLocal) {
            // Success! This is our server
            print("Public key pinning is successfully completed")
            completionHandler(.useCredential, URLCredential(trust:serverTrust))
            return
        } else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
    }
}

private func sha256(data : Data) -> String {
    var keyWithHeader = Data(rsa2048Asn1Header)
    keyWithHeader.append(data)
    var hash = [UInt8](repeating: 0,  count: Int(CC_SHA256_DIGEST_LENGTH))
    keyWithHeader.withUnsafeBytes {
        _ = CC_SHA256($0, CC_LONG(keyWithHeader.count), &hash)
    }
    
    
    return Data(hash).base64EncodedString()
}
Другие вопросы по тегам