Golang x509.VerifyOptions CertificatePolicies are not validated during certificate verification?

5 days ago 4
ARTICLE AD BOX

The CertificatePolicies field in x509.VerifyOptions does not work as a filter that rejects certificates whose policies don't match your list. Its semantics follow RFC 5280's certificate policy validation algorithm, which is more nuanced than a simple "does the cert contain these OIDs?" check.

Why verification still succeeds

Go's x509 policy validation is based on the RFC 5280 path validation algorithm (Section 6). Under this algorithm, policy enforcement only kicks in when the certificate chain explicitly constrains policies through extensions like:

PolicyConstraints – forces explicit policy matching at or below a certain depth in the chain.

InhibitAnyPolicy – restricts use of the anyPolicy OID.

requireExplicitPolicy counter in PolicyConstraints – when this reaches zero, every certificate in the remaining chain must have an acceptable policy.

If none of these constraints are present in your CA or intermediate certificates, the path validation algorithm allows any policy (or no policy) in the end-entity certificate, and verification succeeds regardless of what you pass in CertificatePolicies.

In short: CertificatePolicies in VerifyOptions seeds the initial-policy-set for the RFC 5280 algorithm. It does not cause Go to hard-fail when the leaf cert's policies differ from that set, unless the chain's own extensions demand explicit policy matching.

How to enforce policy checking yourself

After calling cert.Verify(...), inspect the leaf certificate's policy OIDs manually:

go chains, err := cert.Verify(x509.VerifyOptions{ Roots: opts.Roots, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, }) if err != nil { return err } // Manually verify the required policy OID is present required := x509.OID{} // your required OID found := false for _, oid := range cert.Policies { if oid.Equal(required) { found = true break } } if !found { return fmt.Errorf("certificate does not contain required policy OID") }

If you control the CA

Make the CA certificate include a PolicyConstraints extension with requireExplicitPolicy: 0. This forces Go's path validation to enforce that every certificate in the chain asserts one of the policies in the initial-policy-set, which is what you originally expected CertificatePolicies to do automatically.

Reference

See the Go source in crypto/x509/verify.go — the checkChainForKeyUsage and policy-related functions follow the RFC 5280 Section 6.1 state machine. The CertificatePolicies field was added in Go 1.24 (proposal #68529) specifically to support this algorithm, not as a simple allow-list filter.

Read Entire Article