根据项目需求,需要进行HTTPS网络请求并添加证书校验过程,由于没有做过,前前后后研究了两个多星期,才通过校验,可以正常进行网络请求,不多说了直接上代码
- 设置程序中的info.plist
(由于我们使用的是自签名的证书,而苹果ATS(App Transport Security)只信任知名CA颁发的证书,所以在iOS9下即使是HTTPS请求还是会被ATS拦截。)
(验证该域名是否通过ats认证 网上有很多,请自行搜索)
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
- 开始正式验证
//MARK: https证书校验
func setAlamofireHttps() {
shareSessionManager.delegate.sessionDidReceiveChallenge = {(session: URLSession, challenge: URLAuthenticationChallenge) in
let method = challenge.protectionSpace.authenticationMethod
if method == NSURLAuthenticationMethodServerTrust {
LJQPrint("服务端证书认证")
return self.trustServerWithCer(challenge: challenge)
}else if method == NSURLAuthenticationMethodClientCertificate {
LJQPrint("客户端认证")
return AlamofireExtension.sendClientCer()
}else {
LJQPrint("不通过验证")
return (.cancelAuthenticationChallenge, nil)
}
}
}
//不做任何验证,直接信任服务器(这个方法使用暂时没有通过,后续在进行研究)
static private func trustServer(challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
let disposition = URLSession.AuthChallengeDisposition.performDefaultHandling
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?
//获取服务器发送过来的证书
let serverTrust:SecTrust = challenge.protectionSpace.serverTrust!
//获取本地证书(因为自签名证书你需要进行添加锚点,该方法是Alamofire提供的,自动获取该工程下的本地证书,自动添加锚点)
let localCertificates = ServerTrustPolicy.certificates(in: Bundle.main)
//设置校验策略
let securityPolicy = ServerTrustPolicy.pinCertificates(certificates: localCertificates, validateCertificateChain: true, validateHost: true)
//进行校验证书
if securityPolicy.evaluate(serverTrust, forHost: challenge.protectionSpace.host) {
Print("成功")
disposition = URLSession.AuthChallengeDisposition.useCredential
credential = URLCredential(trust: serverTrust)
}else {
Print("失败")
disposition = URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge
}
return (disposition, credential)
}
//发送客户端证书交由服务器验证(未进行校验,该项目只需要进行单项认证)
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)
}
直接信任证书和上传客户端证书由服务器检验方法未进行测试,如有问题,请留言,还望多多指点,
(该方法借鉴多方文档,基本能搜到的文档都查看了,就不一一列举借鉴哪些文档了,多多包涵)
由于Alamofire升级到5.0版本,关于证书校验有改动
## 第一步
/// 获取证书
/// - Returns: Https证书
func certificate() -> SecCertificate? {
let filePath = Bundle.main.path(forResource: "rootcert", ofType: "cer")
if filePath == nil {
return nil
}
let data = try! Data(contentsOf: URL(fileURLWithPath: filePath ?? ""))
let certificate = SecCertificateCreateWithData(nil, data as CFData)!
return certificate
}
## 第二步
/// 证书校验设置
let certificates:[SecCertificate] = [certificate()!]
let trusPolicy = PinnedCertificatesTrustEvaluator(certificates: certificates, acceptSelfSignedCertificates: false, performDefaultValidation: false, validateHost: false)
let manager = ServerTrustManager(evaluators:[(URL.init(string:baseURL)?.host)!:trusPolicy])
sessionManager = Session(configuration: configuration,serverTrustManager: manager)
* 注意: let manager = ServerTrustManager(evaluators:[(URL.init(string:baseURL)?.host)!:trusPolicy])中键值对key不能是 "https://xxxx.xxx.com" 否则请求会报 "failure(Alamofire.AFError.serverTrustEvaluationFailed(reason: Alamofire.AFError.ServerTrustFailureReason.noRequiredEvaluator(host: "xxxx.xxx.com")))",key应该是"xxxx.xxx.com"