golang/go · critical

tls: server certificate contains incorrect key type for sele

Error message

tls: server certificate contains incorrect key type for selected ciphersuite

What it means

During TLS 1.0–1.2 RSA key exchange on the client side, the server's certificate public key is not an *rsa.PublicKey. The RSA key agreement requires encrypting the pre-master secret to the server's RSA public key via rsa.EncryptPKCS1v15. If the cert contains an ECDSA, Ed25519, or other non-RSA key, this type assertion fails.

Source

Thrown at src/crypto/tls/key_agreement.go:93

	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, err
	}
	ckx := new(clientKeyExchangeMsg)
	ckx.ciphertext = make([]byte, len(encrypted)+2)
	ckx.ciphertext[0] = byte(len(encrypted) >> 8)
	ckx.ciphertext[1] = byte(len(encrypted))
	copy(ckx.ciphertext[2:], encrypted)
	return preMasterSecret, ckx, nil
}

// sha1Hash calculates a SHA1 hash over the given byte slices.
func sha1Hash(slices [][]byte) []byte {
	hsha1 := sha1.New()
	for _, slice := range slices {
		hsha1.Write(slice)

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Ensure the server certificate key type matches the negotiated cipher suite (RSA cert for RSA suites, ECDSA cert for ECDSA suites).
  2. Configure the client to prefer ECDHE cipher suites that match the server's certificate key type.
  3. Verify the server is loading the correct certificate for the connection.
  4. Use tls.Config.Certificates to provide both RSA and ECDSA certs so the server can select the right one.
  5. Update to TLS 1.3 which decouples key exchange from authentication, avoiding this class of mismatch.

Example fix

// before: server has only ECDSA cert but client negotiates RSA cipher suite
cfg := &tls.Config{
    CipherSuites: []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA}, // requires RSA cert
    Certificates: []tls.Certificate{ecdsaCert}, // wrong key type!
}
// after: provide matching RSA cert or use ECDHE suites
cfg := &tls.Config{
    CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
    Certificates: []tls.Certificate{ecdsaCert}, // now matches
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Verify server cert key type before connecting (if cert is known)
func verifyRSACertKeyType(cert *x509.Certificate) error {
    if _, ok := cert.PublicKey.(*rsa.PublicKey); !ok {
        return errors.New("server certificate is not RSA but RSA key exchange was negotiated")
    }
    return nil
}

Type guard

func isRSAPublicKey(pub any) bool {
    _, ok := pub.(*rsa.PublicKey)
    return ok
}

Try / catch

// Client-side: handle during handshake
if err := conn.Handshake(); err != nil {
    if strings.Contains(err.Error(), "incorrect key type") {
        log.Printf("server cert key type mismatch: %v", err)
    }
}

Prevention

When it happens

Trigger: Client calls rsaKeyAgreement.generateClientKeyExchange and attempts cert.PublicKey.(*rsa.PublicKey). The negotiated cipher suite is an RSA key-exchange suite (e.g. TLS_RSA_WITH_AES_128_CBC_SHA), but the server's certificate uses a non-RSA key (e.g. ECDSA P-256).

Common situations: Cipher suite and certificate key type mismatch: server presents an ECDSA cert but the client/server negotiated an RSA key exchange cipher suite; misconfigured server that loads the wrong certificate for the negotiated cipher suite; client forces RSA suites while server only has ECDSA certs (or vice versa).

Understand the failure class

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/55ea8e7a27964bbc. Report an issue: GitHub.