golang/go · error

tls: invalid signature by the client certificate

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.

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.

Appendix: 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)