golang/go · critical

tls: failed to sign handshake: %s

Error message

tls: failed to sign handshake: %s

What it means

The TLS 1.3 server failed to sign the CertificateVerify message using its certificate's private key via crypto.SignMessage. The wrapping %s includes the underlying signing error. The server has a special case: if the key is RSA and too small for RSA-PSS (bit length < hash size * 2 + 2 bytes), it sends alertHandshakeFailure; otherwise it sends alertInternalError. This is a server-side configuration or hardware problem, not a client issue.

Source

Thrown at src/crypto/tls/handshake_server_tls13.go:882

	if err != nil {
		return c.sendAlert(alertInternalError)
	}

	signed := signedMessage(serverSignatureContext, hs.transcript)
	signOpts := crypto.SignerOpts(sigHash)
	if sigType == signatureRSAPSS {
		signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
	}
	sig, err := crypto.SignMessage(hs.cert.PrivateKey.(crypto.Signer), c.config.rand(), signed, signOpts)
	if err != nil {
		public := hs.cert.PrivateKey.(crypto.Signer).Public()
		if rsaKey, ok := public.(*rsa.PublicKey); ok && sigType == signatureRSAPSS &&
			rsaKey.N.BitLen()/8 < sigHash.Size()*2+2 { // key too small for RSA-PSS
			c.sendAlert(alertHandshakeFailure)
		} else {
			c.sendAlert(alertInternalError)
		}
		return errors.New("tls: failed to sign handshake: " + err.Error())
	}
	certVerifyMsg.signature = sig

	if _, err := hs.c.writeHandshakeRecord(certVerifyMsg, hs.transcript); err != nil {
		return err
	}

	return nil
}

func (hs *serverHandshakeStateTLS13) sendServerFinished() error {
	c := hs.c

	finished := &finishedMsg{
		verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
	}

	if _, err := hs.c.writeHandshakeRecord(finished, hs.transcript); err != nil {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. If RSA: use a certificate with at least 2048-bit keys (RSA-PSS requires key_size_bytes >= hash_size*2 + 2).
  2. Verify the private key file is valid and not corrupted (openssl rsa -check).
  3. If using an HSM/PKCS#11: ensure the token is connected, the session is valid, and credentials are provided.
  4. Switch to an ECDSA (P-256) certificate which avoids RSA-PSS minimum-size issues.
  5. Check that the private key implements crypto.Signer correctly (custom key types must support Sign with the right options).

Example fix

// before: RSA 1024-bit certificate (too small for TLS 1.3 RSA-PSS)
cert, _ := tls.LoadX509KeyPair("rsa1024.crt", "rsa1024.key")
// after: use at least RSA 2048-bit or switch to ECDSA P-256
cert, _ := tls.LoadX509KeyPair("ecdsa_p256.crt", "ecdsa_p256.key")
Defensive patterns

Strategy: validation

Validate before calling

// Verify the private key can sign with the required algorithm before starting the server
func verifySigningCapability(key crypto.Signer, sigAlg SignatureScheme) error {
    _, _, err := typeAndHashFromSignatureScheme(sigAlg)
    if err != nil {
        return err
    }
    // For RSA, check minimum key size for PSS
    if rsaKey, ok := key.Public().(*rsa.PublicKey); ok {
        _, sigHash, _ := typeAndHashFromSignatureScheme(sigAlg)
        if rsaKey.N.BitLen()/8 < sigHash.Size()*2+2 {
            return fmt.Errorf("RSA key too small (%d bits) for RSA-PSS with %v", rsaKey.N.BitLen(), sigHash)
        }
    }
    return nil
}

Type guard

// Ensure private key implements crypto.Signer
func isSigner(key any) bool {
    _, ok := key.(crypto.Signer)
    return ok
}

Try / catch

// Server-side: check at startup
err := verifySigningCapability(cert.PrivateKey.(crypto.Signer), preferredSigAlg)
if err != nil {
    log.Fatal("server certificate cannot sign handshake: ", err)
}
// Runtime: handle Handshake() error
if err := conn.Handshake(); err != nil {
    if strings.Contains(err.Error(), "failed to sign handshake") {
        log.Printf("signing failure during handshake: %v", err)
    }
}

Prevention

When it happens

Trigger: The server's private key (hs.cert.PrivateKey typed as crypto.Signer) failed to produce a signature. Causes: RSA key too small for RSA-PSS with the negotiated hash; HSM/PKCS#11 token error; corrupted key material; hardware crypto accelerator failure; insufficient permissions on the key store.

Common situations: RSA certificate with a 1024-bit or smaller key used with TLS 1.3 (which requires RSA-PSS, needing larger keys); a PKCS#11 or HSM-backed key that is unreachable or has a session timeout; a cert/key pair loaded from an incorrect file path; key stored in a hardware module that requires a PIN that wasn't provided.

Understand the failure class

Related errors


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