golang/go · error

tls: invalid signature by the client certificate: {err}

Error message

tls: invalid signature by the client certificate: {err}

What it means

TLS 1.2 path: the server failed to verify the client's CertificateVerify signature using verifyHandshakeSignature. The client's signature over the handshake transcript does not match its certificate's public key, indicating a bad signature, wrong key, or tampered transcript.

Source

Thrown at src/crypto/tls/handshake_server.go:797

			if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, certReq.supportedSignatureAlgorithms) {
				c.sendAlert(alertIllegalParameter)
				return errors.New("tls: client certificate used with invalid signature algorithm")
			}
			sigType, sigHash, err = typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
			if err != nil {
				return c.sendAlert(alertInternalError)
			}
			if sigHash == crypto.SHA1 {
				tlssha1.Value() // ensure godebug is initialized
				tlssha1.IncNonDefault()
			}
			if hs.finishedHash.buffer == nil {
				c.sendAlert(alertInternalError)
				return errors.New("tls: internal error: did not keep handshake transcript for TLS 1.2")
			}
			if err := verifyHandshakeSignature(sigType, pub, sigHash, hs.finishedHash.buffer, certVerify.signature); err != nil {
				c.sendAlert(alertDecryptError)
				return errors.New("tls: invalid signature by the client certificate: " + err.Error())
			}
		} else {
			sigType, sigHash, err = legacyTypeAndHashFromPublicKey(pub)
			if err != nil {
				c.sendAlert(alertIllegalParameter)
				return err
			}
			signed := hs.finishedHash.hashForClientCertificate(sigType)
			if err := verifyLegacyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
				c.sendAlert(alertDecryptError)
				return errors.New("tls: invalid signature by the client certificate: " + err.Error())
			}
		}

		c.peerSigAlg = certVerify.signatureAlgorithm

		if err := transcriptMsg(certVerify, &hs.finishedHash); err != nil {
			return err

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Verify the client is signing the exact handshake transcript hash with the private key matching its certificate's public key.
  2. Inspect the wrapped err (concatenated into the message) to distinguish rsa.VerifyErr from ecdsa errors.
  3. Check for middleboxes or proxies that alter handshake bytes — they invalidate the transcript.
  4. Ensure the client certificate chain is correctly generated and the key pair matches.

Example fix

// Client must sign the handshake transcript with the matching private key.
// Go's tls.Config.Certificates / GetClientCertificate handle this automatically.
// If implementing manual signing, sign hs.finishedHash.buffer exactly as the
// server will recompute it — do not modify any handshake messages.
Defensive patterns

Strategy: try-catch

Validate before calling

// Client: verify the cert/key pair match before connecting.
if err := cert.Leaf.CheckSignatureFrom(cert.Leaf); err == nil {
    // sanity: cert parses. For key match, compare public key types.
}
// More directly: sign a probe transcript and verify with the cert's public key.

Try / catch

// Server: surface as an mTLS auth failure.
if err != nil && strings.Contains(err.Error(), "invalid signature by the client certificate") {
    return fmt.Errorf("client signature verification failed: %w", err)
}

Prevention

When it happens

Trigger: verifyHandshakeSignature(sigType, pub, sigHash, hs.finishedHash.buffer, certVerify.signature) returns an error — e.g. crypto/rsa verification failure, ECDSA signature invalid, or Ed25519 mismatch. Triggered when the client signs the wrong data, uses the wrong key, or the transcript diverged.

Common situations: A client signing with a different key than the one in its certificate, a transcript hash mismatch caused by a MITM altering handshake messages, a buggy signer, or corruption in transit. Also a possible attack indicator (signature forgery attempt).

Understand the failure class

Related errors


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