主要針對使用Alamofire進行Https網(wǎng)絡(luò)請求
Alamofire
通過SessionManager.delegate.sessionDidReceiveChallenge
這個block
來驗證證書, block
中會將session
與 challenge
作為參數(shù)傳遞, 用戶通過其中的信息進行判定, 返回URLSession.AuthChallengeDisposition
與 URLCredential
供AlamoFire
處理
-
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