Swift - 使用URLSession通过HTTPS进行网络请求,及证书的使用
(本文代码已升级至Swift3)
3,使用两个证书进行双向验证,以及网络请求
控制台打印输出如下:
4,只使用一个客户端证书
一,证书的生成,以及服务器配置
参考我前面写的这篇文章:Tomcat服务器配置https双向认证(使用keytool生成证书)
文章详细介绍了HTTPS,SSL/TLS。还有使用key tool生成自签名证书,Tomcat下https服务的配置。
二,iOS下APP使用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不需要):
1 2 3 4 5 | < key >NSAppTransportSecurity</ key > < dict > < key >NSAllowsArbitraryLoads</ key > < true /> </ dict > |
3,使用两个证书进行双向验证,以及网络请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | import UIKit import Foundation class ViewController : UIViewController , URLSessionDelegate { override func viewDidLoad() { super .viewDidLoad() //获取数据 } // 使用URLSession请求数据 func httpGet(request: URLRequest ) { let configuration = URLSessionConfiguration . default let session = URLSession (configuration: configuration, delegate: self , delegateQueue: OperationQueue .main) let dataTask = session.dataTask(with: request, completionHandler: {(data, response, error) -> Void in if error != nil { print (error?.localizedDescription) } else { let str = String (data: data!, encoding: String . Encoding .utf8) print ( "访问成功,获取数据如下:" ) print (str) } }) //使用resume方法启动任务 dataTask.resume() } // 在访问资源的时候,如果服务器返回需要授权(提供一个URLCredential对象) // 那么该方法就回被调用(这个是URLSessionDelegate代理方法) func urlSession(_ session: URLSession , didReceive challenge: URLAuthenticationChallenge , completionHandler: @escaping ( URLSession . AuthChallengeDisposition , URLCredential ?) -> Void ) { //认证服务器证书 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) completionHandler( URLSession . AuthChallengeDisposition .useCredential, URLCredential (trust: challenge.protectionSpace.serverTrust!)) } else { completionHandler(.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) completionHandler(.useCredential, urlCredential) } // 其它情况(不接受认证) else { print ( "其它情况(不接受认证)" ) completionHandler(.cancelAuthenticationChallenge, nil ); } } //获取客户端证书相关信息 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 < String , AnyObject > { // 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; } } //定义一个结构体,存储认证相关信息 struct IdentityAndTrust { var identityRef: SecIdentity var trust: SecTrust var certArray: AnyObject } |
由于我们使用的是自签名的证书,那么对服务器的认证全由客户端这边判断。也就是说其实使用一个客户端证书“mykey.p12”也是可以的(项目中也只需导入一个证书)。
当对服务器进行验证的时候,判断服务主机地址是否正确,是的话信任即可(代码高亮部分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | import UIKit import Foundation class ViewController : UIViewController , URLSessionDelegate { //自签名网站地址 let selfSignedHosts = [ "192.168.1.112" , "www.hangge.com" ] override func viewDidLoad() { super .viewDidLoad() //获取数据 } // 使用URLSession请求数据 func httpGet(request: URLRequest ) { let configuration = URLSessionConfiguration . default let session = URLSession (configuration: configuration, delegate: self , delegateQueue: OperationQueue .main) let dataTask = session.dataTask(with: request, completionHandler: {(data, response, error) -> Void in if error != nil { print (error?.localizedDescription) } else { let str = String (data: data!, encoding: String . Encoding .utf8) print ( "访问成功,获取数据如下:" ) print (str) } }) //使用resume方法启动任务 dataTask.resume() } // 在访问资源的时候,如果服务器返回需要授权(提供一个URLCredential对象) // 那么该方法就回被调用(这个是URLSessionDelegate代理方法) func urlSession(_ session: URLSession , didReceive challenge: URLAuthenticationChallenge , completionHandler: @escaping ( URLSession . AuthChallengeDisposition , URLCredential ?) -> Void ) { //认证服务器(这里不使用服务器证书认证,只需地址是我们定义的几个地址即可信任) if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust && self .selfSignedHosts.contains(challenge.protectionSpace.host) { print ( "服务器认证!" ) let credential = URLCredential (trust: challenge.protectionSpace.serverTrust!) completionHandler(.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) completionHandler(.useCredential, urlCredential) } // 其它情况(不接受认证) else { print ( "其它情况(不接受认证)" ) completionHandler(.cancelAuthenticationChallenge, nil ); } } //获取客户端证书相关信息 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 < String , AnyObject > { // 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; } } //定义一个结构体,存储认证相关信息 struct IdentityAndTrust { var identityRef: SecIdentity var trust: SecTrust var certArray: AnyObject } |