美文网首页
Swift - 使用Alamofire通过HTTPS进行网络请求

Swift - 使用Alamofire通过HTTPS进行网络请求

作者: yunxiu | 来源:发表于2018-03-11 15:17 被阅读0次

    转自:http://www.hangge.com/blog/cache/detail_1052.html

    (本文代码已升级至Swift3) 

    我原来写过一篇文章介绍如何使用证书通过SSL/TLS方式进行网络请求(Swift - 使用URLSession通过HTTPS进行网络请求,及证书的使用),当时用的是 URLSession。

    本文介绍如何使用 Alamofire 来实现HTTPS网络请求,由于Alamofire就是对URLSession的封装,所以实现起来区别不大。

    (如果Alamofire的配置使用不了解的,可以先去看看我原来写的文章:Swift - HTTP网络操作库Alamofire使用详解

    一,证书的生成,以及服务器配置

    参考我前面写的这篇文章:Tomcat服务器配置https双向认证(使用keytool生成证书)

    文章详细介绍了HTTPS,SSL/TLS。还有使用key tool生成自签名证书,Tomcat下https服务的配置。

    二,Alamofire使用HTTPS进行网络请求

    1,证书导入

    前面文章介绍了通过客户端浏览器访问HTTPS服务需,需要安装“mykey.p12”,“tomcat.cer”这两个证书。同样,我们开发的应用中也需要把这两个证书添加进来。

    记的同时在 “工程” -> “Build Phases” -> “Copy Bundle Resources” 中添加这两个证书文件。

    2,配置Info.plist

    由于我们使用的是自签名的证书,而苹果ATS(App Transport Security)只信任知名CA颁发的证书,所以在iOS9下即使是HTTPS请求还是会被ATS拦截。

    所以在Info.plist下添加如下配置(iOS8不需要):

    3,使用两个证书进行双向验证,以及网络请求

    import UIKit

    import Alamofire

    class ViewController: UIViewController {

        override func viewDidLoad() {

            super.viewDidLoad()

            //认证相关设置

            let manager = SessionManager.default

            manager.delegate.sessionDidReceiveChallenge = { session, challenge in

                //认证服务器证书

                if challenge.protectionSpace.authenticationMethod

                    == NSURLAuthenticationMethodServerTrust {

                    print("服务端证书认证!")

                    let serverTrust:SecTrust = challenge.protectionSpace.serverTrust!

                    let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)!

                    let remoteCertificateData

                        = CFBridgingRetain(SecCertificateCopyData(certificate))!

                    let cerPath = Bundle.main.path(forResource: "tomcat", ofType: "cer")!

                    let cerUrl = URL(fileURLWithPath:cerPath)

                    let localCertificateData = try! Data(contentsOf: cerUrl)

                    if (remoteCertificateData.isEqual(localCertificateData) == true) {

                        let credential = URLCredential(trust: serverTrust)

                        challenge.sender?.use(credential, for: challenge)

                        return (URLSession.AuthChallengeDisposition.useCredential,

                                URLCredential(trust: challenge.protectionSpace.serverTrust!))

                    } else {

                        return (.cancelAuthenticationChallenge, nil)

                    }

                }

                //认证客户端证书

                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 (.useCredential, urlCredential);

                }

                // 其它情况(不接受认证)

                else {

                    print("其它情况(不接受认证)")

                    return (.cancelAuthenticationChallenge, nil)

                }

            }

            //数据请求

            Alamofire.request("https://192.168.1.112:8443")

                .responseString { response in

                    print(response)

            }

        }

        //获取客户端证书相关信息

        func extractIdentity() -> IdentityAndTrust {

            var identityAndTrust:IdentityAndTrust!

            var securityError:OSStatus = errSecSuccess

            let path: String = Bundle.main.path(forResource: "mykey", ofType: "p12")!

            let PKCS12Data = NSData(contentsOfFile:path)!

            let key : NSString = kSecImportExportPassphrase as NSString

            let options : NSDictionary = [key : "123456"] //客户端证书密码

            //create variable for holding security information

            //var privateKeyRef: SecKeyRef? = nil

            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 {

                    // grab the identity

                    let identityPointer:AnyObject? = certEntry["identity"];

                    let secIdentityRef:SecIdentity = identityPointer as! SecIdentity!

                    print("\(identityPointer)  :::: \(secIdentityRef)")

                    // grab the trust

                    let trustPointer:AnyObject? = certEntry["trust"]

                    let trustRef:SecTrust = trustPointer as! SecTrust

                    print("\(trustPointer)  :::: \(trustRef)")

                    // grab the cert

                    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

    }

    控制台打印输出如下:

    4,只使用一个客户端证书

    由于我们使用的是自签名的证书,那么对服务器的认证全由客户端这边判断。也就是说其实使用一个客户端证书“mykey.p12”也是可以的(项目中也只需导入一个证书)。

    当对服务器进行验证的时候,判断服务主机地址是否正确,是的话信任即可(代码高亮部分)

    import UIKit

    import Alamofire

    class ViewController: UIViewController {

        //自签名网站地址

        let selfSignedHosts = ["192.168.1.112", "www.hangge.com"]

        override func viewDidLoad() {

            super.viewDidLoad()

            //认证相关设置

            let manager = SessionManager.default

            manager.delegate.sessionDidReceiveChallenge = { session, challenge in

                //认证服务器(这里不使用服务器证书认证,只需地址是我们定义的几个地址即可信任)

                if challenge.protectionSpace.authenticationMethod

                    == NSURLAuthenticationMethodServerTrust

                    && self.selfSignedHosts.contains(challenge.protectionSpace.host) {

                    print("服务器认证!")

                    let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)

                    return (.useCredential, 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 (.useCredential, urlCredential);

                }

                // 其它情况(不接受认证)

                else {

                    print("其它情况(不接受认证)")

                    return (.cancelAuthenticationChallenge, nil)

                }

            }

            //数据请求

            Alamofire.request("https://192.168.1.112:8443")

                .responseString { response in

                    print(response)

            }

        }

        //获取客户端证书相关信息

        func extractIdentity() -> IdentityAndTrust {

            var identityAndTrust:IdentityAndTrust!

            var securityError:OSStatus = errSecSuccess

            let path: String = Bundle.main.path(forResource: "mykey", ofType: "p12")!

            let PKCS12Data = NSData(contentsOfFile:path)!

            let key : NSString = kSecImportExportPassphrase as NSString

            let options : NSDictionary = [key : "123456"] //客户端证书密码

            //create variable for holding security information

            //var privateKeyRef: SecKeyRef? = nil

            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 {

                    // grab the identity

                    let identityPointer:AnyObject? = certEntry["identity"];

                    let secIdentityRef:SecIdentity = identityPointer as! SecIdentity!

                    print("\(identityPointer)  :::: \(secIdentityRef)")

                    // grab the trust

                    let trustPointer:AnyObject? = certEntry["trust"]

                    let trustRef:SecTrust = trustPointer as! SecTrust

                    print("\(trustPointer)  :::: \(trustRef)")

                    // grab the cert

                    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

    }

    相关文章

      网友评论

          本文标题:Swift - 使用Alamofire通过HTTPS进行网络请求

          本文链接:https://www.haomeiwen.com/subject/nxlwfftx.html