Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSL Certificate Pinning - Certificate Count Check #378

Closed
dmortim3 opened this issue Sep 12, 2017 · 14 comments
Closed

SSL Certificate Pinning - Certificate Count Check #378

dmortim3 opened this issue Sep 12, 2017 · 14 comments

Comments

@dmortim3
Copy link

Having some issues getting certificate pinning to work. Looking at the code and debugging I'm seeing trustedCount is being set to 1 but the condition if trustedCount == serverCerts.count is not being met because there are various items in the certificate chain, not just one. I have taken a look at Alamofire and it doesn't have this type of check, it just does the if result == .unspecified || result == .proceed and returns true. Would appreciate your thoughts.

if result == .unspecified || result == .proceed {
var trustedCount = 0
for serverCert in serverCerts {
for cert in certs {
if cert == serverCert {
trustedCount += 1
break
}
}
}
if trustedCount == serverCerts.count {
return true
}
}

@daltoniam
Copy link
Owner

#412 changed a few things with the SSL pinning that might help. Specifically it added the ability to not check the whole chain and just check the root certificate. Please try the changes in the master branch and see if that works.

@eliburke
Copy link

@dmortim3 the PR added a boolean "validateEntireChain":

        let sslCerts = certificates.flatMap { SSLCert(data: $0) }
        let security = SSLSecurity(certs: sslCerts, usePublicKeys: false)
        security.validateEntireChain = false
        self.socket.security = security

In the future I think validateEntireChain should default to false, but I opted to preserve existing framework behavior

@daltoniam
Copy link
Owner

3.0.3 has been released with @eliburke improvements!

@ffleandro
Copy link

Hi guys, I know this is a closed issue, but my problem is directly related to this thread.

My server has a self-signed certificate with two authorities in the chain trust, the root ca signs the intermediate certificate, and the intermediate signs my server certificate, so:
Root CA > Intermediate > Server

By the end of the isValid(_ trust: SecTrust, domain: String?) -> Bool method, trustedCount == 2 however serverCerts.count == 3.

From the docs of Security.SecTrust.SecTrustGetCertificateCount(_ trust: SecTrust) -> CFIndex I found the following statement:

Indices run from 0 (leaf) to the anchor (or last certificate found if no anchor was found).
The leaf cert (index 0) is always present regardless of whether the trust
reference has been evaluated or not.

So my questions are:

  1. The leaf certificate is my server's certificate? If so, I think trustedCount should always start with 1, since the leaf certificate has already been validated after if result == .unspecified || result == .proceed;
  2. If I set validateEntireChain to false, only the leaf certificate will be validated? So, no validation regarding the root ca's or intermediate's certificate?

@ffleandro
Copy link

@daltoniam Could you give me some feedback on this issue? Is my understanding incorrect or is this a bug?

@eliburke
Copy link

I can only answer #2 since I’m responsible. Yes, set that option to stop as soon as any cert in the chain is verified. Otherwise it requires all certs be verified.

@ffleandro
Copy link

What exactly does "any cert in the chain is verified" mean?
If it has a valid expiration date, what else?
How is the certificate signature validated without validating the whole chain?

If I disable the chain verification, do I still have authentication validation, ie, can I trust the server I'm connecting to is my server? Anyone can have a valid certificate, but it is not guaranteed it is my issued certificate without validating it's signature against my root-ca's and intermediate's certificate, correct?

@eliburke
Copy link

I’m not going to try and explain this soup to nuts, but trusting a certificate, even a top level root cert, simply means you have its fingerprint or hash, and have decided that it should be considered “trusted”. The operating system or browser has root certificates from commercial top level and signing CAs. Following a cert chain means you can verify certs have been generated and signed in an unbroken chain. You may also choose to check issue/expiration times and CRLs (revoked cert lists)
Given a chain (root)A-B-C-D(leaf) you may have system level trust in A, or application level trust of anything in the chain.
By default, Starscream allows the operating system to determine trust. A cert must typically be issued by a commmercial provider or the user must have added something to the system cert database (hard on iOS).
If you choose self signed certs and provide your own cert database, starscream (by default) requires that every cert in the chain have a fingerprint in its internal database. IMO this is an uncommon assumption. As each cert signs the following one in the chain, trust at any level implies trust upstream to the root, and should imply it downstream to the leaf unless you give out your key pair.

if you disable full chain checking, any ONE cert must pass the validation checks. This is still perfectly secure if you trust the holder of the root or upstream certificate. Your verification routine is responsible for making these checks so if you override if, you have control over whether issue/expiration/hostname checks are performed.

Anyone cannot have a valid cert. you perform the checks. If you trust the fingerprint, then it is valid. The validation routine should not be checking whether it is a well formed cert, it checks the trust.

Sorry I’m typing this on my phone from a cruise ship. If you have more questions I can try and answer next week.

@natonmars
Copy link

I'm having a similar issue: with Starscream can I pin to an intermediate certificate in the chain? Does it support wildcard domain specifications?

@eliburke
Copy link

It should work just fine. All the flag does is tell Starscream it does not need to verify every certificate in the chain. Certificate chains exist because it's a scalable way to distribute trust and manage administrative overhead (imagine if every corporation in the world had to put their own certificate into Chrome).

As soon as you choose to trust a specific certificate and ship an application pinned to that certificate, nothing else matters except your trust in that certificate. You are essentially making it a root certificate. You no longer care that there is an issuer.

As for what kind of verification occurs.. Starscream's SSLSecurity class is where to you need to look if your verification doesn't work.

@natonmars
Copy link

Great thanks. What about supporting wildcard domain specifications?

@eliburke
Copy link

Starscream's SSLSecurity class is where to you need to look if your verification doesn't work.

I don't know if wildcard domain work. Why don't you try it.

@AbdulRehmanWarraich
Copy link

This implementation has been remove in 4.0.0 version. New class has been added with the name of FoundationSecurity in place of SSLSecurity. Which validate certificate.
I want to validate using public key. Please guid me how to do so.

@AbdulRehmanWarraich
Copy link

What I understood so far is that I have to update CertificatePinning -> evaluateTrust protocol to add Public Key pining. I would appreciate if some one can confirm if this is the case. Thank you for your support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants