{"record":{"id":"585d8b7cf7e2c719","repo":"golang/go","slug":"client-doesn-t-support-ecdhe-can-only-use-legacy","errorCode":null,"errorMessage":"client doesn't support ECDHE, can only use legacy RSA key exchange","messagePattern":"client doesn't support ECDHE, can only use legacy RSA key exchange","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/crypto/tls/common.go","lineNumber":1473,"sourceCode":"\t\tif _, err := selectSignatureScheme(vers, c, chi.SignatureSchemes); err != nil {\n\t\t\treturn supportsRSAFallback(err)\n\t\t}\n\t}\n\n\t// In TLS 1.3 we are done because supported_groups is only relevant to the\n\t// ECDHE computation, point format negotiation is removed, cipher suites are\n\t// only relevant to the AEAD choice, and static RSA does not exist.\n\tif vers == VersionTLS13 {\n\t\treturn nil\n\t}\n\n\t// The only signed key exchange we support is ECDHE.\n\tecdheSupported, err := supportsECDHE(config, vers, chi.SupportedCurves, chi.SupportedPoints)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !ecdheSupported {\n\t\treturn supportsRSAFallback(errors.New(\"client doesn't support ECDHE, can only use legacy RSA key exchange\"))\n\t}\n\n\tvar ecdsaCipherSuite bool\n\tif priv, ok := c.PrivateKey.(crypto.Signer); ok {\n\t\tswitch pub := priv.Public().(type) {\n\t\tcase *ecdsa.PublicKey:\n\t\t\tvar curve CurveID\n\t\t\tswitch pub.Curve {\n\t\t\tcase elliptic.P256():\n\t\t\t\tcurve = CurveP256\n\t\t\tcase elliptic.P384():\n\t\t\t\tcurve = CurveP384\n\t\t\tcase elliptic.P521():\n\t\t\t\tcurve = CurveP521\n\t\t\tdefault:\n\t\t\t\treturn supportsRSAFallback(unsupportedCertificateError(c))\n\t\t\t}\n\t\t\tvar curveOk bool","sourceCodeStart":1455,"sourceCodeEnd":1491,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/common.go#L1455-L1491","documentation":"Thrown by SupportsCertificate when the client does not support ECDHE key exchange (no common elliptic curve or point format), and the fallback static-RSA check is wrapped around this error. ECDHE is the only signed key exchange Go supports; without it the connection can only use legacy static RSA. The actual return is supportsRSAFallback(this error), so callers may see nil if static RSA is viable.","triggerScenarios":"SupportsCertificate on a cert where supportsECDHE returns false: the client's SupportedCurves/SupportedPoints share nothing with the server's allowed curves. The error is returned only if static-RSA fallback is also unsupported (TLS 1.3, non-RSA key, or no mutual RSA cipher suite).","commonSituations":"Legacy client advertising no modern curves; server restricted CurvePreferences to curves the client lacks; TLS 1.3 (no static RSA) combined with a curve-less client; hardening that removed common curves.","solutions":["Add common curves (X25519, P-256) to the server's Config.CurvePreferences and the client's SupportedCurves.","Upgrade the client to a TLS stack that advertises ECDHE curves.","If legacy support is required, allow an RSA cipher suite and TLS <=1.2 so static RSA fallback works.","Avoid disabling all curves in CurvePreferences."],"exampleFix":"// before\ncfg.CurvePreferences = []tls.CurveID{tls.CurveP521} // client has only X25519\n\n// after\ncfg.CurvePreferences = []tls.CurveID{tls.X25519, tls.CurveP256, tls.CurveP521}","handlingStrategy":"validation","validationCode":"// Verify ECDHE is achievable for a ClientHello before committing to an ECDSA cert.\nfunc supportsModernECDHE(chi *tls.ClientHelloInfo) bool {\n    for _, c := range chi.SupportedCurves {\n        if c == tls.X25519 || c == tls.CurveP256 { return true }\n    }\n    return false\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep X25519 and P-256 in CurvePreferences; they have the widest client support.","Avoid disabling all curves in server config.","If supporting legacy clients, allow an RSA cipher suite at TLS <=1.2 as fallback.","Survey client SupportedCurves before restricting CurvePreferences."],"tags":["crypto","tls","key-exchange","ecdhe","go"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}