【问题标题】:Certificate pinning in Alamofire在 Alamofire 中固定证书
【发布时间】:2016-04-09 05:51:19
【问题描述】:

我正在创建一个访问 HTTPS Web 服务的 iPad 应用程序。我想实现固定,但遇到了问题。

这个类创建 Alamofire 管理器(主要取自文档):

class NetworkManager {

    var manager: Manager?

    init() {
        let serverTrustPolicies: [String: ServerTrustPolicy] = [
            "www.google.co.uk": .PinCertificates(
                certificates: ServerTrustPolicy.certificatesInBundle(),
                validateCertificateChain: true,
                validateHost: true
            ),
            "insecure.expired-apis.com": .DisableEvaluation
        ]

        manager = Alamofire.Manager(
            configuration: NSURLSessionConfiguration.defaultSessionConfiguration(),
            serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
        )
    }
}

这个函数进行调用:

static let networkManager = NetworkManager()

public static func testPinning() {
    networkManager.manager!.request(.GET, "https://www.google.co.uk").response { response in
        if response.1 != nil {
            print("Success")
            print(response.1)
            print(response.1?.statusCode)
        } else {
            print("Error")
            print(response.3)
        }
    }
}

项目中的 certificate is saved 并显示在“目标 > 构建阶段 > 复制捆绑资源”下。

我目前每次发出请求时都会收到以下错误(来自 testPinning() 中的 else 块):

