我原来写过一篇文章介绍如何使用证书通过SSL/TLS方式进行网络请求(Swift - 使用URLSession通过HTTPS进行网络请求,及证书的使用),当时用的是 URLSession。本文介绍如何使用 Alamofire 来实现HTTPS网络请求,由于Alamofire就是对URLSession的封装,所以实现起来区别不大。(如果Alamofire的配置使用不了解的,可以先去看看我原来写的文章:Swift - HTTP网络操作库Alamofire使用详解)
一,证书的生成,以及服务器配置
参考我前面写的这篇文章:Tomcat服务器配置https双向认证(使用keytool生成证书)
文章详细介绍了HTTPS,SSL/TLS。还有使用key tool生成自签名证书,Tomcat下https服务的配置。
二,Alamofire使用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不需要):
NSAppTransportSecurity
NSAllowsArbitraryLoads
3,使用两个证书进行双向验证,以及网络请求
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
)
}
}
//数据请求
Alamofire
.request(
"[https://192.168.1.112:8443](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
<
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
Alamofire
class
ViewController
:
UIViewController
{
//自签名网站地址
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
)
}
}
//数据请求
Alamofire
.request(
"[https://192.168.1.112:8443](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
<
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
}
声明
本文转自Swift - 使用Alamofire通过HTTPS进行网络请求,及证书的使用
本人记录下来以防后续需要用到