ServerTrustPolicy.swift 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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. // TODO: DocStrings
  31. public func serverTrustPolicyForHost(host: String) -> ServerTrustPolicy? {
  32. return self.policies[host]
  33. }
  34. }
  35. // MARK: -
  36. extension NSURLSession {
  37. private struct AssociatedKeys {
  38. static var ManagerKey = "NSURLSession.ServerTrustPolicyManager"
  39. }
  40. var serverTrustPolicyManager: ServerTrustPolicyManager? {
  41. get {
  42. return objc_getAssociatedObject(self, &AssociatedKeys.ManagerKey) as? ServerTrustPolicyManager
  43. }
  44. set (manager) {
  45. objc_setAssociatedObject(self, &AssociatedKeys.ManagerKey, manager, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
  46. }
  47. }
  48. }
  49. // MARK: - ServerTrustPolicy
  50. // TODO: DocStrings
  51. public enum ServerTrustPolicy {
  52. case PerformDefaultEvaluation(validateHost: Bool)
  53. case PinCertificates(certificates: [SecCertificate], validateHost: Bool)
  54. case PinPublicKeys(publicKeys: [SecKey], validateHost: Bool, allowInvalidCertificates: Bool)
  55. case DisableEvaluation
  56. case CustomEvaluation((serverTrust: SecTrust, host: String) -> Bool)
  57. // MARK: - Bundle Location
  58. // TODO: DocStrings
  59. public func certificatesInBundle(bundle: NSBundle = NSBundle.mainBundle()) -> [SecCertificate] {
  60. var certificates: [SecCertificate] = []
  61. for path in bundle.pathsForResourcesOfType(".cer", inDirectory: nil) as! [String] {
  62. if let
  63. certificateData = NSData(contentsOfFile: path),
  64. certificate = SecCertificateCreateWithData(nil, certificateData)?.takeRetainedValue()
  65. {
  66. certificates.append(certificate)
  67. }
  68. }
  69. return certificates
  70. }
  71. // TODO: DocStrings
  72. public func publicKeysInBundle(bundle: NSBundle = NSBundle.mainBundle()) -> [SecKey] {
  73. var publicKeys: [SecKey] = []
  74. for certificate in certificatesInBundle(bundle: bundle) {
  75. if let publicKey = publicKeyForCertificate(certificate) {
  76. publicKeys.append(publicKey)
  77. }
  78. }
  79. return publicKeys
  80. }
  81. // MARK: - Evaluation
  82. // TODO: DocStrings
  83. public func evaluateServerTrust(serverTrust: SecTrust, isValidForHost host: String) -> Bool {
  84. var serverTrustIsValid = false
  85. switch self {
  86. case let .PerformDefaultEvaluation(validateHost):
  87. let policy = validateHost ? SecPolicyCreateSSL(1, host as CFString) : SecPolicyCreateBasicX509()
  88. SecTrustSetPolicies(serverTrust, [policy.takeRetainedValue()])
  89. serverTrustIsValid = trustIsValid(serverTrust)
  90. case let .PinCertificates(pinnedCertificates, validateHost):
  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. case let .PinPublicKeys(pinnedPublicKeys, validateHost, allowInvalidCertificates):
  97. var certificateChainEvaluationPassed = true
  98. if !allowInvalidCertificates {
  99. let policy = validateHost ? SecPolicyCreateSSL(1, host as CFString) : SecPolicyCreateBasicX509()
  100. SecTrustSetPolicies(serverTrust, [policy.takeRetainedValue()])
  101. certificateChainEvaluationPassed = trustIsValid(serverTrust)
  102. }
  103. if certificateChainEvaluationPassed {
  104. let serverKeys = publicKeysForTrust(serverTrust)
  105. outerLoop: for serverPublicKey in publicKeysForTrust(serverTrust) as [AnyObject] {
  106. for pinnedPublicKey in pinnedPublicKeys as [AnyObject] {
  107. if serverPublicKey.isEqual(pinnedPublicKey) {
  108. serverTrustIsValid = true
  109. break outerLoop
  110. }
  111. }
  112. }
  113. }
  114. case .DisableEvaluation:
  115. serverTrustIsValid = true
  116. case let .CustomEvaluation(closure):
  117. serverTrustIsValid = closure(serverTrust: serverTrust, host: host)
  118. }
  119. return serverTrustIsValid
  120. }
  121. // MARK: - Private - Trust Validation
  122. private func trustIsValid(trust: SecTrust) -> Bool {
  123. var isValid = false
  124. var result = SecTrustResultType(kSecTrustResultInvalid)
  125. let status = SecTrustEvaluate(trust, &result)
  126. if status == errSecSuccess {
  127. let unspecified = SecTrustResultType(kSecTrustResultUnspecified)
  128. let proceed = SecTrustResultType(kSecTrustResultProceed)
  129. isValid = result == unspecified || result == proceed
  130. }
  131. return isValid
  132. }
  133. // MARK: - Private - Public Key Extraction
  134. private func publicKeysForTrust(trust: SecTrust) -> [SecKey] {
  135. var publicKeys: [SecKey] = []
  136. for index in 0..<SecTrustGetCertificateCount(trust) {
  137. let certificate = SecTrustGetCertificateAtIndex(trust, index).takeUnretainedValue()
  138. if let publicKey = publicKeyForCertificate(certificate) {
  139. publicKeys.append(publicKey)
  140. }
  141. }
  142. return publicKeys
  143. }
  144. private func publicKeyForCertificate(certificate: SecCertificate) -> SecKey? {
  145. var publicKey: SecKey?
  146. let policy = SecPolicyCreateBasicX509().takeRetainedValue()
  147. var unmanagedTrust: Unmanaged<SecTrust>?
  148. let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &unmanagedTrust)
  149. if let trust = unmanagedTrust?.takeRetainedValue() where trustCreationStatus == errSecSuccess {
  150. publicKey = SecTrustCopyPublicKey(trust).takeRetainedValue()
  151. }
  152. return publicKey
  153. }
  154. }