主要针对使用Alamofire进行Https网络请求
Alamofire
通过SessionManager.delegate.sessionDidReceiveChallenge
这个block
来验证证书, block
中会将session
与 challenge
作为参数传递, 用户通过其中的信息进行判定, 返回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
//使用默认的操作,如同没有实现这个代理方法
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
//拒绝认证, 但是会重新调用认证的代理方法
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 {
//认证服务器证书
let host = challenge.protectionSpace.host
if
let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
let serverTrust = challenge.protectionSpace.serverTrust
{
//session对对应的host有相应的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 {
// 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
//认证服务器证书
//SecTrustRef validation required. Applies to any protocol.
if method == NSURLAuthenticationMethodServerTrust {
//二选一
//双向认证 (需要cer)
//return self.serverTrust(session: session, challenge: challenge)
//host认证 (这里不使用服务器证书认证,只需自定义的几个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)
}
}
使用同参数同返回值的方法名给sessionDidReceiveChallenge
传值
Alamofire.SessionManager(configuration: configuration).delegate.sessionDidReceiveChallenge = CertificateTrustUtil.alamofireCertificateTrust