根据项目需求,需要进行HTTPS网络请求并添加证书校验过程,由于没有做过,前前后后研究了两个多星期,才通过校验,可以正常进行网络请求,不多说了直接上代码
在进行自签名证书之前,需要做一些操作
- 服务器会给你两个证书,一个是root.cer证书(根证书),一个是sub.cer(应该叫什么我也不清楚了,就称为子证书吧);你需要将这两个证书放到钥匙串中,然后在导出 ".cer" 格式的证书,然后在将这两个导出的证书通过openssl生成一个新证书:
openssl s_client -connect www.baidu.com /dev/null | openssl x509 -outform DER > https.cer
然后将这三个证书放到你的项目中,那么前期过程就已经结束了
- 设置程序中的info.plist
(由于我们使用的是自签名的证书,而苹果ATS(App Transport Security)只信任知名CA颁发的证书,所以在iOS9下即使是HTTPS请求还是会被ATS拦截。)
(验证该域名是否通过ats认证 网上有很多,请自行搜索)
NSAppTransportSecurity
NSAllowsArbitraryLoads
NSExceptionDomains
你请求的域名
NSExceptionRequiresForwardSecrecy
NSExceptionMinimumTLSVersion(TLS最低支持版本,需要问你的后台)
TLSv1.2
NSExceptionAllowsInsecureHTTPLoads
- 开始正式验证
func setAlamofireHttps() {
shareSessionManager.delegate.sessionDidReceiveChallenge = { (session: URLSession, challenge: URLAuthenticationChallenge) in
let method = challenge.protectionSpace.authenticationMethod
if method == NSURLAuthenticationMethodServerTrust {
//验证服务器,直接信任或者验证证书二选一,推荐验证证书,更安全
//服务器证书信任
return NetworkRequest.trustServerWithCer(challenge: challenge)
//不校验服务器证书直接信任
// return NetworkRequest.trustServer(challenge: challenge)
} else if method == NSURLAuthenticationMethodClientCertificate {
//认证客户端证书
return NetworkRequest.sendClientCer()
} else {
//其他情况,不通过验证
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)
}
直接信任证书和上传客户端证书由服务器检验方法未进行测试,如有问题,请留言,还望多多指点,
(该方法借鉴多方文档,基本能搜到的文档都查看了,就不一一列举借鉴哪些文档了,多多包涵)