在alamofire管理器类swift中添加公钥固定

时间:2019-03-06 12:46:50

标签: swift request alamofire public-key-pinning

这是我的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 个答案:

答案 0 :(得分:2)

安全性

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

ServerTrustPolicy

通过安全的HTTPS连接连接到服务器时,ServerTrustPolicy枚举评估通常由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中添加应用传输安全性(ATS),可能无法将自定义ServerTrustPolicyManager与多个ServerTrustPolicy对象一起使用。如果您持续看到CFNetwork SSLHandshake失败(-9806)错误,则可能是您遇到了此问题。除非您在应用程序的plist中配置ATS设置以禁用足够多的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连接是否使用允许的密码套件。在某些情况下,需要将其设置为“否”。为了允许SessionDelegate接收质询回调,必须将NSExceptionAllowsInsecureHTTPLoads设置为YES。一旦质询回调被调用,ServerTrustPolicyManager将接管服务器信任评估。如果尝试连接到仅支持TLS版本小于1.2的主机,则可能还需要指定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

答案 1 :(得分:1)

我建议使用TrustKit。这是一个专用库,可与基于NSURLSession的所有内容(包括Alamofire)一起使用。视您的用例而定,就像向Info.plist添加一些值一样简单。

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

答案 2 :(得分:0)

使用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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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)

答案 5 :(得分:0)

我找到了这个解决方案

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