Certificate pinning in Alamofire

2020-06-12 04:21发布

I am creating an iPad app that accesses HTTPS web services. I want to implement pinning, but am having issues.

This class creates the Alamofire Manager (mostly taken from documentation):

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

This function makes the call:

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

The certificate is saved in the project and shows under 'Targets > Build Phases > Copy Bundle Resources'.

I am currently receiving the following error every time I make the request (from the else block in testPinning()):

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

2条回答
2楼-- · 2020-06-12 04:59

So, the issue was that the certificate was saved in the wrong format.

ServerTrustPolicy.certificatesInBundle() finds all certificates in the bundle based on a list of extensions, then tries to load them using SecCertificateCreateWithData. Per its documentation, this function:

Returns NULL if the data passed in the data parameter is not a valid DER-encoded X.509 certificate

When you export a certificate in Firefox, you have a "format" pop-up at the bottom of the file browser. Select "X.509 Certificate (DER)", and you should get a certificate in the right format for this purpose.

查看更多
爷的心禁止访问
3楼-- · 2020-06-12 05:03

First, you need to download the certificate. The best way is to download the certificate on the Firefox browser.

Step 1

Go to your webpage/ API and click the lock icon to get a certificate.

enter image description here

Step 2

Click View Certificate

enter image description here

Step 3

Click Certificate Fields tab's first section and click export

enter image description here

Step 4

Select the Format:- DER

enter image description here

Step 5

Drag and drop the file into your XCode Project

enter image description here

Step 6

Add Certificate under 'Targets > Build Phases > Copy Bundle Resources'

enter image description here

Step 7

Add Network Manager File. Replace your URL with 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)
         )

     }
 }

Step 8

Add a file to get the session manager

import Foundation
import Alamofire
import SwiftyJSON

class APIPinning {

    private static let NetworkManager = MYPNetworkManager()

    public static func getManager() -> SessionManager {
        return NetworkManager.Manager!
    }
 }

Step 9

Use this session manager on Alamofire eg:-

 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")
        }
}
}
查看更多
登录 后发表回答