[opcuaclient] OpcUaClient — CRL/revocation handling #335

Merged
dohertj2 merged 1 commits from auto/opcuaclient/5 into auto/driver-gaps 2026-04-25 16:08:23 -04:00
Owner

Summary

Explicit revoked-cert handling and three new certificate-validation knobs.

  • OpcUaClientDriverOptions.cs — new OpcUaCertificateValidationOptions record exposed as CertificateValidation:
    • RejectSHA1SignedCertificates = true (deliberately tighter default; SHA-1 certs are spec-deprecated)
    • RejectUnknownRevocationStatus = false (conservative for ops without CRL infrastructure)
    • MinimumCertificateKeySize = 2048
  • OpcUaClientDriver.cs — replaced the if (AutoAcceptCertificates) validator hook with an unconditional OnCertificateValidation handler that delegates to a static, testable EvaluateCertificateValidation pipeline returning a CertificateValidationDecision record. Order:
    1. BadCertificateRevoked / BadCertificateIssuerRevokedalways reject with a distinct "REVOKED" log line
    2. SHA-1 OID detection (1.2.840.113549.1.1.5 / 1.2.840.10045.4.1) when flag set
    3. RSA key-size below minimum (skipped for ECC)
    4. BadCertificateRevocationUnknown / BadCertificateIssuerRevocationUnknown gated by the new flag
    5. BadCertificateUntrusted honouring existing AutoAcceptCertificates

Note

: CRL files drop into the SDK's crl/ subdir of each cert store; the SDK auto-discovers them. No code change needed for CRL discovery itself — only the StatusCode inspection on validator failures.

Default change: RejectSHA1SignedCertificates = true is a deliberately tighter default than prior behavior. Existing deployments using SHA-1 server certs (rare) need to flip the flag.

Test plan

  • dotnet build src/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient — clean (0 / 0)
  • dotnet test tests/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests127 / 127 passed (12 new in OpcUaClientCertValidationTests: defaults, every decision-pipeline branch, SHA-1 detection helper, key-size helper). SHA-1 cert synthesis uses a custom X509SignatureGenerator because .NET 10's CertificateRequest.CreateSelfSigned rejects SHA-1.
  • Live revocation tests — skipped (CRL infrastructure required)

🤖 Auto-generated by the Mode-B execution loop. Closes #277.

Closes #277

## Summary Explicit revoked-cert handling and three new certificate-validation knobs. - **`OpcUaClientDriverOptions.cs`** — new `OpcUaCertificateValidationOptions` record exposed as `CertificateValidation`: - `RejectSHA1SignedCertificates = true` (deliberately tighter default; SHA-1 certs are spec-deprecated) - `RejectUnknownRevocationStatus = false` (conservative for ops without CRL infrastructure) - `MinimumCertificateKeySize = 2048` - **`OpcUaClientDriver.cs`** — replaced the `if (AutoAcceptCertificates)` validator hook with an unconditional `OnCertificateValidation` handler that delegates to a **static, testable** `EvaluateCertificateValidation` pipeline returning a `CertificateValidationDecision` record. Order: 1. `BadCertificateRevoked` / `BadCertificateIssuerRevoked` → **always reject** with a distinct "REVOKED" log line 2. SHA-1 OID detection (`1.2.840.113549.1.1.5` / `1.2.840.10045.4.1`) when flag set 3. RSA key-size below minimum (skipped for ECC) 4. `BadCertificateRevocationUnknown` / `BadCertificateIssuerRevocationUnknown` gated by the new flag 5. `BadCertificateUntrusted` honouring existing `AutoAcceptCertificates` > **Note**: CRL files drop into the SDK's `crl/` subdir of each cert store; the SDK auto-discovers them. No code change needed for CRL discovery itself — only the StatusCode inspection on validator failures. > **Default change**: `RejectSHA1SignedCertificates = true` is a deliberately tighter default than prior behavior. Existing deployments using SHA-1 server certs (rare) need to flip the flag. ## Test plan - [x] `dotnet build src/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient` — clean (0 / 0) - [x] `dotnet test tests/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests` — **127 / 127 passed** (12 new in `OpcUaClientCertValidationTests`: defaults, every decision-pipeline branch, SHA-1 detection helper, key-size helper). SHA-1 cert synthesis uses a custom `X509SignatureGenerator` because .NET 10's `CertificateRequest.CreateSelfSigned` rejects SHA-1. - [ ] Live revocation tests — skipped (CRL infrastructure required) 🤖 Auto-generated by the Mode-B execution loop. Closes #277. Closes #277
dohertj2 added 1 commit 2026-04-25 16:08:19 -04:00
Adds explicit revoked-vs-untrusted distinction to the OpcUaClient driver's
server-cert validation hook, plus three new knobs on a new
OpcUaCertificateValidationOptions sub-record:

  RejectSHA1SignedCertificates  (default true — SHA-1 is OPC UA spec-deprecated;
                                 this is a deliberately tighter default)
  RejectUnknownRevocationStatus (default false — keeps brownfield deployments
                                 without CRL infrastructure working)
  MinimumCertificateKeySize     (default 2048)

The validator hook now runs whether or not AutoAcceptCertificates is set:
revoked / issuer-revoked certs are always rejected with a distinct
"REVOKED" log line; SHA-1 + small-key certs are rejected per policy;
unknown-revocation gates on the new flag; untrusted still honours
AutoAccept.

Decision pipeline factored into a static EvaluateCertificateValidation
helper with a CertificateValidationDecision record so unit tests cover
all branches without needing to spin up an SDK CertificateValidator.

CRL files themselves: the OPC UA SDK reads them automatically from the
crl/ subdir of each cert store — no driver-side wiring needed.
Documented on the new options record.

Tests (12 new) cover defaults, every branch of the decision pipeline,
SHA-1 detection (custom X509SignatureGenerator since .NET 10's
CreateSelfSigned refuses SHA-1), and key-size detection. All 127
OpcUaClient unit tests still pass.

Closes #277

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dohertj2 merged commit c6c694b69e into auto/driver-gaps 2026-04-25 16:08:23 -04:00
dohertj2 deleted branch auto/opcuaclient/5 2026-04-25 16:08:23 -04:00
Sign in to join this conversation.