redis/redis-py · critical · ConnectionError

received and expected certificates do not match

Error message

received and expected certificates do not match

What it means

Raised as a ConnectionError by ocsp_staple_verifier (redis/ocsp.py:165) when an `expected` certificate is supplied (pinned-cert mode) and the server's peer certificate does not match it. The function loads expected via x509.load_pem_x509_certificate and compares peer_cert != e; any mismatch aborts. This is an explicit pinning check — the caller asserted which cert should be present and the server presented a different one.

Solutions

  1. Update the pinned expected certificate to the server's current certificate if the rotation was legitimate.
  2. Confirm you are connecting to the intended endpoint (host/port) whose cert matches the pin.
  3. If the mismatch is unexpected, treat it as a possible MITM and investigate before trusting the new cert.
  4. Automate pin rotation so server and client pins are updated together to avoid stale-pin failures.

Example fix

# before - pinned expected cert is stale after rotation
ocsp_staple_verifier(con, ocsp_bytes, expected=old_cert_pem)  # ConnectionError: received and expected certificates do not match

# after - update the pin to the new server cert (after validating the rotation is legitimate)
ocsp_staple_verifier(con, ocsp_bytes, expected=new_cert_pem)
Defensive patterns

Strategy: try-catch

Validate before calling

from cryptography import x509

def peer_matches_pinned_cert(con, expected_pem):
    peer = con.get_peer_certificate().to_cryptography()
    expected = x509.load_pem_x509_certificate(expected_pem)
    return peer == expected

Try / catch

from redis.exceptions import ConnectionError as RedisConnectionError

try:
    ocsp_staple_verifier(con, ocsp_bytes, expected=pin)
except RedisConnectionError as e:
    if 'do not match' in str(e):
        logging.critical('Peer cert does not match pinned cert - possible MITM or rotation: %s', e)
    raise

Prevention

When it happens

Trigger: Calling ocsp_staple_verifier(con, ocsp_bytes, expected=<pinned_cert_bytes>) where the server presents a different certificate than the pinned one. Common when the pinned cert was rotated and the client's expected value is stale, or when connecting to the wrong endpoint that serves a different cert.

Common situations: Certificate rotated on the server but the client's pinned expected cert not updated; connecting to a different node/region that uses a distinct cert; MITM presenting an attacker cert; pin file path points at an old cert; cert reissued with the same subject but different key/serial.

Understand the failure class

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/cf7607f72f47cd8e. Report an issue: GitHub.

Appendix: source

Thrown at redis/ocsp.py:165

    """
    if ocsp_bytes in [b"", None]:
        raise ConnectionError("no ocsp response present")

    issuer_cert = None
    peer_cert = con.get_peer_certificate().to_cryptography()
    for c in con.get_peer_cert_chain():
        cert = c.to_cryptography()
        if cert.subject == peer_cert.issuer:
            issuer_cert = cert
            break

    if issuer_cert is None:
        raise ConnectionError("no matching issuer cert found in certificate chain")

    if expected is not None:
        e = x509.load_pem_x509_certificate(expected)
        if peer_cert != e:
            raise ConnectionError("received and expected certificates do not match")

    return _check_certificate(issuer_cert, ocsp_bytes)


class OCSPVerifier:
    """A class to verify ssl sockets for RFC6960/RFC6961. This can be used
    when using direct validation of OCSP responses and certificate revocations.

    @see https://datatracker.ietf.org/doc/html/rfc6960
    @see https://datatracker.ietf.org/doc/html/rfc6961
    """

    def __init__(self, sock, host, port, ca_certs=None):
        self.SOCK = sock
        self.HOST = host
        self.PORT = port
        self.CA_CERTS = ca_certs

View on GitHub (pinned to 6a6b581b48)