{"record":{"id":"8a449e200e3f0fa7","repo":"golang/go","slug":"tls-unexpected-serverkeyexchange","errorCode":null,"errorMessage":"tls: unexpected ServerKeyExchange","messagePattern":"tls: unexpected ServerKeyExchange","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/key_agreement.go","lineNumber":79,"sourceCode":"\tif !ok {\n\t\treturn nil, errors.New(\"tls: certificate private key does not implement crypto.Decrypter\")\n\t}\n\t// Perform constant time RSA PKCS #1 v1.5 decryption\n\tpreMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// We don't check the version number in the premaster secret. For one,\n\t// by checking it, we would leak information about the validity of the\n\t// encrypted pre-master secret. Secondly, it provides only a small\n\t// benefit against a downgrade attack and some implementations send the\n\t// wrong version anyway. See the discussion at the end of section\n\t// 7.4.7.1 of RFC 4346.\n\treturn preMasterSecret, nil\n}\n\nfunc (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {\n\treturn errors.New(\"tls: unexpected ServerKeyExchange\")\n}\n\nfunc (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {\n\tpreMasterSecret := make([]byte, 48)\n\tpreMasterSecret[0] = byte(clientHello.vers >> 8)\n\tpreMasterSecret[1] = byte(clientHello.vers)\n\t_, err := io.ReadFull(config.rand(), preMasterSecret[2:])\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\trsaKey, ok := cert.PublicKey.(*rsa.PublicKey)\n\tif !ok {\n\t\treturn nil, nil, errors.New(\"tls: server certificate contains incorrect key type for selected ciphersuite\")\n\t}\n\tencrypted, err := rsa.EncryptPKCS1v15(config.rand(), rsaKey, preMasterSecret)\n\tif err != nil {\n\t\treturn nil, nil, err","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/key_agreement.go#L61-L97","documentation":"The rsaKeyAgreement implementation's processServerKeyExchange unconditionally returns this error because RSA key exchange (as defined in TLS 1.0–1.2) never involves a ServerKeyExchange message — the client encrypts the pre-master secret directly to the server's certificate public key. Receiving a ServerKeyExchange when RSA key exchange was negotiated means the handshake is malformed.","triggerScenarios":"The client received a ServerKeyExchange message despite the negotiated cipher suite using plain RSA key exchange (not RSA-ECDHE or RSA-DHE). This should never happen with a conformant server. It indicates a protocol violation or a cipher suite negotiation mismatch.","commonSituations":"A buggy server that sends ServerKeyExchange for an RSA cipher suite; a MITM that injects a spurious ServerKeyExchange; cipher suite negotiation manipulation; a server implementation bug where DHE/ECDHE code path is erroneously taken for RSA suites.","solutions":["Verify the server is conformant — RSA cipher suites must not produce a ServerKeyExchange message.","Test with a reference TLS implementation (openssl s_client) to reproduce and isolate the issue.","Check that the negotiated cipher suite is correct (no downgrade or misconfiguration).","If you control the server, ensure the RSA key agreement path doesn't send ServerKeyExchange.","Switch to ECDHE cipher suites (preferred in modern TLS) to avoid the RSA key exchange path entirely."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// No caller validation possible — this is a protocol violation by the server.\n// Verify cipher suite selection on the client:\nfunc validateNoRSAKeyExchangeWithServerKE(cfg *tls.Config) error {\n    // RSA key exchange suites should not be used with servers that send ServerKeyExchange\n    // This is informational only; the error surfaces during handshake\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// Client-side: handle during handshake\nif err := conn.Handshake(); err != nil {\n    if strings.Contains(err.Error(), \"unexpected ServerKeyExchange\") {\n        log.Printf(\"server sent invalid ServerKeyExchange for RSA suite: %v\", err)\n    }\n}","preventionTips":["Prefer ECDHE cipher suites which always use ServerKeyExchange.","Disable RSA key exchange suites (TLS_RSA_WITH_*) in client config — they're deprecated.","Use TLS 1.3 which doesn't have this code path.","Test against known-good servers."],"tags":["tls","tls12","rsa","key-exchange","server-key-exchange","client-side"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}