golang/go · critical
tls: certificate cannot be used with the selected cipher sui
Error message
tls: certificate cannot be used with the selected cipher suite
What it means
During TLS 1.0–1.2 ECDHE ServerKeyExchange signing (modern signing path using crypto.SignMessage), the server's certificate key type doesn't match what the cipher suite expects. The check (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA fires when an RSA cipher suite is paired with an ECDSA cert, or an ECDSA cipher suite is paired with an RSA cert. The ka.isRSA flag was set during cipher suite selection.
Source
Thrown at src/crypto/tls/key_agreement.go:209
}
var sig []byte
if ka.version >= VersionTLS12 {
ka.signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms)
if err != nil {
return nil, err
}
sigType, sigHash, err := typeAndHashFromSignatureScheme(ka.signatureAlgorithm)
if err != nil {
return nil, err
}
if sigHash == crypto.SHA1 {
tlssha1.Value() // ensure godebug is initialized
tlssha1.IncNonDefault()
}
signed := slices.Concat(clientHello.random, hello.random, serverECDHEParams)
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
}
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
sig, err = crypto.SignMessage(priv, config.rand(), signed, signOpts)
if err != nil {
return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
}
} else {
sigType, sigHash, err := legacyTypeAndHashFromPublicKey(priv.Public())
if err != nil {
return nil, err
}
signed := hashForServerKeyExchange(sigType, clientHello.random, hello.random, serverECDHEParams)
if (sigType == signaturePKCS1v15) != ka.isRSA {
return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
}View on GitHub (pinned to b6b368adc5)
Solutions
- Ensure the certificate key type matches the cipher suite family: RSA certs for ECDHE-RSA suites, ECDSA certs for ECDHE-ECDSA suites.
- Provide both RSA and ECDSA certificates in tls.Config.Certificates so Go can auto-select the right one.
- Verify tls.Config.CipherSuites only includes suites compatible with the loaded certificate(s).
- Use GetCertificate callback to dynamically select the correct cert based on the negotiated cipher suite.
- Prefer TLS 1.3 which decouples authentication key type from key exchange.
Example fix
// before: ECDSA cert with RSA-only cipher suites
cfg := &tls.Config{
Certificates: []tls.Certificate{ecdsaCert},
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, // needs RSA!
}
// after: match cipher suites to cert or provide both
cfg := &tls.Config{
Certificates: []tls.Certificate{ecdsaCert},
CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
} Defensive patterns
Strategy: type-guard
Validate before calling
// Verify cert/cipher-suite compatibility before starting the server
func validateCertCipherSuiteCompat(cert *tls.Certificate, cipherSuites []uint16) error {
isRSA := false
switch cert.PrivateKey.(crypto.Signer).Public().(type) {
case *rsa.PublicKey:
isRSA = true
}
for _, suite := range cipherSuites {
suiteIsRSA := strings.Contains(cipherSuiteName(suite), "_RSA_") ||
(!strings.Contains(cipherSuiteName(suite), "_ECDSA_"))
// Simplified check — adapt to actual suite classification
if isRSA != suiteIsRSA && strings.Contains(cipherSuiteName(suite), "ECDHE") {
// Potential mismatch for ECDHE suites
}
}
return nil
} Type guard
// Determine if certificate key is RSA for cipher suite matching
func isRSACert(cert *tls.Certificate) bool {
signer, ok := cert.PrivateKey.(crypto.Signer)
if !ok {
return false
}
_, isRSA := signer.Public().(*rsa.PublicKey)
return isRSA
} Try / catch
// Validate at startup
for _, suite := range cfg.CipherSuites {
if err := validateCertCipherSuiteCompat(&cert, cfg.CipherSuites); err != nil {
log.Fatal("cert/cipher-suite mismatch: ", err)
}
}
// Runtime:
if err := conn.Handshake(); err != nil {
if strings.Contains(err.Error(), "certificate cannot be used") {
log.Printf("cert/suite mismatch: %v", err)
}
} Prevention
- Provide both RSA and ECDSA certificates in tls.Config.Certificates.
- Ensure CipherSuites list is compatible with loaded certificates.
- Validate cert/cipher-suite compatibility at startup.
- Use GetCertificate callback for dynamic cert selection.
- Prefer TLS 1.3 to avoid cert-key-type coupling.
When it happens
Trigger: Server signs ECDHE parameters using ka.signatureAlgorithm. If the negotiated cipher suite is ECDHE-RSA (ka.isRSA=true) but the certificate is ECDSA, or the suite is ECDHE-ECDSA (ka.isRSA=false) but the cert is RSA, this mismatch triggers.
Common situations: Server loaded the wrong certificate type for the negotiated cipher suite; misconfigured tls.Config.Certificates with multiple certs where the wrong one was selected; cipher suite preferences and certificate availability are out of sync; a server update changed cert type without updating cipher suite config.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: failed to sign ECDHE parameters: %s
- tls: no supported elliptic curves offered
- tls: failed to sign handshake: %s
- tls: certificate private key does not implement crypto.Decry
- tls: server certificate contains incorrect key type for sele
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/83e4a4675930405e.
Report an issue: GitHub.