Skip to content

Retry MDS trust path validation with truncated x5c - #501

Open
emlun wants to merge 1 commit into
mds-trust-root-newsfrom
mds-truncate-trust-path
Open

Retry MDS trust path validation with truncated x5c#501
emlun wants to merge 1 commit into
mds-trust-root-newsfrom
mds-truncate-trust-path

Conversation

@emlun

@emlun emlun commented Sep 4, 2026

Copy link
Copy Markdown
Member

In August 2026, the FIDO Metadata Service (MDS) updated the trust root certificate from GlobalSign R3 to R46. Soon after, the x5c JWT header was adjusted again to include the new R46 root cross-signed by the old R3 root. This x5c that includes the cross-signed R46 fails to validate with trust anchor R46, but validates successfully with trust anchor R3.

CertPathValidator implements RFC 5280, whose section 6 defines certification path validation, but not certification path construction. The validation algorithm only validates the particular cert path provided as input, but does not attempt to discover alternative certification paths.

The library constructs the cert path explicitly using CertificateFactory.generateCertPath(List<Certificate>) with the x5c array from the JWT header, which (currently) includes the trailing cross-signed R46 certificate and therefore fails path validation.

This change makes FidoMetadataDownloader retry the path validation with x5c truncated one cert at a time. This makes the CertPathValidator successfully validate the cross-signed MDS trust path in the 2nd iteration where the cross-signed R46 is omitted from the cert path.

An alternative solution is to use CertPathBuilder to discover the cert path (with the intermediate certs from x5c provided as an untrusted CertStore) instead of taking x5c directly as the trust path. This has the drawback of introducing a new checked CertPathBuilderException into the public API. We can hide the CertPathBuilderException from the public API by wrapping it with CertPathValidatorException, but this effectively strips the reason value from the CertPathValidatorException that would have been thrown previously.

See also: #498

@emlun
emlun requested a review from fdennis September 4, 2026 15:31
In August 2026, the FIDO Metadata Service (MDS) [updated the trust root
certificate](https://fidoalliance.org/mds-changelog/) from GlobalSign R3 to R46.
Soon after, the `x5c` JWT header was adjusted again to include the new R46 root
cross-signed by the old R3 root. This `x5c` that includes the cross-signed R46
fails to validate with trust anchor R46, but validates successfully with trust
anchor R3.

`CertPathValidator` implements RFC 5280, whose section 6 defines certification
path _validation_, but not certification path _construction_. The validation
algorithm only validates the particular cert path provided as input, but does
not attempt to discover alternative certification paths.

The library constructs the cert path explicitly using
`CertificateFactory.generateCertPath(List<Certificate>)` with the `x5c` array
from the JWT header, which (currently) includes the trailing cross-signed R46
certificate and therefore fails path validation.

This change makes `FidoMetadataDownloader` retry the path validation with `x5c`
truncated one cert at a time. This makes the `CertPathValidator` successfully
validate the cross-signed MDS trust path in the 2nd iteration where the
cross-signed R46 is omitted from the cert path.

An alternative solution is to use `CertPathBuilder` to _discover_ the cert
path (with the intermediate certs from `x5c` provided as an untrusted
`CertStore`) instead of taking `x5c` directly as the trust path. This has the
drawback of introducing a new checked `CertPathBuilderException` into the public
API. We can hide the `CertPathBuilderException` from the public API by wrapping
it with `CertPathValidatorException`, but this effectively strips the `reason`
value from the `CertPathValidatorException` that would have been thrown
previously.

See also: #498
@emlun
emlun force-pushed the mds-truncate-trust-path branch from 0127616 to d1e3f64 Compare September 4, 2026 15:34
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

Test Results

   288 files     288 suites   6m 0s ⏱️
 3 393 tests  3 381 ✅ 12 💤 0 ❌
14 520 runs  14 472 ✅ 48 💤 0 ❌

Results for commit d1e3f64.

♻️ This comment has been updated with latest results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants