你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 自己動手寫一個 iOS 網絡請求庫(五)——設置 SSL 鋼釘

自己動手寫一個 iOS 網絡請求庫(五)——設置 SSL 鋼釘

編輯:IOS開發基礎

0.jpg

  • 代碼示例:https://github.com/johnlui/Swift-On-iOS/blob/master/BuildYourHTTPRequestLibrary

  • 開源項目:Pitaya,適合大文件上傳的 HTTP 請求庫:https://github.com/johnlui/Pitaya

這個系列的文章本已終結,現在續上,就是為了一個未來大家一定會越來越需要的功能:設置 SSL 證書鋼釘。

說起來這個功能也很簡單,在我們調用 HTTPS 協議的時候,事先把 SSL 證書存到 App 本地,然後在每次請求的時候都進行一次驗證,避免中間人攻擊(Man-in-the-middle attack)。同時,這個功能也是我們使用自簽名證書時候必須的,因為系統默認會拒絕我們自己簽名的不受信任的證書,導致連接失敗。

廢話不多說,我們進入正題。

證書獲取

NSURLSession 支持 cer 格式的證書文件,而 Apache 和 Nginx 默認的證書都是 crt 格式,我們需要雙擊將其安裝到系統中,再使用鑰匙串 App 將這個證書導出為 cer 格式即可。

blob.png

blob.png

開搞

經過查詢資料,發現 NSURLSession 提供了 SSL 證書處理的代理方法,我們需要對我們的 NetworkManager 類進行一點點改造。

自定義 session

如果想要調用到我們想要的代理方法,需要我們自定義一下 NSURLSession 對象:

var session: NSURLSession!
... ...
init(... ...) {
    ... ...
    super.init()
    self.session = NSURLSession(configuration: NSURLSession.sharedSession().configuration, delegate: self, delegateQueue: NSURLSession.sharedSession().delegateQueue)
}

實現代理

由於上面我們把 NSURLSession 的代理設置成了 self,所以現在我們要讓 NetworkManager 類實現 NSURLSessionDelegate 這個 protocol。又由於 NSURLSessionDelegate 繼承自 NSObjectProtocol,所以我們需要讓 NetworkManager 繼承自 NSObject 類:

class NetworkManager: NSObject, NSURLSessionDelegate {
... ...

實現代理方法

接下來我們就通過實現 SSL 證書檢查的代理方法來干預網絡請求了。

增加兩個成員變量:

var localCertData: NSData!
var sSLValidateErrorCallBack: (() -> Void)?

增加設置他們的函數:

func addSSLPinning(LocalCertData data: NSData, SSLValidateErrorCallBack: (()->Void)? = nil) {
    self.localCertData = data
    self.sSLValidateErrorCallBack = SSLValidateErrorCallBack
}

實現代理方法,介入網絡請求:

@objc func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
    if let localCertificateData = self.localCertData {
        if let serverTrust = challenge.protectionSpace.serverTrust,
            certificate = SecTrustGetCertificateAtIndex(serverTrust, 0),
            remoteCertificateData: NSData = SecCertificateCopyData(certificate) {
                if localCertificateData.isEqualToData(remoteCertificateData) {
                    let credential = NSURLCredential(forTrust: serverTrust)
                    challenge.sender?.useCredential(credential, forAuthenticationChallenge: challenge)
                    completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, credential)
                } else {
                    challenge.sender?.cancelAuthenticationChallenge(challenge)
                    completionHandler(NSURLSessionAuthChallengeDisposition.CancelAuthenticationChallenge, nil)
                    self.sSLValidateErrorCallBack?()
                }
        } else {
            NSLog("Get RemoteCertificateData or LocalCertificateData error!")
        }
    } else {
        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, nil)
    }
}

至此,檢測 SSL 證書的功能就做完了。接下來我們檢驗成果。

檢驗成果

『Thus, programs must be written for people to read, and only incidentally for machines to execute.』

——《Structure and Interpretation of Computer Programs 》 Harold Abelson

『代碼是寫給人看的,只是恰好能運行。』這句話出自大名鼎鼎的 SICP,出處:https://mitpress.mit.edu/sicp/front/node3.html

在搞完了這個功能之後,我突然發現我好像被 Alamofire 的 API 設計給帶偏了:寫起來方便是最不重要的,便於使用者理解才是最重要的。所以我打算殺掉所有疑似假裝是奇技淫巧的集合型 API,改由純粹的 構造對象->修改對象->發起請求 模式,降低使用者的理解成本。

我使用我的網站 lvwenhan.com  的證書來進行此次驗證:

let network = NetworkManager(url: "https://lvwenhan.com/", method: "GET") { (data, response, error) -> Void in
    if let _ = error {
        NSLog(error.description)
    } else {
        print("證書正確!")
    }
}
let certData = NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("lvwenhancom", ofType: "cer")!)!
network.addSSLPinning(LocalCertData: certData) { () -> Void in
    print("SSL 證書錯誤,遭受中間人攻擊!")
}
network.fire()
return;

得到如下結果:

blob.png

接下來把網址改成 https://www.baidu.com/,運行,查看結果:

blob.png

搞定!

寫在後面的話

本文中我只檢測了經過第三方簽名的受信任的 SSL 證書的檢驗結果,並沒有測試自簽名證書,希望有人測試之後把結果告訴我 :) 在文章下面評論或者上 Github 提 issue 都行~

《自己動手寫一個 iOS 網絡請求庫》系列文章可能真的結束了,感謝你的閱讀!


  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved