Skip to content

SSLCertVerificationError - unable to get local issuer certificate #6717

Closed

Description

@DanSIntel

Version 2.32.0 introduced changes and improvements with SSLContext as specified in the release history:

Improvements - verify=True now reuses a global SSLContext which should improve request time variance between first and subsequent requests. It should also minimize certificate load time on Windows systems when using a Python version built with OpenSSL 3.x. (#6667)

We are facing issue making http requests to webservers which are signed by a local root ca.
The certificate chain is installed correctly on the Windows station and version 2.31.1 is working as expected.

Versions 2.32.x are throwing an error: SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1125)')))

This is a code example that works in 2.31.1 and does not in 2.32.x

from requests.adapters import HTTPAdapter
import requests
from requests.packages.urllib3.util.ssl_ import create_urllib3_context

class SSLContextAdapter(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        context = create_urllib3_context()
        context.load_default_certs()  # this loads the system's CA certificates
        kwargs['ssl_context'] = context
        return super().init_poolmanager(*args, **kwargs)

SESSION = requests.Session()
SESSION.mount('https://', SSLContextAdapter())
SESSION.get(MY_URL_SIGNED_BY_LOCAL_ROOT_CA, headers = headers, verify  = True)

After looking at the lastest changes, if we modifiy our code its working but i dont think that calling the private global _preloaded_ssl_context is the right way:

from requests.adapters import HTTPAdapter, _preloaded_ssl_context
import requests


class SSLContextAdapter(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        _preloaded_ssl_context.load_default_certs() # this loads the system's CA certificates
        return super().init_poolmanager(*args, **kwargs)

SESSION = requests.Session()
SESSION.mount('https://', SSLContextAdapter())
SESSION.get(MY_URL_SIGNED_BY_LOCAL_ROOT_CA, headers = headers, verify  = True)

What is the recommended way for using load_default_certs() with version 2.32.x if the usage has changed or alternatively can you confirm if this is a bug?

Activity

  1. nateprewitt commented on May 22, 2024

    @nateprewitt
    Member

    Hi @DanSIntel, this looks like the same root issue described in #6715. Can you take a look at the proposed patch in #6716 and let us know if the new API proposal meets your use case. From the code you provided, I would think you should be able to do this through pool_kwargs.

    I'd propose we close this as a duplicate of #6715 if the above is agreeable and we'll track progress in the PR.

  2. sigmavirus24 commented on May 22, 2024

    @sigmavirus24
    Contributor

    Duplicate of #6715

  3. DanSIntel commented on May 22, 2024

    @DanSIntel
    Author

    @nateprewitt can you provide an example how using the changes in #6716 can work with my custom sslcontext adapter?

  4. nateprewitt commented on May 22, 2024

    @nateprewitt
    Member

    Do the same thing you're doing now but in your adapters __init__. Then you can patch the return value the same way we're using _preloaded_ssl_context now (ref).

    Something like this:

    class SSLContextAdapter(HTTPAdapter):
        def __init__(
            self,
            pool_connections=DEFAULT_POOLSIZE,
            pool_maxsize=DEFAULT_POOLSIZE,
            max_retries=DEFAULT_RETRIES,
            pool_block=DEFAULT_POOLBLOCK,
        ):
            super().__init__()
            self.custom_context = create_urllib3_context()
            # Any cert modifications can be done here (if you need this per request,
            # do it in the build_connection_pool_key_attributes below.)
            self.custom_context.load_default_certs()
        
        [...]
        
        def build_connection_pool_key_attributes(self, request, verify, cert=None):
            host_params, pool_kwargs = super().build_connection_pool_key_attributes(request, verify, cert)
            pool_kwargs['ssl_context'] = self.custom_context  # you can put this behind a verify is True conditional too
            return host_params, pool_kwargs
  5. DanSIntel commented on May 22, 2024

    @DanSIntel
    Author

    thanks, i verified that it is working so v2.32.3 should do the job

  6. locked as resolved and limited conversation to collaborators on May 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions