ServerTrustPolicy.swift 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Alamofire.swift
  2. //
  3. // Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. import Foundation
  23. // TODO: DocStrings
  24. public class ServerTrustPolicyManager {
  25. let policies: [String: ServerTrustPolicy]
  26. // TODO: DocStrings
  27. public init(policies: [String: ServerTrustPolicy]) {
  28. self.policies = policies
  29. }
  30. func serverTrustPolicyForHost(host: String) -> ServerTrustPolicy? {
  31. return self.policies[host]
  32. }
  33. }
  34. // MARK: -
  35. extension NSURLSession {
  36. private struct AssociatedKeys {
  37. static var ManagerKey = "NSURLSession.ServerTrustPolicyManager"
  38. }
  39. var serverTrustPolicyManager: ServerTrustPolicyManager? {
  40. get {
  41. return objc_getAssociatedObject(self, &AssociatedKeys.ManagerKey) as? ServerTrustPolicyManager
  42. }
  43. set (manager) {
  44. objc_setAssociatedObject(self, &AssociatedKeys.ManagerKey, manager, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
  45. }
  46. }
  47. }
  48. // MARK: - ServerTrustPolicy
  49. // TODO: DocStrings
  50. public enum ServerTrustPolicy {
  51. case PerformDefaultEvaluation(validateHost: Bool)
  52. case PinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool)
  53. case PinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool)
  54. case DisableEvaluation
  55. case CustomEvaluation((serverTrust: SecTrust, host: String) -> Bool)
  56. // MARK: - Bundle Location
  57. // TODO: DocStrings
  58. public func certificatesInBundle(bundle: NSBundle = NSBundle.mainBundle()) -> [SecCertificate] {
  59. var certificates: [SecCertificate] = []
  60. for path in bundle.pathsForResourcesOfType(".cer", inDirectory: nil) as! [String] {
  61. if let
  62. certificateData = NSData(contentsOfFile: path),
  63. certificate = SecCertificateCreateWithData(nil, certificateData)?.takeRetainedValue()
  64. {
  65. certificates.append(certificate)
  66. }
  67. }
  68. return certificates
  69. }
  70. // TODO: DocStrings
  71. public func publicKeysInBundle(bundle: NSBundle = NSBundle.mainBundle()) -> [SecKey] {
  72. var publicKeys: [SecKey] = []
  73. for certificate in certificatesInBundle(bundle: bundle) {
  74. if let publicKey = publicKeyForCertificate(certificate) {
  75. publicKeys.append(publicKey)
  76. }
  77. }
  78. return publicKeys
  79. }
  80. // MARK: - Evaluation
  81. // TODO: DocStrings
  82. public func evaluateServerTrust(serverTrust: SecTrust, isValidForHost host: String) -> Bool {
  83. var serverTrustIsValid = false
  84. switch self {
  85. case let .PerformDefaultEvaluation(validateHost):
  86. let policy = validateHost ? SecPolicyCreateSSL(1, host as CFString) : SecPolicyCreateBasicX509()
  87. SecTrustSetPolicies(serverTrust, [policy.takeRetainedValue()])
  88. serverTrustIsValid = trustIsValid(serverTrust)
  89. case let .PinCertificates(pinnedCertificates, validateCertificateChain, validateHost):
  90. if validateCertificateChain {
  91. let policy = validateHost ? SecPolicyCreateSSL(1, host as CFString) : SecPolicyCreateBasicX509()
  92. SecTrustSetPolicies(serverTrust, [policy.takeRetainedValue()])
  93. SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates)
  94. SecTrustSetAnchorCertificatesOnly(serverTrust, 1)
  95. serverTrustIsValid = trustIsValid(serverTrust)
  96. } else {
  97. let serverCertificatesDataArray = certificateDataForTrust(serverTrust)
  98. let pinnedCertificatesDataArray = certificateDataForCertificates(pinnedCertificates)
  99. outerLoop: for serverCertificateData in serverCertificatesDataArray {
  100. for pinnedCertificateData in pinnedCertificatesDataArray {
  101. if serverCertificateData.isEqualToData(pinnedCertificateData) {
  102. serverTrustIsValid = true
  103. break outerLoop
  104. }
  105. }
  106. }
  107. }
  108. case let .PinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost):
  109. var certificateChainEvaluationPassed = true
  110. if validateCertificateChain {
  111. let policy = validateHost ? SecPolicyCreateSSL(1, host as CFString) : SecPolicyCreateBasicX509()
  112. SecTrustSetPolicies(serverTrust, [policy.takeRetainedValue()])
  113. certificateChainEvaluationPassed = trustIsValid(serverTrust)
  114. }
  115. if certificateChainEvaluationPassed {
  116. let serverKeys = publicKeysForTrust(serverTrust)
  117. outerLoop: for serverPublicKey in publicKeysForTrust(serverTrust) as [AnyObject] {
  118. for pinnedPublicKey in pinnedPublicKeys as [AnyObject] {
  119. if serverPublicKey.isEqual(pinnedPublicKey) {
  120. serverTrustIsValid = true
  121. break outerLoop
  122. }
  123. }
  124. }
  125. }
  126. case .DisableEvaluation:
  127. serverTrustIsValid = true
  128. case let .CustomEvaluation(closure):
  129. serverTrustIsValid = closure(serverTrust: serverTrust, host: host)
  130. }
  131. return serverTrustIsValid
  132. }
  133. // MARK: - Private - Trust Validation
  134. private func trustIsValid(trust: SecTrust) -> Bool {
  135. var isValid = false
  136. var result = SecTrustResultType(kSecTrustResultInvalid)
  137. let status = SecTrustEvaluate(trust, &result)
  138. if status == errSecSuccess {
  139. let unspecified = SecTrustResultType(kSecTrustResultUnspecified)
  140. let proceed = SecTrustResultType(kSecTrustResultProceed)
  141. isValid = result == unspecified || result == proceed
  142. }
  143. return isValid
  144. }
  145. // MARK: - Private - Certificate Data
  146. private func certificateDataForTrust(trust: SecTrust) -> [NSData] {
  147. var certificates: [SecCertificate] = []
  148. for index in 0..<SecTrustGetCertificateCount(trust) {
  149. let certificate = SecTrustGetCertificateAtIndex(trust, index).takeUnretainedValue()
  150. certificates.append(certificate)
  151. }
  152. return certificateDataForCertificates(certificates)
  153. }
  154. private func certificateDataForCertificates(certificates: [SecCertificate]) -> [NSData] {
  155. return certificates.map { SecCertificateCopyData($0).takeRetainedValue() as NSData }
  156. }
  157. // MARK: - Private - Public Key Extraction
  158. private func publicKeysForTrust(trust: SecTrust) -> [SecKey] {
  159. var publicKeys: [SecKey] = []
  160. for index in 0..<SecTrustGetCertificateCount(trust) {
  161. let certificate = SecTrustGetCertificateAtIndex(trust, index).takeUnretainedValue()
  162. if let publicKey = publicKeyForCertificate(certificate) {
  163. publicKeys.append(publicKey)
  164. }
  165. }
  166. return publicKeys
  167. }
  168. private func publicKeyForCertificate(certificate: SecCertificate) -> SecKey? {
  169. var publicKey: SecKey?
  170. let policy = SecPolicyCreateBasicX509().takeRetainedValue()
  171. var unmanagedTrust: Unmanaged<SecTrust>?
  172. let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &unmanagedTrust)
  173. if let trust = unmanagedTrust?.takeRetainedValue() where trustCreationStatus == errSecSuccess {
  174. publicKey = SecTrustCopyPublicKey(trust).takeRetainedValue()
  175. }
  176. return publicKey
  177. }
  178. }