grpc/grpc-go · error

extractCRLIssuer: invalid ASN.1 encoding

Error message

extractCRLIssuer: invalid ASN.1 encoding

What it means

Returned by extractCRLIssuer when the cryptobyte walk over the CRL's DER structure fails one of: outer SEQUENCE, tbsCertList SEQUENCE, optional version INTEGER, signature AlgorithmIdentifier SEQUENCE, or the issuer Name SEQUENCE. Any failure means the bytes are not a well-formed CRL and the issuer DN cannot be extracted, so the CRL is unusable.

Solutions

  1. Verify the bytes are a CRL: decode PEM (type "X509 CRL") or pass valid DER; check with openssl crl -inform DER/PEM -text -noout.
  2. Fetch the CRL URL with curl -sS to confirm the response Content-Type and body are a CRL, not an error page.
  3. Strip any text artifacts (HTTP headers, leading whitespace) before parsing.
  4. Point the CRL provider at the correct distributionPoint URI listed in the certificate's CRLDistributionPoints extension.

Example fix

// before: feeding the raw HTTP response body that included headers
//   der := resp.Body // contains "HTTP/1.1 200 OK\r\n..."
//   issuer, _ := extractCRLIssuer(der)
// after: decode as PEM or pass only the DER body
//   der := crlPemToDer(resp.Body) // strips PEM framing if present
//   issuer, err := extractCRLIssuer(der)
//   if err != nil { /* log and skip this CRL */ }
Defensive patterns

Strategy: try-catch

Validate before calling

// Sanity-check bytes look like a CRL (PEM or DER SEQUENCE) before parsing.
func looksLikeCRL(b []byte) bool {
    if bytes.HasPrefix(b, []byte("-----BEGIN X509 CRL")) { return true }
    if len(b) > 1 && b[0] == 0x30 { return true } // DER SEQUENCE tag
    return false
}

Try / catch

Wrap CRL loading: if extractCRLIssuer or x509.ParseRevocationList fails, log the URL and bytes length, do not install, and retry with backoff. Keep previous CRL on disk.

Prevention

When it happens

Trigger: extractCRLIssuer is given bytes that are not a valid X.509 CRL DER encoding (or PEM that decodes to such). Common when the bytes are actually a different ASN.1 structure, a truncated file, or text with embedded newlines that were not stripped.

Common situations: CRL URL returns an HTML error page or JSON status instead of DER/PEM bytes. PEM-to-DER conversion bug leaves the header text in place. Wrong distribution point served (e.g. a DeltaCRL or a cert instead of a CRL). Network proxy injecting content.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/892656517f6b6fd5. Report an issue: GitHub.

Appendix: source

Thrown at security/advancedtls/crl.go:417

	return crlBytes
}

// extractCRLIssuer extracts the raw ASN.1 encoding of the CRL issuer. Due to the design of
// pkix.CertificateList and pkix.RDNSequence, it is not possible to reliably marshal the
// parsed Issuer to its original raw encoding.
func extractCRLIssuer(crlBytes []byte) ([]byte, error) {
	if bytes.HasPrefix(crlBytes, crlPemPrefix) {
		crlBytes = crlPemToDer(crlBytes)
	}
	der := cryptobyte.String(crlBytes)
	var issuer cryptobyte.String
	// This doubled der.ReadASN1 is intentional, it modifies the input buffer
	if !der.ReadASN1(&der, cbasn1.SEQUENCE) ||
		!der.ReadASN1(&der, cbasn1.SEQUENCE) ||
		!der.SkipOptionalASN1(cbasn1.INTEGER) ||
		!der.SkipASN1(cbasn1.SEQUENCE) ||
		!der.ReadASN1Element(&issuer, cbasn1.SEQUENCE) {
		return nil, errors.New("extractCRLIssuer: invalid ASN.1 encoding")
	}
	return issuer, nil
}

// parseRevocationList comes largely from here
// x509.go:
// https://github.com/golang/go/blob/e2f413402527505144beea443078649380e0c545/src/crypto/x509/x509.go#L1669-L1690
// We must first convert PEM to DER to be able to use the new
// x509.ParseRevocationList instead of the deprecated x509.ParseCRL
func parseRevocationList(crlBytes []byte) (*x509.RevocationList, error) {
	if bytes.HasPrefix(crlBytes, crlPemPrefix) {
		crlBytes = crlPemToDer(crlBytes)
	}
	crl, err := x509.ParseRevocationList(crlBytes)
	if err != nil {
		return nil, err
	}
	return crl, nil

View on GitHub (pinned to 0c51461d27)