如何使用urllib3下载远程服务器的SSL证书?
Since you can't rely on certifi.where() and need to work within your existing environment's built-in tools, here are two practical ways to grab the certificate urllib3 is receiving from phpMyAdmin's server—even when verification fails:
Method 1: Temporarily Disable Cert Verification to Fetch the Certificate
You can turn off certificate validation just long enough to establish a connection and retrieve the certificate details, then inspect them directly. Here's how to adapt your existing code:
#! /usr/bin/python3 import cfnbootstrap from cfnbootstrap.packages import requests from requests.utils import DEFAULT_CA_BUNDLE_PATH from requests.packages import urllib3 import ssl # Create connection with cert verification disabled temporarily conn = urllib3.connection_from_url("https://www.phpmyadmin.net", retries=False) conn.cert_reqs = 'CERT_NONE' # Bypass validation to retrieve the certificate conn.ca_certs = DEFAULT_CA_BUNDLE_PATH try: response = conn.request("GET", "/downloads/") # Pull human-readable certificate details cert = conn.sock.getpeercert() print("Certificate Details:") print(f"Issuer: {dict(cert['issuer'])}") print(f"Subject: {dict(cert['subject'])}") print(f"Valid From: {cert['notBefore']}") print(f"Valid Until: {cert['notAfter']}") # Get the raw PEM-formatted certificate if needed raw_cert = ssl.DER_cert_to_PEM_cert(conn.sock.getpeercert(True)) print("\nRaw PEM Certificate:") print(raw_cert) finally: conn.close()
This works because even with CERT_NONE, the SSL handshake still exchanges certificates—we just skip the validation step. Check the notAfter field to confirm if the certificate is actually expired, or if there's a chain issue (like your environment trusting an outdated root CA that phpMyAdmin no longer uses).
Method 2: Capture the Certificate During a Failed Verification
If you want to see the certificate without disabling verification entirely, use a custom SSL context to capture the certificate before validation fails. Here's how:
#! /usr/bin/python3 import cfnbootstrap from cfnbootstrap.packages import requests from requests.utils import DEFAULT_CA_BUNDLE_PATH from requests.packages import urllib3 import ssl class CertCaptureContext(ssl.SSLContext): def wrap_socket(self, sock, server_hostname=None, **kwargs): wrapped_socket = super().wrap_socket(sock, server_hostname=server_hostname, **kwargs) # Grab certificate details before verification runs try: cert = wrapped_socket.getpeercert() raw_cert = ssl.DER_cert_to_PEM_cert(wrapped_socket.getpeercert(True)) print("Captured Certificate Before Verification:") print(f"Valid Until: {cert['notAfter']}") print("\nRaw PEM:") print(raw_cert) except Exception as e: print(f"Error capturing certificate: {e}") return wrapped_socket # Use our custom context with urllib3 conn = urllib3.connection_from_url("https://www.phpmyadmin.net", retries=False) conn.cert_reqs = 'CERT_REQUIRED' conn.ca_certs = DEFAULT_CA_BUNDLE_PATH # Replace default SSL context with our custom capture context conn.ssl_context = CertCaptureContext(ssl.PROTOCOL_TLS_CLIENT) conn.ssl_context.load_verify_locations(conn.ca_certs) try: response = conn.request("GET", "/downloads/") except ssl.SSLCertVerificationError as e: print(f"\nVerification failed as expected: {e}") finally: conn.close()
This method intercepts the socket-wrapping process to grab the certificate details right before the verification error is thrown. It’s useful if you want to confirm exactly which certificate is being presented during the failed handshake.
A Quick Note on the Root Cause
Even if your browser works fine, your AWS environment might be using an outdated CA bundle (stored at DEFAULT_CA_BUNDLE_PATH). The phpMyAdmin certificate is likely valid, but your server’s trust store may lack the latest root CA needed to verify its chain. Cross-referencing the captured certificate’s issuer with your environment’s trusted CAs will help confirm this.
内容的提问来源于stack exchange,提问作者philolegein

