Skip to content

Validate issuer/CA certificate KeyUsage (keyCertSign, cRLSign) [master378 backport]#3948

Merged
marcschier merged 3 commits into
master378from
copilot/fix-3944-master378
Jul 4, 2026
Merged

Validate issuer/CA certificate KeyUsage (keyCertSign, cRLSign) [master378 backport]#3948
marcschier merged 3 commits into
master378from
copilot/fix-3944-master378

Conversation

@marcschier

Copy link
Copy Markdown
Collaborator

Summary

Backport of the #3944 fix to the master378 (1.5.378) maintenance branch. See the 2.0 PR #3947 for the primary change; this PR applies the equivalent fix to the 1.5.378 code.

The stack did not verify that CA/issuer certificates in a chain assert the KeyUsage required for a CA. Per OPC 10000-6 §6.2.4, Table 52, a CA certificate must carry a KeyUsage extension asserting keyCertSign and cRLSign, and OPC 10000-4 §6.1.3, Table 100 ("Certificate Usage") requires validators to check issuer usage and report Bad_CertificateIssuerUseNotAllowed.

The validator only checked the leaf certificate's KeyUsage and delegated issuer/CA usage to .NET X509Chain, which (RFC 5280 §4.2.1.3) does not reject a CA whose KeyUsage extension is absent — so a CA with no KeyUsage was accepted while strict third-party stacks reject the chain with BadCertificateInvalid.

Changes

  • X509Utils.HasRequiredIssuerKeyUsage(X509Certificate2) — returns whether both keyCertSign and cRLSign are asserted (absent KeyUsage → None → fails).
  • CertificateValidator.InternalValidateAsync — after the leaf KeyUsage check, verifies every issuer/CA certificate in the chain and appends a suppressible Bad_CertificateIssuerUseNotAllowed when the required bits are missing.
  • Two coupled defects fixed in CheckChainStatus:
    1. A FormatException from a named-placeholder format string ({Status}: {Information} → positional {0}: {1}) that crashed validation on any NotValidForUsage chain status.
    2. An inverted isIssuer status-code ternary (issuer↔leaf codes swapped, contrary to Part 4 Table 100).
  • Docs/Certificates.md — new "CA (issuer) KeyUsage validation" subsection.

Every new check/helper carries an in-code citation to the governing spec sections.

