golang/go · error

tls: invalid signature by the client certificate: %s

Error message

tls: invalid signature by the client certificate: %s

What it means

The server failed to verify the client's CertificateVerify signature using verifyHandshakeSignature with the client certificate's public key. The underlying error (appended via %s) indicates the specific verification failure: bad signature bytes, wrong hash, or key/signature mismatch. This means the client's signature over the handshake transcript did not validate against the public key in the client certificate.

Source

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

		// We don't use certReq.supportedSignatureAlgorithms because it would
		// require keeping the certificateRequestMsgTLS13 around in the hs.
		if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms(c.vers, c.vers)) ||
			!isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, signatureSchemesForPublicKey(c.vers, c.peerCertificates[0].PublicKey)) {
			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 sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
			return c.sendAlert(alertInternalError)
		}
		signed := signedMessage(clientSignatureContext, hs.transcript)
		if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
			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.transcript); err != nil {
			return err
		}
	}

	// If we waited until the client certificates to send session tickets, we
	// are ready to do it now.
	if err := hs.sendSessionTickets(); err != nil {
		return err
	}

	return nil
}

func (hs *serverHandshakeStateTLS13) readClientFinished() error {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Verify the client certificate and private key are a matching pair (openssl x509 -noout -modulus vs openssl rsa -noout -modulus for RSA).
  2. Check for any proxy/MITM that could alter handshake messages between client and server.
  3. If using a hardware token/smart card, verify it correctly signs the TLS transcript.
  4. Update the client TLS library — older versions may compute the transcript hash incorrectly.
  5. Compare the client's signature input (transcript hash) against what the server expects.

Example fix

// Verify cert/key match (on the client side)
// For RSA certs:
// openssl x509 -noout -modulus -in client.crt | openssl md5
// openssl rsa -noout -modulus -in client.key | openssl md5
// Both must produce identical output.
// If they differ, regenerate the certificate from the correct key.
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify cert/key match before using them
func verifyCertKeyPair(cert *x509.Certificate, key crypto.PrivateKey) error {
    switch pub := cert.PublicKey.(type) {
    case *rsa.PublicKey:
        rsaKey, ok := key.(*rsa.PrivateKey)
        if !ok || pub.N.Cmp(rsaKey.N) != 0 {
            return errors.New("RSA certificate and key do not match")
        }
    case *ecdsa.PublicKey:
        ecKey, ok := key.(*ecdsa.PrivateKey)
        if !ok || pub.X.Cmp(ecKey.X) != 0 {
            return errors.New("ECDSA certificate and key do not match")
        }
    case ed25519.PublicKey:
        edKey, ok := key.(ed25519.PrivateKey)
        if !ok || !bytes.Equal(pub, edKey.Public().(ed25519.PublicKey)) {
            return errors.New("Ed25519 certificate and key do not match")
        }
    }
    return nil
}

Try / catch

// Server-side: handle during mTLS handshake
if err := conn.Handshake(); err != nil {
    if strings.Contains(err.Error(), "invalid signature by the client certificate") {
        log.Printf("client cert signature verification failed: %v", err)
    }
}

Prevention

When it happens

Trigger: Server is performing mTLS client cert authentication. The client sent a CertificateVerify message whose signature doesn't match: could be a corrupted signature in transit, a client signing the wrong data (wrong transcript hash), a mismatched key pair (cert doesn't match the signing key), or a man-in-the-middle altering the handshake.

Common situations: Client cert/key mismatch (cert was reissued but old key is used); MITM proxy altering handshake messages; client library bug in computing the transcript hash to sign; hardware token (smart card) signing failure that produces invalid output; client using a different TLS implementation for signing vs. cert generation.

Understand the failure class

Related errors


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