轉自:http://www.hangge.com/blog/cache/detail_1052.html
(本文代碼已升級至Swift3)?
我原來寫過一篇文章介紹如何使用證書通過SSL/TLS方式進行網(wǎng)絡請求(Swift - 使用URLSession通過HTTPS進行網(wǎng)絡請求,及證書的使用),當時用的是?URLSession。
本文介紹如何使用?Alamofire?來實現(xiàn)HTTPS網(wǎng)絡請求,由于Alamofire就是對URLSession的封裝,所以實現(xiàn)起來區(qū)別不大。
(如果Alamofire的配置使用不了解的,可以先去看看我原來寫的文章:Swift - HTTP網(wǎng)絡操作庫Alamofire使用詳解)
一,證書的生成,以及服務器配置
參考我前面寫的這篇文章:Tomcat服務器配置https雙向認證(使用keytool生成證書)
文章詳細介紹了HTTPS,SSL/TLS。還有使用key tool生成自簽名證書,Tomcat下https服務的配置。
二,Alamofire使用HTTPS進行網(wǎng)絡請求
1,證書導入
前面文章介紹了通過客戶端瀏覽器訪問HTTPS服務需,需要安裝“mykey.p12”,“tomcat.cer”這兩個證書。同樣,我們開發(fā)的應用中也需要把這兩個證書添加進來。
記的同時在 “工程” -> “Build Phases” -> “Copy Bundle Resources” 中添加這兩個證書文件。
2,配置Info.plist
由于我們使用的是自簽名的證書,而蘋果ATS(App Transport Security)只信任知名CA頒發(fā)的證書,所以在iOS9下即使是HTTPS請求還是會被ATS攔截。
所以在Info.plist下添加如下配置(iOS8不需要):
3,使用兩個證書進行雙向驗證,以及網(wǎng)絡請求
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)
????????????}
????????}
????????//數(shù)據(jù)請求
????????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?{
????//自簽名網(wǎng)站地址
????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)
????????????}
????????}
????????//數(shù)據(jù)請求
????????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
}