這個(gè)文件主要寫(xiě)了如何處理服務(wù)器和客戶(hù)端之間的驗(yàn)證吓懈。
1. ServerTrustPolicyManager
在開(kāi)發(fā)過(guò)程中,我們可能會(huì)請(qǐng)求不同的主機(jī)地址,而一個(gè)服務(wù)器對(duì)應(yīng)一個(gè)信任策略坠七,即ServerTrustPolicy
誊稚,所以需要一個(gè)manager來(lái)管理。
open class ServerTrustPolicyManager {
// 信任策略數(shù)組
open let policies: [String: ServerTrustPolicy]
初始化方法
public init(policies: [String: ServerTrustPolicy]) {
self.policies = policies
}
// 根據(jù)主機(jī)地址返回對(duì)應(yīng)的信任策略
open func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
return policies[host]
}
}
給URLSession
添加serverTrustPolicyManager
倒脓。
extension URLSession {
private struct AssociatedKeys {
static var managerKey = "URLSession.ServerTrustPolicyManager"
}
var serverTrustPolicyManager: ServerTrustPolicyManager? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.managerKey) as? ServerTrustPolicyManager
}
set (manager) {
objc_setAssociatedObject(self, &AssociatedKeys.managerKey, manager, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
2. ServerTrustPolicy
在通過(guò)HTTPS連接服務(wù)器時(shí)渤涌,如果需要認(rèn)證,ServerTrustPolicy
就會(huì)評(píng)估服務(wù)器信任把还,最終決定服務(wù)器信任是否有效和是否建立連接实蓬。
1) 提供了6種評(píng)估機(jī)制
// 執(zhí)行默認(rèn)的評(píng)估
case performDefaultEvaluation(validateHost: Bool)
// 執(zhí)行撤銷(xiāo)評(píng)估
case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags)
// 使用證書(shū)來(lái)驗(yàn)證服務(wù)器信任
case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool)
// 使用公鑰來(lái)驗(yàn)證服務(wù)器信任
case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool)
// 禁用評(píng)估
case disableEvaluation
// 自定義評(píng)估
case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool)
2) 獲取證書(shū)和公鑰
// 獲取證書(shū)
public static func certificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] {
var certificates: [SecCertificate] = []
let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in
bundle.paths(forResourcesOfType: fileExtension, inDirectory: nil)
}.joined())
for path in paths {
if
let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData,
let certificate = SecCertificateCreateWithData(nil, certificateData)
{
certificates.append(certificate)
}
}
return certificates
}
// 從所有證書(shū)中取出所有公鑰
public static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] {
var publicKeys: [SecKey] = []
for certificate in certificates(in: bundle) {
if let publicKey = publicKey(for: certificate) {
publicKeys.append(publicKey)
}
}
return publicKeys
}
3) 評(píng)估服務(wù)器信任
對(duì)于需要驗(yàn)證的評(píng)估機(jī)制,總體思路如下:1)使用SecPolicyCreateSSL
創(chuàng)建SecPolicy
; 2) 使用SecTrustSetPolicies
為SecTrust
設(shè)置安全策略; 3) 驗(yàn)證是否有效
// 評(píng)估服務(wù)器信任是否有效
public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool {
var serverTrustIsValid = false
switch self {
case let .performDefaultEvaluation(validateHost):
let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
SecTrustSetPolicies(serverTrust, policy)
serverTrustIsValid = trustIsValid(serverTrust)
case let .performRevokedEvaluation(validateHost, revocationFlags):
let defaultPolicy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
let revokedPolicy = SecPolicyCreateRevocation(revocationFlags)
SecTrustSetPolicies(serverTrust, [defaultPolicy, revokedPolicy] as CFTypeRef)
serverTrustIsValid = trustIsValid(serverTrust)
case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost):
if validateCertificateChain { // 需要認(rèn)證證書(shū)鏈
let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
SecTrustSetPolicies(serverTrust, policy)
SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray)
SecTrustSetAnchorCertificatesOnly(serverTrust, true)
serverTrustIsValid = trustIsValid(serverTrust)
} else { // 不需要認(rèn)證證書(shū)鏈
let serverCertificatesDataArray = certificateData(for: serverTrust)
let pinnedCertificatesDataArray = certificateData(for: pinnedCertificates)
// outerLoop:給外面這個(gè)for循環(huán)設(shè)置一個(gè)名字
outerLoop: for serverCertificateData in serverCertificatesDataArray {
for pinnedCertificateData in pinnedCertificatesDataArray {
if serverCertificateData == pinnedCertificateData {
// 只要服務(wù)器信任的證書(shū)和指定的證書(shū)有一個(gè)匹配吊履,就驗(yàn)證通過(guò)
serverTrustIsValid = true
break outerLoop
}
}
}
}
case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost):
var certificateChainEvaluationPassed = true
if validateCertificateChain { // 需要認(rèn)證證書(shū)鏈
let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
SecTrustSetPolicies(serverTrust, policy)
certificateChainEvaluationPassed = trustIsValid(serverTrust)
}
if certificateChainEvaluationPassed { // 通過(guò)了證書(shū)鏈認(rèn)證
outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeys(for: serverTrust) as [AnyObject] {
for pinnedPublicKey in pinnedPublicKeys as [AnyObject] {
if serverPublicKey.isEqual(pinnedPublicKey) {
// 只要服務(wù)器信任的公鑰和指定的公鑰相同安皱,則驗(yàn)證通過(guò)
serverTrustIsValid = true
break outerLoop
}
}
}
}
case .disableEvaluation:
serverTrustIsValid = true
case let .customEvaluation(closure):
serverTrustIsValid = closure(serverTrust, host)
}
return serverTrustIsValid
}
// 判斷服務(wù)器信任是否有效
private func trustIsValid(_ trust: SecTrust) -> Bool {
var isValid = false
var result = SecTrustResultType.invalid
let status = SecTrustEvaluate(trust, &result)
if status == errSecSuccess {
let unspecified = SecTrustResultType.unspecified
let proceed = SecTrustResultType.proceed
// 如果結(jié)果是unspecified或者proceed,則認(rèn)為是有效的
isValid = result == unspecified || result == proceed
}
return isValid
}
4) 其他私有的方法
// 提供一個(gè)SecTrust艇炎,返回對(duì)應(yīng)的證書(shū)數(shù)據(jù)
private func certificateData(for trust: SecTrust) -> [Data] {
var certificates: [SecCertificate] = []
for index in 0..<SecTrustGetCertificateCount(trust) {
if let certificate = SecTrustGetCertificateAtIndex(trust, index) {
certificates.append(certificate)
}
}
return certificateData(for: certificates)
}
// 把證書(shū)轉(zhuǎn)化為數(shù)據(jù)
private func certificateData(for certificates: [SecCertificate]) -> [Data] {
return certificates.map { SecCertificateCopyData($0) as Data }
}
// 提供一個(gè)SecTrust酌伊,返回對(duì)應(yīng)的公鑰
private static func publicKeys(for trust: SecTrust) -> [SecKey] {
var publicKeys: [SecKey] = []
for index in 0..<SecTrustGetCertificateCount(trust) {
if
let certificate = SecTrustGetCertificateAtIndex(trust, index),
let publicKey = publicKey(for: certificate)
{
publicKeys.append(publicKey)
}
}
return publicKeys
}
// 從某個(gè)證書(shū)中取出公鑰
private static func publicKey(for certificate: SecCertificate) -> SecKey? {
var publicKey: SecKey?
let policy = SecPolicyCreateBasicX509()
var trust: SecTrust?
let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust)
if let trust = trust, trustCreationStatus == errSecSuccess {
publicKey = SecTrustCopyPublicKey(trust)
}
return publicKey
}
有任何問(wèn)題,歡迎大家留言!
歡迎加入我管理的Swift開(kāi)發(fā)群:536353151
居砖,本群只討論Swift相關(guān)內(nèi)容虹脯。
原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處奏候。謝謝循集!