golang/go · error
tls: unexpected ServerKeyExchange
Error message
tls: unexpected ServerKeyExchange
What it means
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.
Source
Thrown at src/crypto/tls/key_agreement.go:79
if !ok {
return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter")
}
// Perform constant time RSA PKCS #1 v1.5 decryption
preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48})
if err != nil {
return nil, err
}
// We don't check the version number in the premaster secret. For one,
// by checking it, we would leak information about the validity of the
// encrypted pre-master secret. Secondly, it provides only a small
// benefit against a downgrade attack and some implementations send the
// wrong version anyway. See the discussion at the end of section
// 7.4.7.1 of RFC 4346.
return preMasterSecret, nil
}
func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
return errors.New("tls: unexpected ServerKeyExchange")
}
func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
preMasterSecret := make([]byte, 48)
preMasterSecret[0] = byte(clientHello.vers >> 8)
preMasterSecret[1] = byte(clientHello.vers)
_, err := io.ReadFull(config.rand(), preMasterSecret[2:])
if err != nil {
return nil, nil, err
}
rsaKey, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
return nil, nil, errors.New("tls: server certificate contains incorrect key type for selected ciphersuite")
}
encrypted, err := rsa.EncryptPKCS1v15(config.rand(), rsaKey, preMasterSecret)
if err != nil {
return nil, nil, errView on GitHub (pinned to b6b368adc5)
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.
Defensive patterns
Strategy: try-catch
Validate before calling
// No caller validation possible — this is a protocol violation by the server.
// Verify cipher suite selection on the client:
func validateNoRSAKeyExchangeWithServerKE(cfg *tls.Config) error {
// RSA key exchange suites should not be used with servers that send ServerKeyExchange
// This is informational only; the error surfaces during handshake
return nil
} Try / catch
// Client-side: handle during handshake
if err := conn.Handshake(); err != nil {
if strings.Contains(err.Error(), "unexpected ServerKeyExchange") {
log.Printf("server sent invalid ServerKeyExchange for RSA suite: %v", err)
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: server certificate contains incorrect key type for sele
- tls: server selected unsupported curve
- tls: certificate private key does not implement crypto.Decry
- tls: no supported key exchange methods (CurveIDs)
- tls: invalid ClientKeyExchange message
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/8a449e200e3f0fa7.
Report an issue: GitHub.