我原來寫過一篇文章介紹如何使用證書通過SSL/TLS方式進(jìn)行網(wǎng)絡(luò)請(qǐng)求(Swift - 使用URLSession通過HTTPS進(jìn)行網(wǎng)絡(luò)請(qǐng)求厕诡,及證書的使用),當(dāng)時(shí)用的是 URLSession卿吐。本文介紹如何使用 Alamofire 來實(shí)現(xiàn)HTTPS網(wǎng)絡(luò)請(qǐng)求,由于Alamofire就是對(duì)URLSession的封裝锋华,所以實(shí)現(xiàn)起來區(qū)別不大嗡官。(如果Alamofire的配置使用不了解的,可以先去看看我原來寫的文章:Swift - HTTP網(wǎng)絡(luò)操作庫(kù)Alamofire使用詳解)
一供置,證書的生成谨湘,以及服務(wù)器配置
參考我前面寫的這篇文章:Tomcat服務(wù)器配置https雙向認(rèn)證(使用keytool生成證書)
文章詳細(xì)介紹了HTTPS,SSL/TLS芥丧。還有使用key tool生成自簽名證書紧阔,Tomcat下https服務(wù)的配置。
二续担,Alamofire使用HTTPS進(jìn)行網(wǎng)絡(luò)請(qǐng)求
1擅耽,證書導(dǎo)入
前面文章介紹了通過客戶端瀏覽器訪問HTTPS服務(wù)需,需要安裝“mykey.p12”物遇,“tomcat.cer”這兩個(gè)證書乖仇。同樣憾儒,我們開發(fā)的應(yīng)用中也需要把這兩個(gè)證書添加進(jìn)來。
記的同時(shí)在 “工程” -> “Build Phases” -> “Copy Bundle Resources” 中添加這兩個(gè)證書文件起趾。
2警儒,配置Info.plist
由于我們使用的是自簽名的證書训裆,而蘋果ATS(App Transport Security)只信任知名CA頒發(fā)的證書,所以在iOS9下即使是HTTPS請(qǐng)求還是會(huì)被ATS攔截蜀铲。
所以在Info.plist下添加如下配置(iOS8不需要):
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
3边琉,使用兩個(gè)證書進(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ù)器證書
if
challenge.protectionSpace.authenticationMethod
==
NSURLAuthenticationMethodServerTrust
{
print
(
"服務(wù)端證書認(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)證客戶端證書
else
if
challenge.protectionSpace.authenticationMethod
==
NSURLAuthenticationMethodClientCertificate
{
print
(
"客戶端證書認(rèn)證变姨!"
)
//獲取客戶端證書相關(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)
}
}
//獲取客戶端證書相關(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"
]
//客戶端證書密碼
//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è)客戶端證書由于我們使用的是自簽名的證書,那么對(duì)服務(wù)器的認(rèn)證全由客戶端這邊判斷蹄衷。也就是說其實(shí)使用一個(gè)客戶端證書“mykey.p12”也是可以的(項(xiàng)目中也只需導(dǎo)入一個(gè)證書)忧额。當(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ù)器證書認(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)證客戶端證書
else
if
challenge.protectionSpace.authenticationMethod
==
NSURLAuthenticationMethodClientCertificate
{
print
(
"客戶端證書認(rèn)證耍属!"
)
//獲取客戶端證書相關(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)
}
}
//獲取客戶端證書相關(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"
]
//客戶端證書密碼
//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通過HTTPS進(jìn)行網(wǎng)絡(luò)請(qǐng)求巩检,及證書的使用
本人記錄下來以防后續(xù)需要用到