基于Cloudflare ios 使用swiftHTTP做移动终端双向认证

基于cloudflare做双向认证,上一篇已经说过,这里主要说一下ios终端如何做双向认证或单向认证终端。

ios中,如果需要做双向认证,必须得有一个终端的pkcs7或者pkcs12证书,然后还需要一个服务端的xxxx.cer证书(类似公钥)。

但是cloudflare并没有提供服务端的cer证书,只提供客户端证书。所以,只做单向的客户端认证也可以。其目的都是为了让burpsuite之类的抓包工具无法获取通讯的请求。

具体的设置之前链接中同样设置即可,然后把生成的pkcs12证书放入ios终端代码里,并拖入项目根目录。

然后在xx项目.xcodeproj文件里,找到Build Phases标签的Copy Bundle Resources项,点击添加按钮,导入刚刚的证书文件即可:

然后在ViewController.swift里面写入测试代码,查看能不能通过证书访问测试地址(前提条件是,测试地址已经开启了证书认证),代码如下:

import UIKit
import SwiftHTTP
 
class ViewController: UIViewController {
    let selfSignedHosts = ["sslssl.xxxxxxxx.co"]     //测试地址
    override func viewDidLoad() {
        super.viewDidLoad()
        do {
            let opt = try HTTP.GET("https://sslssl.xxxxxxxx.co")
            opt!.auth = { challenge in
                //认证服务器(这里不使用服务器证书认证,只需地址是我们定义的几个地址即可信任)
                if challenge.protectionSpace.authenticationMethod
                    == NSURLAuthenticationMethodServerTrust
                    && self.selfSignedHosts.contains(challenge.protectionSpace.host) {
                    print("服务器认证!")
                    let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
                    return credential
                }
                //认证客户端证书
                else if challenge.protectionSpace.authenticationMethod
                    == NSURLAuthenticationMethodClientCertificate
                {
                    print("客户端证书认证!")
                    //获取客户端证书相关信息
                    let identityAndTrust:IdentityAndTrust = self.extractIdentity();
                    let urlCredential:URLCredential = URLCredential(
                        identity: identityAndTrust.identityRef,
                        certificates: identityAndTrust.certArray as? [AnyObject],
                        persistence: URLCredential.Persistence.forSession)
                    return urlCredential
                }
                // 其它情况(不接受认证)
                else {
                    print("其它情况(不接受认证)")
                    return nil
                }
            }
            HTTP.GET("https://sslssl.xxxxxxxx.co"){ response in
                print("访问成功,获取数据如下:")
                print(response.text as Any)
            }
        } catch let error {
            print("请求失败:  \(error)")
        }
    }
    //获取客户端证书相关信息
    func extractIdentity() -> IdentityAndTrust {
        var identityAndTrust:IdentityAndTrust!
        var securityError:OSStatus = errSecSuccess
         
        let path: String = Bundle.main.path(forResource: "panda", ofType: "p12")! //证书名字
        let PKCS12Data = NSData(contentsOfFile:path)!
        let key : NSString = kSecImportExportPassphrase as NSString
        let options : NSDictionary = [key : "123123123aaaa"] //客户端证书密码
        var items : CFArray?
        securityError = SecPKCS12Import(PKCS12Data, options, &items)
        if securityError == errSecSuccess {
            let certItems:CFArray = (items as CFArray?)!;
            let certItemsArray:Array = certItems as Array
            let dict:AnyObject? = certItemsArray.first;
            if let certEntry:Dictionary = dict as? Dictionary<String, AnyObject> {
                let identityPointer:AnyObject? = certEntry["identity"]
                let secIdentityRef:SecIdentity = (identityPointer as! SecIdentity?)!
                print("\(String(describing: identityPointer))  :::: \(secIdentityRef)")
                let trustPointer:AnyObject? = certEntry["trust"]
                let trustRef:SecTrust = trustPointer as! SecTrust
                print("\(String(describing: trustPointer))  :::: \(trustRef)")
                let chainPointer:AnyObject? = certEntry["chain"]
                identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef,
                                        trust: trustRef, certArray:  chainPointer!)
            }
        }
        return identityAndTrust;
    }
     
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
//定义一个结构体,存储认证相关信息
struct IdentityAndTrust {
    var identityRef:SecIdentity
    var trust:SecTrust
    var certArray:AnyObject
}

运行结果:

调试过程中,如果需要重新安装或者修改测试地址。需要command+shift+k 清理编译缓存,且删除手机应用重新安装….

证书密码,取决于终端分段存储隐藏的够不够好。