I am trying to use the following code to download a simple https:// URL in Windows 10:
import ssl
from urllib.request import urlopen
urlopen('https://xkcd.com/', context=ssl.create_default_context())
But that code crashes with:
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1129)>
This error appears to indicate that Python couldn’t validate the SSL certificate from the xkcd.com domain.
I believe ssl.create_default_context() (used above) tells Python to use the same certificate authorities as elsewhere on Windows 10. In a different application (Edge) I can successfully visit xkcd.com so it appears that the system certificate authorities match the certificate provided by that domain. So I’m not sure why Python can’t validate the domain’s certificate.
How can I alter the above code to use the Windows 10 certificate authorities and not give a certificate validation error?
This is very easy with requests (or even urllib3) instead of urllib, you just need to pip install requests at the command line (something already installed it in my global Python so I didn’t use a venv). Both handle SSL verification automatically for you. This worked just now in Windows 11.
Your code does work for me as it is too (in Python 3.11. Perhaps my security settings are lax e.g. dev mode). Do you have a specific requirement for urllib, or to use a special ssl context?
You can play with SSLContext.load_default_certs() and other methods too, to try and find where they are on your system.
However I don’t want to rely on certifi since any version of certifi I statically bundle with my app will become out of date after a few years and I want old versions of the app to keep running for a long time.
My app is bundled as a native Windows .exe using py2exe so I don’t think I can use Windows Subsystem for Linux.
I wondered how to check those - thanks for that. The GlobalSign cert is there on my Win 11:
import ssl
ctx = ssl.create_default_context()
print(', '.join(c['serialNumber']
for c in ctx.get_ca_certs()
if 'Global' in repr(c)
)
)
04000000000121585308A2,
Did you miss a Windows update from MS that pushed out extra certs?
What version of libssl-1_1.dll have you got in the DLLs folder in your sys.path? Mine’s 1.1.1.21
Is this a problem with your machine or OS only, at the end of the day? If your users’ machines have recent Windows installations like mine, your code will work for them.