{"record":{"id":"d9d0daee29d86094","repo":"golang/go","slug":"tls-invalid-signature-by-the-client-certificate-d9d0da","errorCode":null,"errorMessage":"tls: invalid signature by the client certificate: %s","messagePattern":"tls: invalid signature by the client certificate: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_server_tls13.go","lineNumber":1104,"sourceCode":"\t\t// We don't use certReq.supportedSignatureAlgorithms because it would\n\t\t// require keeping the certificateRequestMsgTLS13 around in the hs.\n\t\tif !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms(c.vers, c.vers)) ||\n\t\t\t!isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, signatureSchemesForPublicKey(c.vers, c.peerCertificates[0].PublicKey)) {\n\t\t\tc.sendAlert(alertIllegalParameter)\n\t\t\treturn errors.New(\"tls: client certificate used with invalid signature algorithm\")\n\t\t}\n\t\tsigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)\n\t\tif err != nil {\n\t\t\treturn c.sendAlert(alertInternalError)\n\t\t}\n\t\tif sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {\n\t\t\treturn c.sendAlert(alertInternalError)\n\t\t}\n\t\tsigned := signedMessage(clientSignatureContext, hs.transcript)\n\t\tif err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,\n\t\t\tsigHash, signed, certVerify.signature); err != nil {\n\t\t\tc.sendAlert(alertDecryptError)\n\t\t\treturn errors.New(\"tls: invalid signature by the client certificate: \" + err.Error())\n\t\t}\n\t\tc.peerSigAlg = certVerify.signatureAlgorithm\n\n\t\tif err := transcriptMsg(certVerify, hs.transcript); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// If we waited until the client certificates to send session tickets, we\n\t// are ready to do it now.\n\tif err := hs.sendSessionTickets(); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (hs *serverHandshakeStateTLS13) readClientFinished() error {","sourceCodeStart":1086,"sourceCodeEnd":1122,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_server_tls13.go#L1086-L1122","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the client certificate and private key are a matching pair (openssl x509 -noout -modulus vs openssl rsa -noout -modulus for RSA).","Check for any proxy/MITM that could alter handshake messages between client and server.","If using a hardware token/smart card, verify it correctly signs the TLS transcript.","Update the client TLS library — older versions may compute the transcript hash incorrectly.","Compare the client's signature input (transcript hash) against what the server expects."],"exampleFix":"// Verify cert/key match (on the client side)\n// For RSA certs:\n// openssl x509 -noout -modulus -in client.crt | openssl md5\n// openssl rsa -noout -modulus -in client.key | openssl md5\n// Both must produce identical output.\n// If they differ, regenerate the certificate from the correct key.","handlingStrategy":"try-catch","validationCode":"// Verify cert/key match before using them\nfunc verifyCertKeyPair(cert *x509.Certificate, key crypto.PrivateKey) error {\n    switch pub := cert.PublicKey.(type) {\n    case *rsa.PublicKey:\n        rsaKey, ok := key.(*rsa.PrivateKey)\n        if !ok || pub.N.Cmp(rsaKey.N) != 0 {\n            return errors.New(\"RSA certificate and key do not match\")\n        }\n    case *ecdsa.PublicKey:\n        ecKey, ok := key.(*ecdsa.PrivateKey)\n        if !ok || pub.X.Cmp(ecKey.X) != 0 {\n            return errors.New(\"ECDSA certificate and key do not match\")\n        }\n    case ed25519.PublicKey:\n        edKey, ok := key.(ed25519.PrivateKey)\n        if !ok || !bytes.Equal(pub, edKey.Public().(ed25519.PublicKey)) {\n            return errors.New(\"Ed25519 certificate and key do not match\")\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// Server-side: handle during mTLS handshake\nif err := conn.Handshake(); err != nil {\n    if strings.Contains(err.Error(), \"invalid signature by the client certificate\") {\n        log.Printf(\"client cert signature verification failed: %v\", err)\n    }\n}","preventionTips":["Verify client cert/key pair consistency before distribution.","Test mTLS client auth end-to-end with known-good certs.","Monitor for MITM interference if signature errors appear unexpectedly.","Use modern, well-tested client TLS libraries."],"tags":["tls","tls13","mtls","client-certificate","signature-verification","server-side"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}