【问题标题】:add public key pinning in alamofire manger class swift在 alamofire manger 类 swift 中添加公钥固定
【发布时间】:2019-07-28 03:23:12
【问题描述】:

这是我的 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

【问题讨论】:

    标签: swift request alamofire public-key-pinning


    【解决方案1】:

    我找到了解决办法

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

    【讨论】:

    • 这只是 URL 会话而不是 Alamofire
    • 不,你错了,我用这个代码和 alamofire 再次给 customdelegate 读取代码以确保@TornikeGomareli
    【解决方案2】:

    安全

    在与服务器和 Web 服务通信时使用安全的 HTTPS 连接是保护敏感数据的重要一步。默认情况下,Alamofire 将使用安全框架提供的 Apple 内置验证来评估服务器提供的证书链。虽然这保证了证书链是有效的,但它并不能防止中间人 (MITM) 攻击或其他潜在漏洞。为了减轻 MITM 攻击,处理敏感客户数据或财务信息的应用程序应使用 ServerTrustPolicy 提供的证书或公钥固定。

    ServerTrustPolicy

    ServerTrustPolicy 枚举在通过安全 HTTPS 连接连接到服务器时评估通常由 URLAuthenticationChallenge 提供的服务器信任。

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

    有许多不同的服务器信任评估案例让您可以完全控制验证过程:

    • performDefaultEvaluation:使用默认的服务器信任评估 同时允许您控制是否验证提供的主机 迎接挑战。
    • pinCertificates:使用固定证书验证服务器 相信。如果其中一个被固定,则服务器信任被认为是有效的 证书与服务器证书之一匹配。
    • pinPublicKeys:使用固定的公钥验证服务器 相信。如果其中一个被固定,则服务器信任被认为是有效的 公钥与服务器证书公钥之一匹配。
    • disableEvaluation:禁用所有评估,这反过来将始终 将任何服务器信任视为有效。
    • customEvaluation:使用关联的闭包来评估 服务器信任的有效性,从而使您可以完全控制 验证过程。谨慎使用。

    服务器信任策略管理器

    ServerTrustPolicyManager 负责存储服务器信任策略到特定主机的内部映射。这允许 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 提供的默认评估。 子类化服务器信任策略管理器

    如果您发现自己需要更灵活的服务器信任策略匹配行为(即通配符域),则将 ServerTrustPolicyManager 子类化并使用您自己的自定义实现覆盖 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。 应用传输安全

    随着在 iOS 9 中添加 App Transport Security (ATS),使用带有多个 ServerTrustPolicy 对象的自定义 ServerTrustPolicyManager 可能无效。如果您不断看到 CFNetwork SSLHandshake failed (-9806) 错误,您可能遇到了这个问题。 Apple 的 ATS 系统会覆盖整个质询系统,除非您在应用的 plist 中配置 ATS 设置以禁用足够多的 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 接收挑战回调。一旦挑战回调被调用,ServerTrustPolicyManager 将接管服务器信任评估。如果您尝试连接到仅支持低于 1.2 的 TLS 版本的主机,您可能还需要指定 NSTemporaryExceptionMinimumTLSVersion。

    建议在生产环境中始终使用有效证书。 在本地网络中使用自签名证书

    如果您尝试连接到在您的本地主机上运行的服务器,并且您使用的是自签名证书,则需要将以下内容添加到您的 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#L290-L450

    【讨论】:

      【解决方案3】:

      Alamofire 已更改使用新版本(Alamofire 5.0)截断的 sll 固定代码。

      您应该像下面一样使用 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)
      

      【讨论】:

        【解决方案4】:
        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)
        )
        
        

        【讨论】:

        • Alamofire使用SessionManager管理ServerTrust,可以使用自定义ServerTrustPolicyManager设置Server Trust,然后可以使用SessionManager.request方法,发送请求
        【解决方案5】:

        使用 TrustKit 和 Alamofire 进行 SSL 固定。在这里,我包含了 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")
                        }
                }
            }
        }
        

        如需完整教程,请参阅this link

        【讨论】:

          【解决方案6】:

          我建议使用TrustKit。它是一个专用库,可以处理基于 NSURLSession 的所有内容,包括 Alamofire。根据您的用例,它可能就像向 Info.plist 添加一些值一样简单。

          与任何安全措施一样,证书固定不是您应该自己实现的,但您应该使用经过验证的库。

          【讨论】:

          • 它看起来非常有用的库非常感谢您,但是您能解释一下演示中的有效引脚和备份引脚是什么,因为我没有得到它,我对此了解不多,我只有一个公钥是有效的密码吗?以及从哪里获得备份密钥
          • repo 包含一个从证书生成哈希的脚本。这称为引脚。这可能就是你所拥有的。备份引脚是一个证书,可以在后端出现一些问题时使用。你不必提供一个真实的,你必须可以弥补。
          • 你能告诉我如何在 alamofire 中使用这个 TrustKit 库吗?因为我找不到如何使用 alamofire 来实现
          • @RoohAl-mahaba 用 alamofire 检查我对 Trustkit 的回答。
          • 这里是代码示例:github.com/Alamofire/Alamofire/issues/… 别忘了用正确的配置初始化 TrustKit
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-01
          • 2015-04-18
          • 1970-01-01
          • 1970-01-01
          • 2017-12-17
          • 2019-10-03
          相关资源
          最近更新 更多