truststore icon indicating copy to clipboard operation
truststore copied to clipboard

Implement SSLContext.get_ca_certs()

Open sethmlarson opened this issue 2 years ago • 8 comments

I suspect this will be possible in both macOS and Windows trust store APIs.

sethmlarson avatar Feb 26 '23 21:02 sethmlarson

Can you explain (like I'm 5) why this would be more complicated than calling the underlying SSLContext.get_ca_certs method? I'm on Windows, and when I do:

import ssl

ctx = ssl.create_default_context()
ctx.get_ca_certs()

I get a full list of certificates in my Windows trust store. If I do:

import ssl
import truststore
truststore.inject_into_ssl()

ctx = ssl.create_default_context()
ctx._ctx.get_ca_certs()

I get the same list. Why not pass through the get_ca_certs call the same way as, say, load_default_certs?

FaustinCarter avatar Oct 10 '23 02:10 FaustinCarter

@FaustinCarter In Windows, that works because the default SSL context loads the CA certs from the Windows trust store: https://github.com/python/cpython/blob/9cfb4e0d1ebf2900c19ee07697818c621f46cc3d/Lib/ssl.py#L532-L534

On my Mac, import ssl; ctx = ssl.create_default_context(); ctx.get_ca_certs() returns the CA certs from the OpenSSL that Python was compiled with, not the CA certs from the OS trust store.

davisagli avatar Oct 10 '23 16:10 davisagli

Wanted to also note that get_ca_certs() doesn't work the same across all platforms. It'd be nice to have it work in a similar way for all platforms. For example, OpenSSL doesn't take into account certificate directories in get_ca_certs() until /after/ a certificate has been loaded by a lookup (instead of listing all possible certificates that are candidates to be roots).

sethmlarson avatar Oct 10 '23 17:10 sethmlarson

For example, OpenSSL doesn't take into account certificate directories in get_ca_certs() until /after/ a certificate has been loaded by a lookup (instead of listing all possible certificates that are candidates to be roots).

@sethmlarson I think this is true across platforms (get_ca_certs returns the certs that have been loaded into the OpenSSL context -- either explicitly loaded OR implicitly loaded from capath during a lookup). It's just that on Windows load_default_certs does some extra work to explicitly load the CA certs from the OS trust store into OpenSSL.

davisagli avatar Oct 10 '23 17:10 davisagli

Thanks to you both. Very helpful discussion. It's a nice surprise to discover an instance where Windows "just works"! Usually it's the dreaded edge case that's too hard to solve!

FaustinCarter avatar Oct 10 '23 17:10 FaustinCarter

So if I understand this correctly:

  • ssl.SSLContext.load_default_certs works correctly on Windows and Linux
  • ssl.SSLContext.get_ca_certs will return any certs that have been loaded into the SSLContext object

So maybe a reasonable path forward is:

  1. For Mac, write a function that truststore._api.SSLContext.load_default_certs can use to pull from the SecTrust trust object and load into the underlying ssl.SSLContext object. For Windows and Linux, drop through to the original ssl.SSLContext.load_default_certs.
  2. Enable truststore._api.SSLContext.get_ca_certs to call the underlying ssl.SSLContext.get_ca_certs method.

I guess one obvious open implementation question for 1 is: which store to pull from. By default, Windows pulls from ROOT and CA. It looks like there are a couple of options for getting certificates out of a SecTrust object:

  • SecTrustSettingsCopyCertificates pulls certs from a specific domain (user/local/system)
  • SecTrustCopyAnchorCertificates pull "root" certificates
  • SecTrustCopyCustomAnchorCertificates pulls "custom" certificates, whatever those are

FaustinCarter avatar Oct 10 '23 18:10 FaustinCarter