Alamofire HTTPS自签名证书校验

根据项目需求,需要进行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"
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用OkHttp时,如果我们想要忽略HTTPS证书校验,可以通过自定义一个用于信任所有证书的信任管理器来实现。 首先,我们需要创建一个TrustManager的实现类,该类用于信任所有的证书。可以通过重写checkClientTrusted和checkServerTrusted方法来实现,这两个方法在验证客户端和服务器端证书时被调用,我们可以在这里不进行任何校验直接返回一个空的链表。 接下来,我们需要创建一个SSLContext对象,并使用我们自定义的TrustManager进行初始化。然后,使用这个SSLContext对象创建一个SSLSocketFactory对象。 最后,我们将这个SSLSocketFactory对象设置到OkHttpClient中的SSL socket factory中,这样OkHttp在发起HTTPS请求时就会忽略证书校验了。 下面是一个示例代码: ```java // 创建信任所有证书的TrustManager X509TrustManager trustManager = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { // 可以不进行任何校验直接返回 } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { // 可以不进行任何校验直接返回 } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }; try { // 创建一个SSLContext并使用我们的TrustManager初始化 SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{trustManager}, null); // 创建SSLSocketFactory SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(sslSocketFactory, trustManager) .build(); // 使用client发起HTTPS请求 // ... } catch (Exception e) { e.printStackTrace(); } ``` 需要注意的是,忽略HTTPS证书校验存在一定的安全风险,因此建议仅在开发环境中使用,并在正式环境中使用正规的证书进行校验

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值