Alamofire 5.0 双向认证

双向认证分为两方向的认证:

  1. 客户端认证服务端;
  2. 服务端认证客户端;

以下逐个说明:

认证服务器

认证服务器分为:

  1. DefaultTrustEvaluator,使用默认的验证方式,验证证书的有效性,证书信任链那套
  2. RevocationTrustEvaluator,验证证书是否被吊销
  3. PinnedCertificatesTrustEvaluator,验证证书是否同本地的一致,可以是自签证书
  4. PublicKeysTrustEvaluator,验证证书的公钥,可以是自签证书,不过这个有个好处就是不用关心证书的有效期了
  5. CompositeTrustEvaluator,混合模式
  6. DisabledTrustEvaluator,不验证证书

我们目前使用的是验证公钥,以此为例:

// 验证服务器的公钥
// 默认获取了 cer 结尾的文件,只需要把证书放到项目里边就行了,不需要显示指定
let trustManager = ServerTrustManager(evaluators: [
    M_NodeServerUrl: PublicKeysTrustEvaluator()
])
let session = Session(serverTrustManager: trustManager)
               
// 请求的时候直接使用
session.request(xxxx)...

认证客户端

认证客户端是通过 nsurl 协议来完成的,这部分其实不完全算是 Alamofire 的行为,可以通过继承 SessionDelegate 类,然后重写方法
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 来实现。
具体代码如下:

class WSSessionDelegate: SessionDelegate {
    override func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        switch challenge.protectionSpace.authenticationMethod {
        case NSURLAuthenticationMethodClientCertificate:
        	// 获取证书
            guard let file = Bundle.main.path(forResource: "client", ofType: "p12"),
                  let p12Data = try? Data.init(contentsOf: URL.init(string: file)!) else {
                
                completionHandler(.performDefaultHandling, nil)
                return
            }
            
            let p12Contents = PKCS12(pkcs12Data: p12Data, password: "xxxx")
            guard let identity = p12Contents.identity else {
                completionHandler(.performDefaultHandling, nil)
                return
            }
            
            let credential = URLCredential(identity: identity,
                                           certificates: nil,
                                           persistence: .none)
            challenge.sender?.use(credential, for: challenge)
            completionHandler(.useCredential, credential)
        default:
            completionHandler(.performDefaultHandling, nil)
        }
    }
    
    private class PKCS12 {
        let label: String?
        let keyID: NSData?
        let trust: SecTrust?
        let certChain: [SecTrust]?
        let identity: SecIdentity?
        
        /// Creates a PKCS12 instance from a piece of data.
        /// - Parameters:
        ///   - pkcs12Data: the actual data we want to parse.
        ///   - password: The password required to unlock the PKCS12 data.
        public init(pkcs12Data: Data, password: String) {
            let importPasswordOption: NSDictionary
                = [kSecImportExportPassphrase as NSString: password]
            var items: CFArray?
            let secError: OSStatus
                = SecPKCS12Import(pkcs12Data as NSData,
                                  importPasswordOption, &items)
            guard secError == errSecSuccess else {
                if secError == errSecAuthFailed {
                    NSLog("Incorrect password?")
                    
                }
                fatalError("Error trying to import PKCS12 data")
            }
            guard let theItemsCFArray = items else { fatalError() }
            let theItemsNSArray: NSArray = theItemsCFArray as NSArray
            guard let dictArray
                    = theItemsNSArray as? [[String: AnyObject]] else {
                fatalError()
            }
            func f<T>(key: CFString) -> T? {
                for dict in dictArray {
                    if let value = dict[key as String] as? T {
                        return value
                    }
                }
                return nil
            }
            self.label = f(key: kSecImportItemLabel)
            self.keyID = f(key: kSecImportItemKeyID)
            self.trust = f(key: kSecImportItemTrust)
            self.certChain = f(key: kSecImportItemCertChain)
            self.identity = f(key: kSecImportItemIdentity)
        }
    }
}

let session = Session(delegate: WSSessionDelegate())
// 请求的时候直接使用
session.request(xxxx)...

两个结合起来可以是

let session = Session(delegate: WSSessionDelegate(), serverTrustManager: trustManager)

官方参考文档

你可能感兴趣的:(iOS开发,swift)