Alamofire進行Https網(wǎng)絡(luò)請求自簽名證書

主要針對使用Alamofire進行Https網(wǎng)絡(luò)請求

Alamofire 通過SessionManager.delegate.sessionDidReceiveChallenge 這個block來驗證證書, block中會將sessionchallenge 作為參數(shù)傳遞, 用戶通過其中的信息進行判定, 返回URLSession.AuthChallengeDispositionURLCredentialAlamoFire處理

  • URLSession.AuthChallengeDisposition(認證的意向, 枚舉值):
     //Use the specified credential, which may be nil
     //使用具體的證書
     case useCredential
     
     //Use the default handling for the challenge as though this delegate method were not implemented. The provided credential parameter is ignored
     //使用默認的操作,如同沒有實現(xiàn)這個代理方法
     case performDefaultHandling
     
     //Cancel the entire request. The provided credential parameter is ignored
     //取消認證
     case cancelAuthenticationChallenge
     
     //Reject this challenge, and call the authentication delegate method again with the next authentication protection space. The provided credential parameter is ignored
     //拒絕認證, 但是會重新調(diào)用認證的代理方法
     case rejectProtectionSpace
    ```
 - `URLCredential`(證書管理類):
   ```swift
     //This class is an immutable object representing an authentication credential.  The actual type of the credential is determined by the constructor called in the categories declared below
     //證書的類型由URLCredential的分類的實例化方法確定
     
     //represent an internet password credential
     //相當于賬號密碼認證
     public init(user: String, password: String, persistence: URLCredential.Persistence)
     
     //represent a client certificate credential. Client certificates are commonly stored on the users computer in the keychain and must be presented to the server during a handshake
     //客戶端證書 存在本地客戶端(獲取本地P12證書, 用于客戶端認證)
     public init(identity: SecIdentity, certificates certArray: [Any]?, persistence: URLCredential.Persistence)
     
     //specifies that the specified trust has been accepted
     //具體的trust生成的證書(用于獲取遠端證書與本地cer證書對比的雙向認證)
     public init(trust: SecTrust)
  • AlamoFire的處理方式
//默認處理方式
    var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
    var credential: URLCredential?

    if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
        //使用自定義的簽名方式
        (disposition, credential) = sessionDidReceiveChallenge(session, challenge)
        
    } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
        
        //認證服務(wù)器證書
        let host = challenge.protectionSpace.host
        
        if
            let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
            let serverTrust = challenge.protectionSpace.serverTrust
        {
            //session對對應(yīng)的host有相應(yīng)的Policy alamofire默認下session.serverTrustPolicyManager就為nil
            if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
                //認證
                disposition = .useCredential
                credential = URLCredential(trust: serverTrust)
            } else {
                //取消
                disposition = .cancelAuthenticationChallenge
            }
        }
    }

認證過程

  • 自簽名host認證
//定義host白名單
static let selfSignedHosts = ["yue.haofenshu.com"]
class func selfSignedTrust(session: URLSession, challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
        
        var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
        var credential: URLCredential?
        
        if selfSignedHosts.contains(challenge.protectionSpace.host) {
            
            disposition = URLSession.AuthChallengeDisposition.useCredential
            credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
            
        }
        
        return (disposition, credential)
    }
  • 雙向認證
class func serverTrust(session: URLSession, challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
        
        var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
        var credential: URLCredential?
        
        //grab remote certificate
        let serverTrust:SecTrust = challenge.protectionSpace.serverTrust!
        let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)!
        let remoteCertificateData = CFBridgingRetain(SecCertificateCopyData(certificate))!
        
        //grab local certificate
        let cerPath = Bundle.main.path(forResource: "haofenshu", 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)
    }
  • 客戶端認證
