{"record":{"id":"55cb50e6da798d95","repo":"hyperledger/fabric","slug":"public-key-of-server-certificate-presented-by-s-d","errorCode":null,"errorMessage":"public key of server certificate presented by %s doesn't match the expected public key","messagePattern":"public key of server certificate presented by (.+?) doesn't match the expected public key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"orderer/common/cluster/connections.go","lineNumber":64,"sourceCode":"\tconnMapping := &ConnectionStore{\n\t\tConnections: &connMapperReporter{\n\t\t\tConnectionMapper:          make(ConnByCertMap),\n\t\t\ttlsConnectionCountMetrics: tlsConnectionCount,\n\t\t},\n\t\tdialer: dialer,\n\t}\n\treturn connMapping\n}\n\n// verifyHandshake returns a predicate that verifies that the remote node authenticates\n// itself with the given TLS certificate\nfunc (c *ConnectionStore) verifyHandshake(endpoint string, certificate []byte) RemoteVerifier {\n\treturn func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {\n\t\terr := crypto.CertificatesWithSamePublicKey(certificate, rawCerts[0])\n\t\tif err == nil {\n\t\t\treturn nil\n\t\t}\n\t\treturn errors.Errorf(\"public key of server certificate presented by %s doesn't match the expected public key\",\n\t\t\tendpoint)\n\t}\n}\n\n// Disconnect closes the gRPC connection that is mapped to the given certificate\nfunc (c *ConnectionStore) Disconnect(expectedServerCert []byte) {\n\tc.lock.Lock()\n\tdefer c.lock.Unlock()\n\n\tconn, connected := c.Connections.Lookup(expectedServerCert)\n\tif !connected {\n\t\treturn\n\t}\n\tconn.Close()\n\tc.Connections.Remove(expectedServerCert)\n}\n\n// Connection obtains a connection to the given endpoint and expects the given server certificate","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/cluster/connections.go#L46-L82","documentation":"verifyHandshake builds a TLS RemoteVerifier that compares the certificate presented during the handshake against the expected server certificate using crypto.CertificatesWithSamePublicKey. If the public keys differ, the server at the endpoint is not the one we pinned, so the connection is rejected.","triggerScenarios":"The remote orderer at endpoint presented a TLS certificate whose public key does not match the certificate bytes captured earlier (certificate passed into ConnectionStore), during gRPC handshake verification.","commonSituations":"The remote node rotated/reissued its TLS cert; DNS or load balancer routes the endpoint to a different node; cluster TLS certs regenerated while peers cached the old public key.","solutions":["Update the expected certificate (re-fetch the remote node's current TLS CA/cert) and reconnect","Verify the endpoint resolves to the intended host (no wrong DNS/LB routing)","If certs were rotated, redistribute the new TLS certificates across the cluster and restart the affected orderers"],"exampleFix":"// before\nconn, err := mgr.Connect(endpoint, staleCert)\n// after\nnewCert := fetchCurrentTLSCert(endpoint) // re-pull rotated cert\nconn, err := mgr.Connect(endpoint, newCert)","handlingStrategy":"validation","validationCode":"certs, err := x509.ParseCertificates(rawCerts)\nif err != nil || len(rawCerts) == 0 {\n    return fmt.Errorf(\"no valid server certificate\")\n}\nif err := crypto.CertificatesWithSamePublicKey(expectedCert, rawCerts[0]); err != nil {\n    return fmt.Errorf(\"unexpected server cert at endpoint\")\n}","typeGuard":"func certMatches(expected, presented []byte) bool {\n    return crypto.CertificatesWithSamePublicKey(expected, presented) == nil\n}","tryCatchPattern":"verifier := store.verifyHandshake(endpoint, expectedCert)\nif err := verifier(rawCerts, nil); err != nil {\n    log.Errorf(\"TLS pinning failed for %s: %v\", endpoint, err)\n    return err\n}","preventionTips":["Distribute TLS cert updates cluster-wide before expiry/rotation","Pin certificates, not just CAs, when using verifyHandshake","Monitor for cert rotation and refresh pinned certs automatically","Verify DNS/LB so the endpoint always reaches the intended node"],"tags":["tls","certificate","security","handshake","cluster"],"backgroundTag":"tls-cert-mismatch","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}