فهرست منبع

Added the ability to toggle certificate chain validation for certificate pinning.

Christian Noon 10 سال پیش
والد
کامیت
a1ea73189b
3فایلهای تغییر یافته به همراه116 افزوده شده و 23 حذف شده
  1. 38 7
      Source/ServerTrustPolicy.swift
  2. 70 14
      Tests/ServerTrustPolicyTests.swift
  3. 8 2
      Tests/TLSEvaluationTests.swift

+ 38 - 7
Source/ServerTrustPolicy.swift

@@ -58,7 +58,7 @@ extension NSURLSession {
 // TODO: DocStrings
 public enum ServerTrustPolicy {
     case PerformDefaultEvaluation(validateHost: Bool)
-    case PinCertificates(certificates: [SecCertificate], 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)
@@ -106,14 +106,28 @@ public enum ServerTrustPolicy {
             SecTrustSetPolicies(serverTrust, [policy.takeRetainedValue()])
 
             serverTrustIsValid = trustIsValid(serverTrust)
-        case let .PinCertificates(pinnedCertificates, validateHost):
-            let policy = validateHost ? SecPolicyCreateSSL(1, host as CFString) : SecPolicyCreateBasicX509()
-            SecTrustSetPolicies(serverTrust, [policy.takeRetainedValue()])
+        case let .PinCertificates(pinnedCertificates, validateCertificateChain, validateHost):
+            if validateCertificateChain {
+                let policy = validateHost ? SecPolicyCreateSSL(1, host as CFString) : SecPolicyCreateBasicX509()
+                SecTrustSetPolicies(serverTrust, [policy.takeRetainedValue()])
 
-            SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates)
-            SecTrustSetAnchorCertificatesOnly(serverTrust, 1)
+                SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates)
+                SecTrustSetAnchorCertificatesOnly(serverTrust, 1)
 
-            serverTrustIsValid = trustIsValid(serverTrust)
+                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
 
@@ -162,6 +176,23 @@ public enum ServerTrustPolicy {
         return isValid
     }
 
+    // MARK: - Private - Certificate Data
+
+    private func certificateDataForTrust(trust: SecTrust) -> [NSData] {
+        var certificates: [SecCertificate] = []
+
+        for index in 0..<SecTrustGetCertificateCount(trust) {
+            let certificate = SecTrustGetCertificateAtIndex(trust, index).takeUnretainedValue()
+            certificates.append(certificate)
+        }
+
+        return certificateDataForCertificates(certificates)
+    }
+
+    private func certificateDataForCertificates(certificates: [SecCertificate]) -> [NSData] {
+        return certificates.map { SecCertificateCopyData($0).takeRetainedValue() as NSData }
+    }
+
     // MARK: - Private - Public Key Extraction
 
     private func publicKeysForTrust(trust: SecTrust) -> [SecKey] {

+ 70 - 14
Tests/ServerTrustPolicyTests.swift

@@ -582,7 +582,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafValidDNSName.trust
         let certificates = [TestCertificates.LeafValidDNSName]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: false)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: false
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -596,7 +600,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafValidDNSName.trust
         let certificates = [TestCertificates.IntermediateCA2]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: false)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: false
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -610,7 +618,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafValidDNSName.trust
         let certificates = [TestCertificates.RootCA]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: false)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: false
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -624,7 +636,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafValidDNSName.trust
         let certificates = [TestCertificates.LeafSignedByCA2]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: false)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: false
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -638,7 +654,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafValidDNSName.trust
         let certificates = [TestCertificates.IntermediateCA1]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: false)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: false
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -652,7 +672,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafExpired.trust
         let certificates = [TestCertificates.LeafExpired]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: false)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: false
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -666,7 +690,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafExpired.trust
         let certificates = [TestCertificates.IntermediateCA2]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: false)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: false
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -682,7 +710,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafValidDNSName.trust
         let certificates = [TestCertificates.LeafValidDNSName]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: true)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: true
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -696,7 +728,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafValidDNSName.trust
         let certificates = [TestCertificates.IntermediateCA2]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: true)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: true
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -710,7 +746,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafValidDNSName.trust
         let certificates = [TestCertificates.RootCA]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: true)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: true
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -724,7 +764,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafValidDNSName.trust
         let certificates = [TestCertificates.LeafSignedByCA2]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: true)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: true
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -738,7 +782,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafValidDNSName.trust
         let certificates = [TestCertificates.IntermediateCA1]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: true)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: true
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -752,7 +800,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafExpired.trust
         let certificates = [TestCertificates.LeafExpired]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: true)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: true
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)
@@ -766,7 +818,11 @@ class ServerTrustPolicyPinCertificatesTestCase: ServerTrustPolicyTestCase {
         let host = "test.alamofire.org"
         let serverTrust = TestTrusts.LeafExpired.trust
         let certificates = [TestCertificates.IntermediateCA2]
-        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: true)
+        let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
+            certificates: certificates,
+            validateCertificateChain: true,
+            validateHost: true
+        )
 
         // When
         let serverTrustIsValid = serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host)

+ 8 - 2
Tests/TLSEvaluationTests.swift

@@ -126,7 +126,10 @@ class TLSEvaluationExpiredLeafCertificateTestCase: BaseTestCase {
     func testThatExpiredCertificateRequestFailsWhenPinningLeafCertificate() {
         // Given
         let certificates = [TestCertificates.Leaf]
-        let policies = [self.host: ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: true)]
+        let policies: [String: ServerTrustPolicy] = [
+            self.host: .PinCertificates(certificates: certificates, validateCertificateChain: true, validateHost: true)
+        ]
+
         let manager = Manager(
             configuration: self.configuration,
             serverTrustPolicyManager: ServerTrustPolicyManager(policies: policies)
@@ -152,7 +155,10 @@ class TLSEvaluationExpiredLeafCertificateTestCase: BaseTestCase {
     func testThatExpiredCertificateRequestFailsWhenPinningAllCertificates() {
         // Given
         let certificates = [TestCertificates.Leaf, TestCertificates.IntermedateCA, TestCertificates.RootCA]
-        let policies = [self.host: ServerTrustPolicy.PinCertificates(certificates: certificates, validateHost: true)]
+        let policies: [String: ServerTrustPolicy] = [
+            self.host: .PinCertificates(certificates: certificates, validateCertificateChain: true, validateHost: true)
+        ]
+
         let manager = Manager(
             configuration: self.configuration,
             serverTrustPolicyManager: ServerTrustPolicyManager(policies: policies)