Skip to content
This repository has been archived by the owner on Jan 17, 2023. It is now read-only.

Fixed certificate validation for servers providing incomplete chains #3159

Conversation

p4checo
Copy link
Contributor

@p4checo p4checo commented Nov 13, 2015

Some servers send incomplete certificate chains (without the Root CA), which
cause issues in the latest version's (2.6.3) certificate pinning mechanism.
In these cases, when AFSecurityPolicys -evaluateServerTrust: invokes
AFCertificateTrustChainForServerTrust(serverTrust) (prior to setting the
anchor), the Root CA isn't present (the server didn't send it). Afterwards,
even though AFServerTrustIsValid(serverTrust) succeeds, the next validation
fails since no pinned certificate matches any of the serverCertificates.

By fetching the AFCertificateTrustChainForServerTrust(serverTrust) after
the AFServerTrustIsValid(serverTrust) validation, the complete chain is
obtained and the Root CAs match (pinned vs validated).

A slight optimization was also made when validating if the serverTrust
certificates contain the pinnedCertificates, looping in reverse order and
returning after finding the first match.

Follow up of PR #2921, but rebased for 3.0.0

@kcharwood kcharwood added this to the 3.0.0 milestone Nov 13, 2015
@@ -388,6 +418,28 @@ - (void)testPolicyWithCertificatePinningAllowsHTTPBinOrgServerTrustWithHTTPBinOr
XCTAssertTrue([policy evaluateServerTrust:AFUTHTTPBinOrgServerTrust() forDomain:@"httpbin.org"], @"Policy should allow server trust");
}

- (void)testPolicyWithCertificatePinningAllowsGoogleComServerTrustIncompleteChainWithRootCertificatePinnedAndValidDomainName {
// google.com sends an incomplete certificate chain consisting of 3 certificates ( *.google.com, Google Internet Authority G2 and GeoTrust Global CA)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you update the comment with a reference to the pull request number? Thanks for the docs here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

@kcharwood
Copy link
Contributor

Are there any negative tests we should consider here?

@p4checo
Copy link
Contributor Author

p4checo commented Nov 13, 2015

I think the negative tests are the same as the "classic" certificate pinning case, i.e. if none of the server´s certificates is pinned, the connection should be cancelled. The same goes for expired and invalid certificates, I guess.

I can't think of a specific negative case for this particular issue. Any idea?

@kcharwood
Copy link
Contributor

I couldn't think of any either, just wanted to pose the question.

Some servers send incomplete certificate chains (without the Root CA), which
cause issues in the latest version's (2.6.3) certificate pinning mechanism.
In these cases, when `AFSecurityPolicy`s `-evaluateServerTrust:` invokes
`AFCertificateTrustChainForServerTrust(serverTrust)` (prior to setting the
anchor), the Root CA isn't present (the server didn't send it). Afterwards,
even though `AFServerTrustIsValid(serverTrust)` succeeds, the next validation
fails since no pinned certificate matches any of the `serverCertificates`.

By fetching the `AFCertificateTrustChainForServerTrust(serverTrust)` *after*
the `AFServerTrustIsValid(serverTrust)` validation, the complete chain is
obtained and the Root CAs match (pinned vs validated).

A slight optimization was also made when validating if the `serverTrust`
certificates contain the `pinnedCertificates`, looping in reverse order and
returning after finding the first match.
@p4checo p4checo force-pushed the fix-incomplete-chain-certificate-validation-3_0_0 branch from cda9072 to 468c2ba Compare November 13, 2015 17:36
@kcharwood kcharwood changed the title Fix certificate validation for servers providing incomplete chains Fixed certificate validation for servers providing incomplete chains Nov 13, 2015
kcharwood added a commit that referenced this pull request Nov 13, 2015
…e-validation-3_0_0

Fixed certificate validation for servers providing incomplete chains
@kcharwood kcharwood merged commit 99b48ae into AFNetworking:3_0_0_branch Nov 13, 2015
@p4checo
Copy link
Contributor Author

p4checo commented Nov 13, 2015

🎉 🍻 🎉

@p4checo p4checo deleted the fix-incomplete-chain-certificate-validation-3_0_0 branch November 13, 2015 17:56
@kcharwood
Copy link
Contributor

@p4checo it looks like expiration day has come so the test suite is now failing.

Can you walk me through where you grabbed these two chains so I can get them updated?

@p4checo
Copy link
Contributor Author

p4checo commented Feb 2, 2016

Hi @kcharwood,

I used https://www.ssllabs.com/ssltest/analyze.html?d=google.com to validate whether it was using two chains, and then I got the certificates using OpenSSL:

openssl s_client -showcerts -connect google.com:443 </dev/null 2>/dev/null
(I think I ran the command multiple times for both chains to appear, but I'm not sure)

However, I've now checked that google.com seems to no longer have 2 certification paths 😓

@kcharwood
Copy link
Contributor

How should we proceed here? I don't want to disable the test if I don't have too.

@p4checo
Copy link
Contributor Author

p4checo commented Feb 2, 2016

Ideally, we should find another case of a multiple certification path host and update the test. 😄

At the time I was lucky google.com itself was an example. But surely there are other cases.

@kcharwood
Copy link
Contributor

K. I don't have a lot of time to dig on this issue at the moment, so I may need to disable it in the near term to allow the test suite to pass for some other issues I need to push through.

@p4checo
Copy link
Contributor Author

p4checo commented Feb 2, 2016

Me neither 😞. If I manage to put aside some time for this I'll let you know.

@RadimC
Copy link

RadimC commented Sep 27, 2017

If we've set our pinned certs as the exclusive anchors in our trust store, why do we need to explicitly compare cert data at all?

NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
    [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
 
return AFServerTrustIsValid(serverTrust);

What problem is the additional code solving?

@p4checo
Copy link
Contributor Author

p4checo commented Sep 27, 2017

The issue this PR was fixing was due to incomplete certificate chains being sent by the server and making certificate pinning fail precisely because of this certificate data comparison.

However, the point you raise is valid since the certificates are already defined as anchors, we shouldn't have to compare the data ourselves manually. That should be (and already is) done more efficiently by SecTrustEvaluate (via AFServerTrustIsValid), right?

So, that data comparison check (and my complete "fix") would be unnecessary because AFServerTrustIsValid would handle it automatically by using the defined anchors. I guess I didn't see the complete picture back then 😅

I heard they welcome PR's in this project, @RadimC 😉

Cheers! 🍻

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
4 participants