Select the CRL signed by the path's issuer when CAs share a subject name - #1944
Merged
Conversation
OpcUaCertificateRevocationChecker reaches into sun.security.provider.certpath by reflection and bails in its constructor when that access is denied, so without --add-opens every validation falls back to the default JDK checker and logs a warning. The test JVM had no such option, which meant no test in the project has ever run the custom checker, and the two paths differ in whether ValidationCheck.REVOCATION_LISTS is honored. Open the package for surefire so the checker under test is the one production uses when the option is present.
When the custom revocation checker cannot be installed, the fallback builds a PKIXRevocationChecker, sets NO_FALLBACK, PREFER_CRLS and SOFT_FAIL on it, and then never adds it to the PKIXParameters. Only setRevocationEnabled(true) takes effect, so the default checker runs with default options and a CRL that cannot be located fails the whole path rather than being tolerated, contradicting the warning logged alongside it.
PKIX selects a CRL by issuer name alone. Handed several candidates it does not try each in turn: it takes the indirect-CRL route, fails to build a path to the CRL signer, and reports UNDETERMINED_REVOCATION_STATUS, so the whole path fails even though the right CRL was present. A Global Discovery Server produces exactly that trust list. A certificate group that offers more than one certificate type issues one CA per type, all under the group's single configured subject name, and publishes a CRL for each, so a client that pulls its trust list from such a group cannot validate any peer the group certified. Drop a CRL when the path holds a same-named certificate but none of them signed it. A CRL whose issuer name matches nothing in the path belongs to some other path and is left alone. CrlTestUtil now emits the AuthorityKeyIdentifier and CRL number a real CA writes. The extension matters: with a key-id-only AKI the JDK resolves the right CRL and the failure does not reproduce, so a test built on JcaX509ExtensionUtils.createAuthorityKeyIdentifier alone passes either way.
Use the existing Bouncy Castle routing when verifying Brainpool CRL signatures so unsupported SunEC curves cannot discard valid revocation information. Keep default provider selection for other issuer keys. Cover decoded P256 and P384 CRLs with revoked and unrevoked certificates, including required and optional CRL checks. Document the validation flow and provider invariant at package level.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A client cannot validate any peer certified by a Global Discovery Server certificate group that offers more than one certificate type.
Such a group issues one CA per certificate type, all under the group's single configured subject name, and publishes a CRL for each. The OPC Foundation .NET reference GDS does this by design, so it is what real trust lists look like: several same-DN CA certificates and several same-DN CRLs.
PKIX selects a CRL by issuer name alone. Given more than one candidate it does not try each in turn — it takes the indirect-CRL route through
DistributionPointFetcher.verifyCRL, fails to build a path to the CRL signer, and reportsUNDETERMINED_REVOCATION_STATUS.validateTrustedCertPathturns that intoBad_CertificateRevocationUnknownand the handshake dies, even though the correct CRL was in the collection and verifies cleanly. The outcome does not depend on CRL order, and two same-named CAs are enough.I hit this against a real GDS: an Ignition Gateway pulling from a group carrying RSA, NIST P-256 and P-384 was rejected by a Milo server on every connection. Each connection came up as soon as all but its own CA was removed from the server's trust list.
What changed
selectApplicableCrlsfilters the collection before it reaches PKIX. A CRL is dropped when the path holds a same-named certificate and none of them signed it. A CRL whose issuer name matches nothing in the path belongs to some other path and is left alone, so this only removes candidates that were going to be ambiguous.The fallback now registers the revocation checker it configures. When the custom checker cannot be installed, the fallback built a
PKIXRevocationChecker, setNO_FALLBACK,PREFER_CRLSandSOFT_FAILon it, and never calledaddCertPathChecker. OnlysetRevocationEnabled(true)took effect, so the default checker ran with default options and a CRL that could not be located failed the whole path — the opposite of what the warning logged beside it promises.stack-core's surefire run opens
sun.security.provider.certpath.OpcUaCertificateRevocationCheckerreaches into that package by reflection and bails in its constructor when access is denied, so without the option every validation silently falls back. No test in the project had ever run the custom checker. The two paths differ on whetherValidationCheck.REVOCATION_LISTSis honored, and this bug is only visible on the custom one — with the fallback'sSOFT_FAILnow correctly applied, the symptom is suppressed rather than fixed.Notes for review
The new tests only discriminate because
CrlTestUtilnow emits the AuthorityKeyIdentifier and CRL number a real CA writes. With a key-id-only AKI the JDK resolves the right CRL and the failure does not reproduce at all, so a test built onJcaX509ExtensionUtils.createAuthorityKeyIdentifieralone passes with or without the fix. I verified the discrimination by reverting each change in turn.I also checked the fix against the bytes from the live failure — the issued leaf, the three same-DN CAs and their three CRLs — outside this test harness: unfiltered gives
UNDETERMINED_REVOCATION_STATUS, filtered gives a valid path.stack-core1244 tests and thesdk-server/integration-testsrun (1288) pass. Each commit builds and tests green on its own. Unrelated and pre-existing:BinaryDataTypeDictionaryReaderTestindtd-readerfails withNoClassDefFoundError: com/google/common/io/ByteStreamson a clean tree as well.