golang/go · critical

tls: failed to sign ECDHE parameters: %s

Error message

tls: failed to sign ECDHE parameters: %s

What it means

During TLS 1.0–1.2 ECDHE ServerKeyExchange parameter signing (modern path using crypto.SignMessage), the private key's Sign operation failed. The underlying error is appended via %s. This is a server-side signing failure, not a protocol error — the key could not produce a signature over the ECDHE parameters (client random + server random + server ECDHE params).

Source

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

		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")
		}
		sig, err = priv.Sign(config.rand(), signed, sigHash)
		if err != nil {
			return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
		}
	}

	skx := new(serverKeyExchangeMsg)
	sigAndHashLen := 0

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Verify the private key file is valid: openssl rsa -check (RSA) or openssl ec -check (ECDSA).
  2. If using an HSM/PKCS#11: ensure the token is connected, unlocked, and the session is active.
  3. Test the key independently: write a small program that calls priv.Sign() outside of TLS.
  4. If using a custom crypto.Signer, debug its Sign method implementation.
  5. Ensure the key type matches the signing algorithm (RSA-PSS requires RSA key, ECDSA requires EC key).

Example fix

// Debug the signing key in isolation
// before: untested custom signer in production
cert := tls.Certificate{PrivateKey: customKey}
// after: verify Sign works outside TLS first
sig, err := customKey.Sign(rand.Reader, testHash, nil)
if err != nil {
    log.Fatal("key signing failed:", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the key can sign before starting the server
func testSigning(key crypto.Signer) error {
    digest := make([]byte, 32)
    opts := crypto.Hash(0) // vary based on key type
    switch key.Public().(type) {
    case *rsa.PublicKey:
        opts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: crypto.SHA256}
    }
    _, err := key.Sign(rand.Reader, digest, opts)
    return err
}

Try / catch

// Verify signing capability at startup
if err := testSigning(cert.PrivateKey.(crypto.Signer)); err != nil {
    log.Fatal("key cannot sign ECDHE parameters: ", err)
}
// Runtime:
if err := conn.Handshake(); err != nil {
    if strings.Contains(err.Error(), "failed to sign ECDHE parameters") {
        log.Printf("ECDHE signing failure: %v", err)
    }
}

Prevention

When it happens

Trigger: Server calls crypto.SignMessage(priv, config.rand(), signed, signOpts) and it returns an error. Causes: HSM/PKCS#11 failure, corrupted key, unsupported sign options, RSA-PSS with wrong salt length, key revoked or locked.

Common situations: HSM or PKCS#11 token disconnected or session expired; corrupted key file; RSA key that doesn't support PSS; key in a hardware module requiring authentication that wasn't provided; a custom crypto.Signer implementation with a bug in its Sign method.

Understand the failure class

Related errors


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