golang/go · error

tls: failed to parse client certificate: {err}

Error message

tls: failed to parse client certificate: {err}

What it means

processCertsFromClient failed to ASN.1-parse one of the client's certificate bytes via x509.ParseCertificate. The bytes are not a valid X.509 certificate. The server alerts decode_error and surfaces the wrapped parse error.

Source

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

	if _, err := hs.c.writeHandshakeRecord(finished, &hs.finishedHash); err != nil {
		return err
	}

	copy(out, finished.verifyData)

	return nil
}

// processCertsFromClient takes a chain of client certificates either from a
// certificateMsg message or a certificateMsgTLS13 message and verifies them.
func (c *Conn) processCertsFromClient(certificate Certificate) error {
	certificates := certificate.Certificate
	certs := make([]*x509.Certificate, len(certificates))
	var err error
	for i, asn1Data := range certificates {
		if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
			c.sendAlert(alertDecodeError)
			return errors.New("tls: failed to parse client certificate: " + err.Error())
		}
		if certs[i].PublicKeyAlgorithm == x509.RSA {
			n := certs[i].PublicKey.(*rsa.PublicKey).N.BitLen()
			if max, ok := checkKeySize(n); !ok {
				c.sendAlert(alertBadCertificate)
				return fmt.Errorf("tls: client sent certificate containing RSA key larger than %d bits", max)
			}
		}
	}

	if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) {
		if c.vers == VersionTLS13 {
			c.sendAlert(alertCertificateRequired)
		} else {
			c.sendAlert(alertHandshakeFailure)
		}
		return errors.New("tls: client didn't provide a certificate")
	}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Regenerate the client certificate with a known-good tool (openssl x509, cfssl) and ensure DER encoding is sent over the wire.
  2. Inspect the wrapped err to identify the ASN.1 failure point (e.g. 'sequence tag mismatch').
  3. Verify the certificate validates independently: openssl x509 -in client.crt -noout -text.
  4. If the client is third-party, request they resend a valid chain.

Example fix

// Before: client sent a PEM-encoded cert (wrong for TLS wire format)
block, _ := pem.Decode(pemBytes)
// forgot to use block.Bytes

// After: send DER bytes
cert := tls.Certificate{
    Certificate: [][]byte{block.Bytes}, // DER, not PEM
    PrivateKey:  priv,
}
Defensive patterns

Strategy: validation

Validate before calling

// Client: validate the cert parses as DER before adding to tls.Certificate.
for i, der := range derCerts {
    if _, err := x509.ParseCertificate(der); err != nil {
        return fmt.Errorf("cert[%d] invalid: %w", i, err)
    }
}
cert := tls.Certificate{Certificate: derCerts, PrivateKey: priv}

Try / catch

// Server: log the wrapped parse error for diagnosis.
if err != nil && strings.Contains(err.Error(), "failed to parse client certificate") {
    log.Warn("client sent malformed cert", "err", err)
}

Prevention

When it happens

Trigger: For each asn1Data entry in certificate.Certificate, x509.ParseCertificate returns an error — malformed DER, truncated bytes, wrong tag, or non-X.509 structure. The server sends alertDecodeError.

Common situations: A client sending a corrupt or truncated certificate chain, a misconfigured client presenting a PEM-encoded (not DER) certificate, a bug in certificate generation, or an attacker injecting garbage into the certificate field.

Understand the failure class

Related errors


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