123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- import Foundation
- public class ServerTrustPolicyManager {
-
- public let policies: [String: ServerTrustPolicy]
-
- public init(policies: [String: ServerTrustPolicy]) {
- self.policies = policies
- }
-
- public func serverTrustPolicyForHost(host: String) -> ServerTrustPolicy? {
- return policies[host]
- }
- }
- extension NSURLSession {
- private struct AssociatedKeys {
- static var ManagerKey = "NSURLSession.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)
- }
- }
- }
- public enum ServerTrustPolicy {
- case PerformDefaultEvaluation(validateHost: Bool)
- case PinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool)
- case PinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool)
- case DisableEvaluation
- case CustomEvaluation((serverTrust: SecTrust, host: String) -> Bool)
-
-
- public static func certificatesInBundle(bundle: NSBundle = NSBundle.mainBundle()) -> [SecCertificate] {
- var certificates: [SecCertificate] = []
- let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in
- bundle.pathsForResourcesOfType(fileExtension, inDirectory: nil)
- }.flatten())
- for path in paths {
- if let
- certificateData = NSData(contentsOfFile: path),
- certificate = SecCertificateCreateWithData(nil, certificateData)
- {
- certificates.append(certificate)
- }
- }
- return certificates
- }
-
- public static func publicKeysInBundle(bundle: NSBundle = NSBundle.mainBundle()) -> [SecKey] {
- var publicKeys: [SecKey] = []
- for certificate in certificatesInBundle(bundle) {
- if let publicKey = publicKeyForCertificate(certificate) {
- publicKeys.append(publicKey)
- }
- }
- return publicKeys
- }
-
-
- public func evaluateServerTrust(serverTrust: SecTrust, isValidForHost 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 .PinCertificates(pinnedCertificates, validateCertificateChain, validateHost):
- if validateCertificateChain {
- let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
- SecTrustSetPolicies(serverTrust, [policy])
- SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates)
- SecTrustSetAnchorCertificatesOnly(serverTrust, true)
- serverTrustIsValid = trustIsValid(serverTrust)
- } else {
- let serverCertificatesDataArray = certificateDataForTrust(serverTrust)
- let pinnedCertificatesDataArray = certificateDataForCertificates(pinnedCertificates)
- outerLoop: for serverCertificateData in serverCertificatesDataArray {
- for pinnedCertificateData in pinnedCertificatesDataArray {
- if serverCertificateData.isEqualToData(pinnedCertificateData) {
- serverTrustIsValid = true
- break outerLoop
- }
- }
- }
- }
- case let .PinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost):
- var certificateChainEvaluationPassed = true
- if validateCertificateChain {
- let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
- SecTrustSetPolicies(serverTrust, [policy])
- certificateChainEvaluationPassed = trustIsValid(serverTrust)
- }
- if certificateChainEvaluationPassed {
- outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeysForTrust(serverTrust) as [AnyObject] {
- for pinnedPublicKey in pinnedPublicKeys as [AnyObject] {
- if serverPublicKey.isEqual(pinnedPublicKey) {
- serverTrustIsValid = true
- break outerLoop
- }
- }
- }
- }
- case .DisableEvaluation:
- serverTrustIsValid = true
- case let .CustomEvaluation(closure):
- serverTrustIsValid = closure(serverTrust: serverTrust, host: host)
- }
- return serverTrustIsValid
- }
-
- private func trustIsValid(trust: SecTrust) -> Bool {
- var isValid = false
- var result = SecTrustResultType(kSecTrustResultInvalid)
- let status = SecTrustEvaluate(trust, &result)
- if status == errSecSuccess {
- let unspecified = SecTrustResultType(kSecTrustResultUnspecified)
- let proceed = SecTrustResultType(kSecTrustResultProceed)
- isValid = result == unspecified || result == proceed
- }
- return isValid
- }
-
- private func certificateDataForTrust(trust: SecTrust) -> [NSData] {
- var certificates: [SecCertificate] = []
- for index in 0..<SecTrustGetCertificateCount(trust) {
- if let certificate = SecTrustGetCertificateAtIndex(trust, index) {
- certificates.append(certificate)
- }
- }
- return certificateDataForCertificates(certificates)
- }
- private func certificateDataForCertificates(certificates: [SecCertificate]) -> [NSData] {
- return certificates.map { SecCertificateCopyData($0) as NSData }
- }
-
- private static func publicKeysForTrust(trust: SecTrust) -> [SecKey] {
- var publicKeys: [SecKey] = []
- for index in 0..<SecTrustGetCertificateCount(trust) {
- if let
- certificate = SecTrustGetCertificateAtIndex(trust, index),
- publicKey = publicKeyForCertificate(certificate)
- {
- publicKeys.append(publicKey)
- }
- }
- return publicKeys
- }
- private static func publicKeyForCertificate(certificate: SecCertificate) -> SecKey? {
- var publicKey: SecKey?
- let policy = SecPolicyCreateBasicX509()
- var trust: SecTrust?
- let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust)
- if let trust = trust where trustCreationStatus == errSecSuccess {
- publicKey = SecTrustCopyPublicKey(trust)
- }
- return publicKey
- }
- }
|