我原來(lái)寫(xiě)過(guò)一篇文章介紹如何使用證書(shū)通過(guò)SSL/TLS方式進(jìn)行網(wǎng)絡(luò)請(qǐng)求(Swift - 使用URLSession通過(guò)HTTPS進(jìn)行網(wǎng)絡(luò)請(qǐng)求,及證書(shū)的使用),當(dāng)時(shí)用的是 URLSession。本文介紹如何使用 Alamofire 來(lái)實(shí)現(xiàn)HTTPS網(wǎng)絡(luò)請(qǐng)求,由于Alamofire就是對(duì)URLSession的封裝,所以實(shí)現(xiàn)起來(lái)區(qū)別不大。(如果Alamofire的配置使用不了解的,可以先去看看我原來(lái)寫(xiě)的文章:Swift - HTTP網(wǎng)絡(luò)操作庫(kù)Alamofire使用詳解)
一,證書(shū)的生成,以及服務(wù)器配置
參考我前面寫(xiě)的這篇文章:Tomcat服務(wù)器配置https雙向認(rèn)證(使用keytool生成證書(shū))
文章詳細(xì)介紹了HTTPS,SSL/TLS。還有使用key tool生成自簽名證書(shū),Tomcat下https服務(wù)的配置。
二,Alamofire使用HTTPS進(jìn)行網(wǎng)絡(luò)請(qǐng)求
1,證書(shū)導(dǎo)入
前面文章介紹了通過(guò)客戶端瀏覽器訪問(wèn)HTTPS服務(wù)需,需要安裝“mykey.p12”,“tomcat.cer”這兩個(gè)證書(shū)。同樣,我們開(kāi)發(fā)的應(yīng)用中也需要把這兩個(gè)證書(shū)添加進(jìn)來(lái)。
記的同時(shí)在 “工程” -> “Build Phases” -> “Copy Bundle Resources” 中添加這兩個(gè)證書(shū)文件。
2,配置Info.plist
由于我們使用的是自簽名的證書(shū),而蘋(píng)果ATS(App Transport Security)只信任知名CA頒發(fā)的證書(shū),所以在iOS9下即使是HTTPS請(qǐng)求還是會(huì)被ATS攔截。
所以在Info.plist下添加如下配置(iOS8不需要):
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
3,使用兩個(gè)證書(shū)進(jìn)行雙向驗(yàn)證,以及網(wǎng)絡(luò)請(qǐng)求
import
UIKit
import
Alamofire
class
ViewController
:
UIViewController
{
override
func
viewDidLoad() {
super
.viewDidLoad()
//認(rèn)證相關(guān)設(shè)置
let
manager =
SessionManager
.
default
manager.delegate.sessionDidReceiveChallenge = { session, challenge
in
//認(rèn)證服務(wù)器證書(shū)
if
challenge.protectionSpace.authenticationMethod
==
NSURLAuthenticationMethodServerTrust
{
print
(
"服務(wù)端證書(shū)認(rèn)證!"
)
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
)
}
}
//認(rèn)證客戶端證書(shū)
else
if
challenge.protectionSpace.authenticationMethod
==
NSURLAuthenticationMethodClientCertificate
{
print
(
"客戶端證書(shū)認(rèn)證!"
)
//獲取客戶端證書(shū)相關(guān)信息
let
identityAndTrust:
IdentityAndTrust
=
self
.extractIdentity();
let
urlCredential:
URLCredential
=
URLCredential
(
identity: identityAndTrust.identityRef,
certificates: identityAndTrust.certArray
as
? [
AnyObject
],
persistence:
URLCredential
.
Persistence
.forSession);
return
(.useCredential, urlCredential);
}
// 其它情況(不接受認(rèn)證)
else
{
print
(
"其它情況(不接受認(rèn)證)"
)
return
(.cancelAuthenticationChallenge,
nil
)
}
}
//數(shù)據(jù)請(qǐng)求
Alamofire
.request(
"[https://192.168.1.112:8443](https://192.168.1.112:8443/)"
)
.responseString { response
in
print
(response)
}
}
//獲取客戶端證書(shū)相關(guān)信息
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"
]
//客戶端證書(shū)密碼
//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()
}
}
//定義一個(gè)結(jié)構(gòu)體,存儲(chǔ)認(rèn)證相關(guān)信息
struct
IdentityAndTrust
{
var
identityRef:
SecIdentity
var
trust:
SecTrust
var
certArray:
AnyObject
}
4,只使用一個(gè)客戶端證書(shū)由于我們使用的是自簽名的證書(shū),那么對(duì)服務(wù)器的認(rèn)證全由客戶端這邊判斷。也就是說(shuō)其實(shí)使用一個(gè)客戶端證書(shū)“mykey.p12”也是可以的(項(xiàng)目中也只需導(dǎo)入一個(gè)證書(shū))。當(dāng)對(duì)服務(wù)器進(jìn)行驗(yàn)證的時(shí)候,判斷服務(wù)主機(jī)地址是否正確,是的話信任即可(代碼高亮部分)
import
UIKit
import
Alamofire
class
ViewController
:
UIViewController
{
//自簽名網(wǎng)站地址
let
selfSignedHosts = [
"192.168.1.112"
,
"www.hangge.com"
]
override
func
viewDidLoad() {
super
.viewDidLoad()
//認(rèn)證相關(guān)設(shè)置
let
manager =
SessionManager
.
default
manager.delegate.sessionDidReceiveChallenge = { session, challenge
in
//認(rèn)證服務(wù)器(這里不使用服務(wù)器證書(shū)認(rèn)證,只需地址是我們定義的幾個(gè)地址即可信任)
if
challenge.protectionSpace.authenticationMethod
==
NSURLAuthenticationMethodServerTrust
&&
self
.selfSignedHosts.contains(challenge.protectionSpace.host) {
print
(
"服務(wù)器認(rèn)證!"
)
let
credential =
URLCredential
(trust: challenge.protectionSpace.serverTrust!)
return
(.useCredential, credential)
}
//認(rèn)證客戶端證書(shū)
else
if
challenge.protectionSpace.authenticationMethod
==
NSURLAuthenticationMethodClientCertificate
{
print
(
"客戶端證書(shū)認(rèn)證!"
)
//獲取客戶端證書(shū)相關(guān)信息
let
identityAndTrust:
IdentityAndTrust
=
self
.extractIdentity();
let
urlCredential:
URLCredential
=
URLCredential
(
identity: identityAndTrust.identityRef,
certificates: identityAndTrust.certArray
as
? [
AnyObject
],
persistence:
URLCredential
.
Persistence
.forSession);
return
(.useCredential, urlCredential);
}
// 其它情況(不接受認(rèn)證)
else
{
print
(
"其它情況(不接受認(rèn)證)"
)
return
(.cancelAuthenticationChallenge,
nil
)
}
}
//數(shù)據(jù)請(qǐng)求
Alamofire
.request(
"[https://192.168.1.112:8443](https://192.168.1.112:8443/)"
)
.responseString { response
in
print
(response)
}
}
//獲取客戶端證書(shū)相關(guān)信息
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"
]
//客戶端證書(shū)密碼
//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()
}
}
//定義一個(gè)結(jié)構(gòu)體,存儲(chǔ)認(rèn)證相關(guān)信息
struct IdentityAndTrust
{
var identityRef: SecIdentity
var trust: SecTrust
var certArray: AnyObject
}
聲明
本文轉(zhuǎn)自Swift - 使用Alamofire通過(guò)HTTPS進(jìn)行網(wǎng)絡(luò)請(qǐng)求,及證書(shū)的使用
本人記錄下來(lái)以防后續(xù)需要用到