Testing

  • New tests in CertificateValidatorTest.cs (matching the branch's existing style: X509Certificate2, classic asserts, throwing contract, event-based approver): a deterministic helper unit test plus reject / suppress / accept integration tests.
  • Full CertificateValidator fixture green on net8.0 and net48, no regressions.

Compatibility

  • This is long-standing behaviour on both master and master378 — not a 2.0 regression. No public API change; the new error is suppressible.

Relates to #3944 · 2.0 PR: #3947

Backport to the 1.5.378 maintenance branch. OPC UA Part 6 6.2.4 Table 52 requires CA/issuer certificates to assert the keyCertSign and cRLSign KeyUsage bits. The validator only checked the leaf certificate's KeyUsage and delegated issuer usage to X509Chain, which per RFC 5280 does not reject a CA whose KeyUsage extension is absent, so non-compliant CAs were accepted (interop failures with strict third-party stacks). Add an explicit, suppressible BadCertificateIssuerUseNotAllowed check (Part 4 6.1.3 Table 100) over each issuer/CA certificate in the chain.

Also fixes two coupled defects in CheckChainStatus: a FormatException from a named-placeholder format string, and an inverted issuer/leaf usage status-code mapping.

Relates to #3944
@marcschier marcschier added the 1.5.378 Only affects 1.5.378 (pre 2.0) label Jul 3, 2026
@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.13%. Comparing base (6a304ef) to head (41e7851).

Additional details and impacted files

Impacted file tree graph

@@              Coverage Diff              @@
##           master378    #3948      +/-   ##
=============================================
- Coverage      60.28%   60.13%   -0.15%     
=============================================
  Files            378      378              
  Lines          79069    79082      +13     
  Branches       13838    13840       +2     
=============================================
- Hits           47664    47555     -109     
- Misses         26989    27105     +116     
- Partials        4416     4422       +6     
Files with missing lines Coverage Δ
...Core/Security/Certificates/CertificateValidator.cs 77.93% <100.00%> (+0.87%) ⬆️
...ack/Opc.Ua.Core/Security/Certificates/X509Utils.cs 75.94% <100.00%> (+0.08%) ⬆️

... and 14 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@marcschier
marcschier requested a review from mrsuciu July 3, 2026 12:04
@marcschier
marcschier marked this pull request as ready for review July 3, 2026 12:06
Copilot AI review requested due to automatic review settings July 3, 2026 12:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.

Backports stricter OPC UA compliance checks by validating that issuer/CA certificates in a chain assert required KeyUsage bits, and fixes related chain-status handling issues.

Changes:

  • Adds issuer/CA KeyUsage validation (keyCertSign, cRLSign) and reports BadCertificateIssuerUseNotAllowed (suppressible).
  • Fixes CheckChainStatus formatting crash and corrects issuer vs leaf status code mapping for NotValidForUsage.
  • Adds tests and documentation describing CA (issuer) KeyUsage validation and suppression behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
Tests/Opc.Ua.Core.Tests/Security/Certificates/CertificateValidatorTest.cs Adds unit + integration tests for issuer KeyUsage enforcement, suppression, and acceptance.
Stack/Opc.Ua.Core/Security/Certificates/X509Utils.cs Introduces helper to verify issuer/CA KeyUsage requirements.
Stack/Opc.Ua.Core/Security/Certificates/CertificateValidator.cs Validates issuer/CA KeyUsage during chain validation; fixes NotValidForUsage status handling.
Docs/Certificates.md Documents new CA KeyUsage validation behavior and suppression guidance.

Comment thread Tests/Opc.Ua.Core.Tests/Security/Certificates/CertificateValidatorTest.cs Outdated
Comment thread Tests/Opc.Ua.Core.Tests/Security/Certificates/CertificateValidatorTest.cs Outdated

@mrsuciu mrsuciu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks Ok, tests have copilot comments

Comment thread Stack/Opc.Ua.Core/Security/Certificates/CertificateValidator.cs
… event in finally

Addresses PR review: the None-KeyUsage case exercised a present-but-empty extension, not an absent one. Add CreateCertificateWithoutKeyUsage (built via CertificateRequest so no KeyUsage extension is emitted) and assert the helper rejects it. Switch the test CA to a relative validity window (now minus one day) so it is always valid. Wrap the CertificateValidation event subscription in try/finally so the handler is always detached even if validation throws.
@marcschier marcschier added the ready Ready to merge once CI Passes label Jul 3, 2026
CI failed on Linux/macOS: the OpenSSL chain engine raises extra suppressible errors (BadCertificateUntrusted, BadCertificateChainIncomplete/PartialChain) for a self-signed CA with empty/absent KeyUsage that Windows CryptoAPI does not, so the fixed 4-code approver rejected the cert. Approve every error via the CertificateValidation event (e.Accept) and assert the issuer-use error was among them, making the test independent of the platform chain implementation. Verified on Linux via WSL.
@marcschier
marcschier merged commit 21eccdd into master378 Jul 4, 2026
48 checks passed
@marcschier
marcschier deleted the copilot/fix-3944-master378 branch July 4, 2026 03:48
marcschier added a commit that referenced this pull request Jul 4, 2026
## Summary

Follow-up to #3948 (merged) that fixes a **macOS-only** CI failure
introduced by the issuer/CA KeyUsage tests.

On the macOS Core test job, three tests failed with:

```
Interop+AppleCrypto+AppleCommonCryptoCryptographicException : Unknown format in import.
   at System.Security.Cryptography.X509Certificates.X509Pal.AppleX509Pal.GetCertContentType(...)
   at System.Security.Cryptography.X509Certificates.X509CertificateLoader.LoadCertificate(...)
```

The affected tests built a test CA with an **empty** KeyUsage extension
(`new X509KeyUsageExtension(X509KeyUsageFlags.None, true)` → a zero-bit
`BIT STRING`). Windows (CryptoAPI) and Linux (OpenSSL) load such a
certificate, but macOS (AppleCrypto) rejects it as an unknown format.
The production code is unaffected — this is purely a test-certificate
artifact.

## Changes

- Removed the degenerate empty-KeyUsage test certificate. The
**truly-absent** KeyUsage case (built via `CertificateRequest` so no
KeyUsage extension is emitted) already covers the `GetKeyUsage() ==
None` code path.
- The reject/suppress integration tests now use a
present-but-insufficient `DigitalSignature` KeyUsage (a valid,
macOS-loadable certificate that is still missing
`keyCertSign`/`cRLSign`).

## Testing

- `CertificateValidator` IssuerKeyUsage tests pass on **Windows** and
**Linux** (net10.0). The change directly removes the macOS root cause
(the empty-KeyUsage extension); all remaining test certificates use
standard formats that macOS already loads (the compliant-CA test passed
on macOS).

Relates to #3944 · follow-up to #3948
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.5.378 Only affects 1.5.378 (pre 2.0) ready Ready to merge once CI Passes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants