iOS Https證書驗證問題全解

在調(diào)用Https地址請求數(shù)據(jù)時,我們會遇到證書驗證的問題。
關于證書調(diào)用的方法。主要有兩個代理
這兩個方法是不同的,一個是session負責處理,一個是task負責處理,處理的方式一樣,一般我們會用session來處理。


Requests credentials from the delegate in response to a session-level authentication request from the remote server.
從委托請求憑據(jù),以響應來自遠程服務器的會話級身份驗證請求。
optional func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)

  • When a remote server asks for client certificates or Windows NT LAN Manager (NTLM) authentication, to allow your app to provide appropriate credentials
  • When a session first establishes a connection to a remote server that uses SSL or TLS, to allow your app to verify the server’s certificate chain
    If you do not implement this method, the session calls its delegate’s urlSession(_:task:didReceive:completionHandler:) method instead.

翻譯:

這種方法在兩種情況下調(diào)用:
當遠程服務器請求客戶端證書或Windows NT LAN Manager (NTLM)身份驗證時,允許您的應用程序提供適當?shù)膽{據(jù)
當會話首次建立到使用SSL或TLS的遠程服務器的連接時,允許您的應用程序驗證服務器的證書鏈
如果不實現(xiàn)此方法,則會話將調(diào)用其委托的urlSession(_:task:didReceive:completionHandler:)方法。

當sesion的代理方法未實現(xiàn)時,會執(zhí)行下面的這種方式,如果兩種方法都未實現(xiàn)的時候會怎么處理呢?我們下面會說到。

Requests credentials from the delegate in response to an authentication request from the 
remote server.
從委托請求憑據(jù)以響應來自遠程服務器的身份驗證請求。
optional func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)

This method handles task-level authentication challenges. The URLSessionDelegate protocol also provides a session-level authentication delegate method. The method called depends on the type of authentication challenge:
For session-level challenges—NSURLAuthenticationMethodNTLM, NSURLAuthenticationMethodNegotiate, NSURLAuthenticationMethodClientCertificate, or NSURLAuthenticationMethodServerTrust—the NSURLSession object calls the session delegate’s urlSession(_:didReceive:completionHandler:) method. If your app does not provide a session delegate method, the NSURLSession object calls the task delegate’s urlSession(_:task:didReceive:completionHandler:) method to handle the challenge.
For non-session-level challenges (all others), the URLSession object calls the session delegate’s urlSession(_:task:didReceive:completionHandler:) method to handle the challenge. If your app provides a session delegate and you need to handle authentication, then you must either handle the authentication at the task level or provide a task-level handler that calls the per-session handler explicitly. The session delegate’s urlSession(_:didReceive:completionHandler:) method is not called for non-session-level challenges.

翻譯:

此方法處理任務級身份驗證挑戰(zhàn)。URLSessionDelegate協(xié)議還提供了一個任務級的身份驗證委托方法。所調(diào)用的方法取決于認證挑戰(zhàn)的類型:
對于會話級別的挑戰(zhàn)——nsurlauthenticationmethodntlm、NSURLAuthenticationMethodNegotiate、NSURLAuthenticationMethodClientCertificate或nsurlauthenticationmethodservertrust——NSURLSession對象調(diào)用會話委托的urlSession(_:didReceive:completionHandler:)方法。如果你的應用程序沒有提供一個會話委托方法,NSURLSession對象會調(diào)用任務委托的urlSession(_:task:didReceive:completionHandler:)方法來處理這個挑戰(zhàn)。
對于非會話級別的挑戰(zhàn)(所有其他挑戰(zhàn)),URLSession對象調(diào)用會話委托的URLSession (_:task:didReceive:completionHandler:)方法來處理該挑戰(zhàn)。如果您的應用程序提供了一個會話委托,而您需要處理身份驗證,那么您必須在任務級處理身份驗證,或者提供一個顯式調(diào)用每個會話處理程序的任務級處理程序。對于非會話級挑戰(zhàn),不會調(diào)用會話委托的urlSession(_:didReceive:completionHandler:)方法。

