Swift - 使用SwiftHTTP通过HTTPS进行网络请求,及证书的使用
作者:hangge | 2016-02-07 15:13
(本文代码已升级至Swift3)
我原来写过两篇文章介绍如何使用证书通过SSL/TLS方式进行网络请求,这两篇文章分别使用的是 URLSession 和 Alamofire。
本文介绍如何使用 SwiftHTTP 来实现HTTPS网络请求,由于SwiftHTTP就是对URLSession的封装,所以实现起来区别不大。
(如果SwiftHTTP的配置使用不了解的,可以先去看看我原来写的文章:Swift - HTTP网络操作库SwiftHTTP使用详解)


3,使用两个证书进行双向验证,以及网络请求
4,只使用一个客户端证书
由于我们使用的是自签名的证书,那么对服务器的认证全由客户端这边判断。也就是说其实使用一个客户端证书“mykey.p12”也是可以的(项目中也只需导入一个证书)。
当对服务器进行验证的时候,判断服务主机地址是否正确,是的话信任即可(代码高亮部分)
我原来写过两篇文章介绍如何使用证书通过SSL/TLS方式进行网络请求,这两篇文章分别使用的是 URLSession 和 Alamofire。
本文介绍如何使用 SwiftHTTP 来实现HTTPS网络请求,由于SwiftHTTP就是对URLSession的封装,所以实现起来区别不大。
(如果SwiftHTTP的配置使用不了解的,可以先去看看我原来写的文章:Swift - HTTP网络操作库SwiftHTTP使用详解)
一,证书的生成,以及服务器配置
参考我前面写的这篇文章:Tomcat服务器配置https双向认证(使用keytool生成证书)
文章详细介绍了HTTPS,SSL/TLS。还有使用key tool生成自签名证书,Tomcat下https服务的配置。
二,SwiftHTTP使用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不需要):
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
3,使用两个证书进行双向验证,以及网络请求
import UIKit
import SwiftHTTP
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
do {
let opt = try HTTP.GET("https://192.168.1.112:8443")
opt.auth = { 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 URLCredential(trust: challenge.protectionSpace.serverTrust!)
} else {
return 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 urlCredential
}
// 其它情况(不接受认证)
else {
print("其它情况(不接受认证)")
return nil
}
}
opt.start { response in
print("访问成功,获取数据如下:")
print(response.text)
}
} catch let error {
print("请求失败: \(error)")
}
}
//获取客户端证书相关信息
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;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
//定义一个结构体,存储认证相关信息
struct IdentityAndTrust {
var identityRef:SecIdentity
var trust:SecTrust
var certArray:AnyObject
}
控制台打印输出如下:

4,只使用一个客户端证书
由于我们使用的是自签名的证书,那么对服务器的认证全由客户端这边判断。也就是说其实使用一个客户端证书“mykey.p12”也是可以的(项目中也只需导入一个证书)。
当对服务器进行验证的时候,判断服务主机地址是否正确,是的话信任即可(代码高亮部分)
import UIKit
import SwiftHTTP
class ViewController: UIViewController {
//自签名网站地址
let selfSignedHosts = ["192.168.1.112", "www.hangge.com"]
override func viewDidLoad() {
super.viewDidLoad()
do {
let opt = try HTTP.GET("https://192.168.1.112:8443")
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
}
}
opt.start { response in
print("访问成功,获取数据如下:")
print(response.text)
}
} catch let error {
print("请求失败: \(error)")
}
}
//获取客户端证书相关信息
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;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
//定义一个结构体,存储认证相关信息
struct IdentityAndTrust {
var identityRef:SecIdentity
var trust:SecTrust
var certArray:AnyObject
}
全部评论(0)