Optional(Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLKey=https://www.google.co.uk/, NSLocalizedDescription=cancelled, NSErrorFailingURLStringKey=https://www.google.co.uk/})

【问题讨论】:

  • 你提供了什么证书?您是否检查过 ServerTrustPolicy.certificatesInBundle() 返回的内容(即它是否实际上包含证书)?如果 Google 对同一个域使用多个证书,也不会感到太惊讶。
  • ServerTrustPolicy.certificatesInBundle() 返回 0,我不明白。有没有一种特定的方式可以将 .cer 证书添加到捆绑包中?关于谷歌,这是一个替换我正在开发的站点的示例站点,但上述问题是相同的。
  • 你的意思是它返回一个空数组?该调用的返回值应该是一个数组,而不是一个数字。您可能想尝试NSBundle.mainBundle().pathsForResourcesOfType(".cer", inDirectory: nil) 以查看是否在捆绑包中找到了该文件(如果是,则可能表明证书文件内容存在问题)。调用ServerTrustPolicy.certificatesInBundle() 时,日志中会显示什么内容?
  • 是的,这是正确的,我的意思是一个 0 值的数组。仔细观察,可以在linkcertificatesInBundle() 中找到那行代码。在那里的 for 循环中,找到了证书,但第 138 行阻止将证书添加到数组中。我怀疑它也可能是 .cer 的内容,尽管它在 Xcode 中打开得很好(关于我应该如何保存证书的任何建议,而不是问题中链接的方法?)。另外,我如何查看这些日志?
  • 当您在 Firefox 中导出证书时,文件浏览器底部会弹出一个“格式”弹出窗口。你选择了什么格式?我相信您需要“X.509 证书 (DER)”(根据 SecCertificateCreateWithData 的文档,其中指出“如果在 data 参数中传递的数据不是有效的 DER 编码的 X.509 证书,则返回 NULL ”(强调我的)。

标签: ios swift alamofire pinning


【解决方案1】:

在 Alamofire 版本 +5 之后发生了很大变化,我将在这里列出我为固定证书所做的工作。我的目标是 Moya,因为 Moya 作为 Alamofire 之上的一个层,适用于 Alamofire 的东西应该适用于 Moya。

首先要获得服务器证书,您需要在浏览器中打开它,然后单击锁定图标 然后您需要单击证书,如屏幕截图所示。 之后,您需要将此证书拖到您的桌面或下载它的任何地方,请查看下面的屏幕截图。 然后,您需要在 Xcode 检查屏幕截图中使用添加文件将其添加到您的项目中。 这是我的 Alamofire 代码:

var session: Session!

class ViewController: UIViewController {

func testPinning() {


let evaluators: [String: ServerTrustEvaluating] = [
    "stackoverflow.com": PublicKeysTrustEvaluator()
]

let manager = ServerTrustManager(evaluators: evaluators)

session = Session(serverTrustManager: manager)


session
    .request("https://stackoverflow.com/questions/34611112/certificate-pinning-in-alamofire/55902588#55902588", method: .get)
    .validate()
    .response(completionHandler: { [weak self] response in
        switch response.result {
        case .success:
            print(response.data)
        case .failure(let error):
            switch error {
            case .serverTrustEvaluationFailed(let reason):
                // The reason here is a place where you might fine-tune your
                // error handling and possibly deduce if it's an actualy MITM
                // or just another error, like certificate issue.
                //
                // In this case, this will show `noRequiredEvaluator` if you try
                // testing against a domain not in the evaluators list which is
                // the closest I'm willing to setting up a MITM. In production,
                // it will most likely be one of the other evaluation errors.
                print(reason)
            default:
                print("default")
            }
        }
    })

}

对于 Moya,您需要将该会话添加到您的 moya 提供程序。

    let evaluators: [String: ServerTrustEvaluating] = [
    "stackoverflow.com": PublicKeysTrustEvaluator()
]

let manager = ServerTrustManager(evaluators: evaluators)

session = Session(serverTrustManager: manager)



let provider = MoyaProvider<YourStackOVerflowProvider>(
    session: session
)


provider.request(.pluginManger) { response in
    switch response {
    case .failure(let err):
        print(err)
    case .success(let response):
        print(response)
    }
}

【讨论】:

    【解决方案2】:

    首先,您需要下载证书。最好的办法是在火狐浏览器上下载证书。

    第 1 步

    转到您的网页/API 并单击锁定图标以获取证书。

    第 2 步

    点击查看证书

    第 3 步

    单击证书字段选项卡的第一部分,然后单击导出

    第 4 步

    选择格式:- DER

    第 5 步

    将文件拖放到您的 XCode 项目中

    第 6 步

    在“目标 > 构建阶段 > 复制捆绑资源”下添加证书

    第 7 步

    添加网络管理器文件。将您的网址替换为 google.com

     import Foundation
     import Alamofire
     import SwiftyJSON
    
     class MYPNetworkManager {
    
    
         var Manager: SessionManager?
    
         init() {
             let serverTrustPolicies: [String: ServerTrustPolicy] = [
                 "https://google.com": .pinCertificates(
                     certificates: ServerTrustPolicy.certificates(),
                     validateCertificateChain: true,
                     validateHost: true
                 ),
                 "insecure.expired-apis.com": .disableEvaluation
             ]
    
             Manager = SessionManager(
                 serverTrustPolicyManager: ServerTrustPolicyManager(policies: 
     serverTrustPolicies)
             )
    
         }
     }
    

    第 8 步

    添加文件以获取会话管理器

    import Foundation
    import Alamofire
    import SwiftyJSON
    
    class APIPinning {
    
        private static let NetworkManager = MYPNetworkManager()
    
        public static func getManager() -> SessionManager {
            return NetworkManager.Manager!
        }
     }
    

    第 9 步

    在 Alamofire 上使用此会话管理器 例如:-

     public static func testPinning() {
    NetworkManager.Manager!.request("YourURL", method: .get, encoding: URLEncoding.httpBody, headers: MConnect.headersWithToken)
        .validate()
        .responseJSON { response in
    
            print(response)
            switch response.result {
            case .success:
    
                if let value = response.result.value {
                    let json = JSON(value)
                    print(json)
                } else {
    
                }
    
            case .failure:
                print("Error")
            }
    }
    }
    

    【讨论】:

      【解决方案3】:

      所以,问题是证书以错误的格式保存。

      ServerTrustPolicy.certificatesInBundle() 根据扩展列表查找捆绑包中的所有证书,然后尝试使用SecCertificateCreateWithData 加载它们。根据其文档,此功能:

      如果 data 参数中传递的数据无效,则返回 NULL DER 编码的 X.509 证书

      当您在 Firefox 中导出证书时,文件浏览器底部会弹出一个“格式”弹出窗口。选择“X.509 证书 (DER)”,您应该会得到一个格式正确的证书。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-12-06
        • 1970-01-01
        • 2017-11-01
        • 2018-10-28
        • 1970-01-01
        • 2019-07-17
        • 2016-09-21
        相关资源
        最近更新 更多