class func clientTrust(session: URLSession, challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
        
        let disposition = URLSession.AuthChallengeDisposition.useCredential
        var credential: URLCredential?
        
        //獲取項目中P12證書文件的路徑
        let path: String = Bundle.main.path(forResource: "haofenshu", ofType: "p12")!
        let PKCS12Data = NSData(contentsOfFile:path)!
        let key : NSString = kSecImportExportPassphrase as NSString
        let options : NSDictionary = [key : "123456"] //客戶端證書密碼
        
        var items: CFArray?
        let error = SecPKCS12Import(PKCS12Data, options, &items)
        
        if error == errSecSuccess {
            
            if let itemArr = items as? NSArray,
                let item = itemArr.firstObject as? Dictionary<String, AnyObject> {
                
                // grab the identity
                let identityPointer = item["identity"];
                let secIdentityRef = identityPointer as! SecIdentity
                
                // grab the trust
                // let trustPointer = item["trust"]
                // let trustRef = trustPointer as! SecTrust
                
                // grab the cert
                let chainPointer = item["chain"]
                let chainRef = chainPointer as? [Any]
                
                // persistence: Credential should be stored only for this session
                credential = URLCredential.init(identity: secIdentityRef, certificates: chainRef, persistence: URLCredential.Persistence.forSession)
                
            }
            
        }
        
        return (disposition, credential)
    }

配置認證

class func alamofireCertificateTrust(session: URLSession, challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
        
        let method = challenge.protectionSpace.authenticationMethod
        
        //認證服務(wù)器證書
        //SecTrustRef validation required.  Applies to any protocol.
        if method == NSURLAuthenticationMethodServerTrust {
            
            //二選一
            //雙向認證 (需要cer)
            //return self.serverTrust(session: session, challenge: challenge)
            
            //host認證 (這里不使用服務(wù)器證書認證,只需自定義的幾個host即可信任)
            return self.selfSignedTrust(session: session, challenge: challenge)
            
        }
            
        //認證客戶端證書
        //SSL Client certificate.  Applies to any protocol.
        else if method == NSURLAuthenticationMethodClientCertificate {
            
            return self.clientTrust(session: session, challenge: challenge);
            
        }
            
            // 其它情況(不接受認證)
        else {
            
            return (.cancelAuthenticationChallenge, nil)
            
        }
    }

使用同參數(shù)同返回值的方法名給sessionDidReceiveChallenge傳值

Alamofire.SessionManager(configuration: configuration).delegate.sessionDidReceiveChallenge = CertificateTrustUtil.alamofireCertificateTrust
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末授段,一起剝皮案震驚了整個濱河市叛拷,隨后出現(xiàn)的幾起案子本砰,更是在濱河造成了極大的恐慌尤勋,老刑警劉巖缘缚,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件成福,死亡現(xiàn)場離奇詭異,居然都是意外死亡困食,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門翎承,熙熙樓的掌柜王于貴愁眉苦臉地迎上來硕盹,“玉大人,你說我怎么就攤上這事叨咖±痴觯” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵芒澜,是天一觀的道長仰剿。 經(jīng)常有香客問我,道長痴晦,這世上最難降的妖魔是什么南吮? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮誊酌,結(jié)果婚禮上部凑,老公的妹妹穿的比我還像新娘。我一直安慰自己碧浊,他們只是感情好涂邀,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著箱锐,像睡著了一般比勉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驹止,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天浩聋,我揣著相機與錄音,去河邊找鬼臊恋。 笑死衣洁,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的抖仅。 我是一名探鬼主播坊夫,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼撤卢!你這毒婦竟也來了环凿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤凸丸,失蹤者是張志新(化名)和其女友劉穎拷邢,沒想到半個月后袱院,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屎慢,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡瞭稼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了腻惠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片环肘。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖集灌,靈堂內(nèi)的尸體忽然破棺而出悔雹,到底是詐尸還是另有隱情,我是刑警寧澤欣喧,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布腌零,位于F島的核電站,受9級特大地震影響唆阿,放射性物質(zhì)發(fā)生泄漏益涧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一驯鳖、第九天 我趴在偏房一處隱蔽的房頂上張望闲询。 院中可真熱鬧,春花似錦浅辙、人聲如沸扭弧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸽捻。三九已至,卻和暖如春泽腮,著一層夾襖步出監(jiān)牢的瞬間泊愧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工盛正, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留删咱,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓豪筝,卻偏偏與公主長得像痰滋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子续崖,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容