這兩個代理方法的總結(jié)就是,如果是session級別的驗證,那么可以使用任一一種方法解決。
如果是非會話級別的驗證(on-session-level challenges (all others)),那么session級別不能替代task級別。

  • 不同的請求驗證方式
NSURLAuthenticationMethodClientCertificate
此保護空間使用客戶端證書身份驗證。
NSURLAuthenticationMethodNegotiate
協(xié)商在這個保護空間中使用Kerberos還是NTLM身份驗證。
NSURLAuthenticationMethodNTLM
對這個保護空間使用NTLM身份驗證。
NSURLAuthenticationMethodServerTrust
為這個保護空間執(zhí)行服務器信任身份驗證(證書驗證)。

在處理會話的時候,將分為三個部分

  • 驗證服務器證書 NSURLAuthenticationMethodServerTrust
  • 服務器驗證本地 NSURLAuthenticationMethodClientCertificate
  • 其他 NSURLAuthenticationMethodNTLM,NSURLAuthenticationMethodNegotiate

一般我們會用到兩種,一種是我們驗證服務器,一種是服務器驗證客戶端。

NSURLAuthenticationMethodServerTrust

如果我們驗證服務器的話,可以有兩種方式

  • 直接驗證通過
  • 使用代碼和證書驗證
  //不做任何驗證,直接信任服務器
    static private func trustServer(challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
        let disposition = URLSession.AuthChallengeDisposition.useCredential
        let credential = URLCredential.init(trust: challenge.protectionSpace.serverTrust!)
        return (disposition, credential)
        
    }
    //驗證服務器證書
    static private func trustServerWithCer(challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
        
        var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
        var credential: URLCredential?
        
        //獲取服務器發(fā)送過來的證書
        let serverTrust:SecTrust = challenge.protectionSpace.serverTrust!
        let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)!
        let remoteCertificateData = CFBridgingRetain(SecCertificateCopyData(certificate))!
         //加載本地CA證書
        let cerPath = Bundle.main.path(forResource: "你本地的cer證書文件名", ofType: "cer")!
        let cerUrl = URL(fileURLWithPath:cerPath)
        let localCertificateData = try! Data(contentsOf: cerUrl)
        if (remoteCertificateData.isEqual(localCertificateData) == true) {            
            //服務器證書驗證通過
            disposition = URLSession.AuthChallengeDisposition.useCredential
            credential = URLCredential(trust: serverTrust)            
        } else {
            //服務器證書驗證失敗
            disposition = URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge
        }
        return (disposition, credential)
    }
    
NSURLAuthenticationMethodClientCertificate
 //發(fā)送客戶端證書交由服務器驗證
    static private func sendClientCer() -> (URLSession.AuthChallengeDisposition, URLCredential?) {
        
        let disposition = URLSession.AuthChallengeDisposition.useCredential
        var credential: URLCredential?
        
        //獲取項目中P12證書文件的路徑
        let path: String = Bundle.main.path(forResource: "你本地的p12證書文件名", ofType: "p12")!
        let PKCS12Data = NSData(contentsOfFile:path)!
        let key : NSString = kSecImportExportPassphrase as NSString
        let options : NSDictionary = [key : "p12證書的密碼"] //客戶端證書密碼
        
        var items: CFArray?
        let error = SecPKCS12Import(PKCS12Data, options, &items)
        
        if error == errSecSuccess {
            
            let itemArr = items! as Array
            let item = itemArr.first!
            
            let identityPointer = item["identity"];
            let secIdentityRef = identityPointer as! SecIdentity
            
            let chainPointer = item["chain"]
            let chainRef = chainPointer as? [Any]
            
            credential = URLCredential.init(identity: secIdentityRef, certificates: chainRef, persistence: URLCredential.Persistence.forSession)
            
        }
        
        return (disposition, credential)
        
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容