Files
UA-.NETStandard/docs/WhatsNewIn2.0.md
romanett 7fe7794a19 Enable AES-GCM and ChaCha20-Poly1305 policies on net48 via BouncyCastle (stacked on #4703) (#4705)
> Follow-up to #4703 (now merged to master; this PR has been retargeted
to `master`).

Part of #4510. #4703 enabled the CBC ECC policies on net48 through a
BouncyCastle raw-ECDH polyfill. The AEAD policies were still unavailable
there because .NET Framework has no `AesGcm` or `ChaCha20Poly1305`. This
PR adds those ciphers the same way, so on net48 all of these are now
offered:
`ECC_{nistP256,nistP384,brainpoolP256r1,brainpoolP384r1}_AesGcm` /
`_ChaChaPoly`, `RSA_DH_AesGcm`, `RSA_DH_ChaChaPoly`.

## Changes
- **Polyfill**
`src/Opc.Ua.Security.Certificates/Org.BouncyCastle/AeadCiphers.cs`
(internal, `#if NETFRAMEWORK`, next to `EcdhRawAgreement`): `AesGcm` and
`ChaCha20Poly1305` types with the .NET 8 BCL surface. They wrap
BouncyCastle's `GcmBlockCipher(AesEngine)` and
`Org.BouncyCastle.Crypto.Modes.ChaCha20Poly1305`.
- A tag mismatch throws `CryptographicException`, never BC's
`InvalidCipherTextException`, so
`PlatformSymmetricCryptoProvider.DecryptAuthenticated` still returns
`false` and the channel still raises a protocol error.
- GCM releases plaintext blocks before it checks the tag, so plaintext
stays in a scratch buffer and is copied out only after the tag verifies.
On failure the destination is cleared, as the BCL does. Scratch buffers
and the key copy are zeroed.
- **Opc.Ua.Core**: aliases the polyfill types on `NETFRAMEWORK` and
removes the `NET8_0_OR_GREATER` gates in `SecurityPolicies`
(`SupportsAesGcmPolicy` / `SupportsChaCha20Poly1305Policy`),
`CryptoUtils` (`SymmetricEncryptAndSign` / `SymmetricDecryptAndVerify`,
AEAD helpers) and `PlatformSymmetricCryptoProvider`. The channel, the
provider facet and the encrypted ECC user tokens (`EncryptedSecret`,
through `CryptoUtils`) now run the same code on every target. .NET 8+
keeps the BCL ciphers.
- **FIPS** (requested by the #4703 session, after a2ecf13b1): under
`CryptoCompliancePolicy.FipsOnly`, `CryptoCompliance.IsPolicyPermitted`
now also withholds every AEAD policy on .NET Framework. That adds
`RSA_DH_AesGcm`, which is FIPS-approved and not ECC. The BouncyCastle
cipher runs outside any validated module and the user-token path
bypasses the provider registry, so the auditor cannot see it. Tests and
`docs/CryptoProvider.md` are updated to match.
- **Docs**: EccProfiles (Known Limitations), migration `certificates.md`
(and the bundled skill copy), ProductionChecklist, Profiles,
WhatsNewIn2.0, CryptoProvider. They state that on net48 these primitives
run in managed BouncyCastle, which is not a validated (FIPS) module and
not hardware-accelerated or hardened against cache-timing attacks like
CNG AES.

## Tests
- New `AeadPolyfillTests` (Core.Tests):
- Known answers: NIST GCM test cases 2/4 (AES-128) and 14/16 (AES-256),
plus RFC 8439 §2.8.2 for ChaCha20-Poly1305. They run through
`PlatformSymmetricCryptoProvider` on every TFM and through the polyfill
directly.
- Tampering with the tag, the ciphertext or the AAD gives `false` from
the provider, and `CryptographicException` with a cleared plaintext from
the polyfill.
  - Argument validation.
- **BCL cross-check:** on .NET 8+, Core.Tests also compiles
`AeadCiphers.cs` (`AEAD_POLYFILL_CROSSCHECK`, test-only
`BouncyCastle.Cryptography` reference). Polyfill and BCL must produce
identical ciphertext and tag, and each must decrypt the other's output:
3 algorithms × 8 message lengths (0 to 8192) × 3 AAD lengths.
- `CryptoProviderRegistryTests`: `RSA_DH_AesGcm` is permitted under
FipsOnly only off .NET Framework and withheld on it.
- Tests that `Assert.Ignore` when AEAD is unavailable now run on net48,
e.g. `EccSecretRejectsDifferentNegotiatedPolicyAsync` and the
`RSA_DH_AesGcm` server session tests.

### Local results
| Run | Result |
|---|---|
| Core.Tests net48, full (before the FIPS commit) | 6049 passed, 0
failed, 88 skipped |
| Core.Tests crypto/AEAD/ECC/EncryptedSecret/Nonce subset, net48 /
net10.0 | 278 / 343 passed, 0 skipped |
| Server.Tests session-security/ECC subset, net48 | 128 passed, 0
skipped |
| `EccInteropTests` net48 vs **open62541 1.5.8 (OpenSSL)** peer | all 16
AEAD cases pass, 4 of them two-renewal token-renewal runs
(`ECC_nistP256_AesGcm`/`_ChaChaPoly` × Sign/SignAndEncrypt ×
Anonymous/UserName, both directions). Remaining 8 failures are the
documented open62541 CBC Sign-only HKDF differences in
`expected-differences.json` |
| `EccInteropTests` net48 vs 1.5.378 LegacyPeer | unchanged 26 passed /
28 skipped |

Note on the acceptance criterion: the 1.5.378 LegacyPeer does not
implement the AEAD policies, so its
`_AesGcm`/`_ChaChaPoly`/`*AeadTokenRenewalAsync` cases skip on every TFM
("The peer (UA-.NETStandard) does not declare …"). Before this PR they
skipped on the 2.0 side; now they get past it and stop at the peer
check. The open62541 peer is the real cross-stack check of the net48
AEAD path against a non-BouncyCastle implementation.

## PubSub DTLS on net48
The DTLS 1.3 transport (`src/Opc.Ua.PubSub.Udp/Dtls`) was compiled out
on .NET Framework, so net48 registered no DTLS profiles. It now reuses
the same polyfills (`Opc.Ua.Security.Certificates` gains
`InternalsVisibleTo Opc.Ua.PubSub.Udp`):
- The `NET8_0_OR_GREATER` gates in the handshake, ECDHE, record
protection and profile probe are removed.
- Two .NET 8 APIs get net48 fallbacks: the AES-ECB record-number mask
uses `CreateEncryptor`, and the handshake random uses
`RandomNumberGenerator.Create`.
- The HKDF probe now checks the managed `DtlsHkdf` that the key schedule
actually uses.
- The public `IsAeadSupported` / `IsRawSharedSecretSupported` probes are
kept and now return `true` everywhere.
- The DTLS tests now also run on net48. The net48 "registers no
profiles" test now asserts that the NIST AEAD profiles are registered.
The socket transport-peer tests and the benchmarks stay .NET 8-only
because they use .NET 8 socket APIs.
- `docs/PubSub.md` DTLS profile table: the net48 column is updated.

Results: PubSub.Udp.Tests net48, full suite: **229 passed, 0 skipped**.
It includes complete NIST AEAD, brainpool and integrity-only handshakes
and ChaCha20 records. The DTLS subset is 76 on net48 vs 81 on net10.0;
the 5 still-gated socket-peer tests are the difference.

Note: DTLS profile selection does not consult `CryptoCompliancePolicy`
on any target. That is pre-existing and unchanged here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 06:30:14 +02:00

12 KiB

What's New in OPC UA .NET Standard 2.0

Version 2.0 expands the SDK beyond the 1.5.378 client/server APIs with managed connections, model-driven application development, injectable services, and distributed deployment options. This overview highlights what those changes enable; the linked guides contain API contracts, examples, and limitations.

Contents

At a glance

Area What 2.0 enables
Application development Host clients and servers through one dependency-injection builder; generate model types and typed client proxies instead of writing service plumbing.
Client connections Use ManagedSession for reconnection and failover, with callback-based or asynchronous streaming subscriptions.
Data and deployment Use strongly typed values, arrays, and matrices; publish supported applications with Native AOT.
Server capabilities Add historian providers, alarms, state machines, file access, and the models in the specification catalogue through reusable services.
Security and discovery Manage certificate lifecycles and identities through providers, and integrate GDS pull/push management and token services.
PubSub and availability Compose publishers/subscribers with transport packages, or opt into redundancy and distributed state for client/server deployments.

Feature details

Dependency injection and hosting

services.AddOpcUa() provides a common entry point for clients, servers, and feature libraries. The .NET Generic Host manages server lifetime; options, telemetry, identities, certificates, and application providers use the same container. Direct construction remains available. See Dependency Injection.

Client

ManagedSession manages connection recovery behind a fluent builder while the classic Session remains available for applications that own that lifecycle. The V2 subscription API adds options-based callbacks, triggering relationships, automatic partitioning, and IAsyncEnumerable streams for short-lived monitoring. Typed helpers cover history, alarms, files, and model-change tracking. See Sessions, Subscriptions, File System Client, and Model Change Tracking.

Source generators and modeling

ModelDesign and NodeSet2 inputs generate node-manager scaffolding, state types, and typed ObjectType client proxies. POCO annotations generate IEncodeable implementations, and model dependencies can be registered across assemblies. Models that change independently of the binary can instead be loaded at runtime. See Node Managers, Source-Generated Data Types, Model Dependencies, and Runtime NodeSets.

Type system and immutability

Readonly value types and the Variant-based API reduce boxing and make OPC UA values explicit. ByteString, ArrayOf<T>, and MatrixOf<T> represent binary, array, and matrix data. Runtime complex-type loading supports server-defined structures and enumerations without requiring a generated CLR class for each type. See Complex Types and Schema Generation.

Performance, memory, and pooling

Value-type representations and pooled buffers reduce allocations in encoding and notification paths. Server work also improves resource lifetime and repeated permission checks. These changes do not imply that every workload is faster; use the measured results and sizing guidance for your deployment. See Benchmarks, Server Scalability, and Rate Limiting.

Async, cancellation, and TimeProvider

AsyncCustomNodeManager supports asynchronous service and data-source operations. Request cancellation follows the request lifetime, while TimeProvider enables controlled clocks and timers for testing. Existing synchronous compatibility APIs do not make blocking waits appropriate in new asynchronous handlers. See Async Server Support and Node Managers.

Native AOT

Source-generated models, runtime type representations, and revised serialization paths support applications published without a JIT compiler. Choose AOT-compatible providers and the default complex-type builder; the optional Reflection.Emit builder still requires runtime code generation. See Native AOT: publish an application and Complex Types.

OPC UA companion-spec coverage

The SDK adds model libraries, hosting support, and client helpers for the following services and domains. Core OPC UA specification parts are listed separately from domain-specific companions and draft extensions.

Core specification services and models

Capability Specification Guide
Alarms and conditions OPC 10000-9 Alarms and Conditions
Historical access and aggregates OPC 10000-11 and OPC 10000-13 Historical Access and Aggregates
State machines OPC 10000-16 State Machines
Alias names OPC 10000-17 Alias Names
Node and reference management OPC 10000-4 Node Management
File transfer and file systems OPC 10000-5 Annex C and OPC 10000-20 File System Client
Roles and user management OPC 10000-18 Role-Based User Management
Global discovery and certificate management (GDS) OPC 10000-12 Global Discovery Server
Local discovery (LDS and LDS-ME) OPC 10000-12 LDS / LDS-ME and the Console LDS Server sample
Device onboarding: registrar ticket administration OPC 10000-21 OnboardingClient and the onboarding sample

Companion and extension models

Domain Specification or status Guide
Device composition and software updates OPC 10000-100 (Devices) Device Integration and Software Update
Machines, job management, process values, energy and result transfer OPC 40001-1/-2/-3/-4/-101 (Machinery), with OPC 34100 (Energy Consumption Management) and OPC 30081 (PA-DIM) Machinery
Pumps, compressors and vacuum pumps OPC 40223 (Pumps and Vacuum Pumps) Pumps
Scales and scale systems OPC 40200 (Weighing Technology), with OPC 30050 (PackML) Scales
Motion-device systems and controllers OPC 40010-1 (Robotics), over OPC 10000-200 (Industrial Automation, its own Opc.Ua.IA package) Robotics
Task-level robot commands Robot Intent (draft) Robot Intent
Manufacturing resources and job control OPC 10030 (ISA-95 Common Model) and OPC 10031-4 (Job Control V1/V2) ISA-95
Relative and geographic positioning OPC 10000-210 (RSL) and OPC 10000-211 (GPOS) Positioning
Perception, media, and feedback Vision (draft) Vision
Model catalogues, deployments, and inference AI Model Management and Inference (draft) AI Model Management
Resource registration, versioning, and federation xRegistry abstract base model (draft) xRegistry
Scene bindings and address-space materialization OpenUSD Bindings and OpenUSD Scene (both draft) OpenUSD
Web of Things connectivity and conversion OPC 10100-1, with preview registry extensions WoT Connectivity, WoT / NodeSet Conversion, and WoT Bindings

The draft Generators model in the generator server is a sample-local model, not a standalone SDK library.

Draft entries are identified by model name rather than an unverified specification number; their namespace URIs and NodeIds remain provisional. The guides describe provider requirements and supported operations. Model coverage does not imply complete runtime implementation or certification for every specification.

Security and certificates

Reference-counted certificates and CertificateManager provide explicit ownership, trust-list management, rotation, and lifecycle notifications. Identity providers support application-specific authentication. Pluggable crypto providers let an application replace platform cryptography with another library, a remote service, or hardware such as a TPM, HSM, or PKCS#11 token. Providers can be selected by purpose and security policy, including support for hardware-held private keys. ECC SecureChannel and user-token policies, including the AEAD variants, are available on every target; on .NET Framework the ECDH agreement and the AES-GCM and ChaCha20-Poly1305 ciphers use BouncyCastle. Curve and cipher availability also depends on the platform. See Certificate Manager, Identity Providers, Crypto Providers, and ECC Profiles.

Global Discovery Server

GDS support extends application registration, pull/push certificate management, custom certificate groups, token issuance, and credential services. A Local Discovery Server is also available as a hosted component or standalone sample. For OPC 10000-21 device onboarding, clients register and unregister tickets with a device registrar through OnboardingClient, and the registrar keeps them in a pluggable ITicketStore. See GDS for capability boundaries and conformance evidence, Authorization Service, and Key Credential Service.

Part 14 PubSub modernization

A fluent builder and dependency-injection extensions compose PubSub applications from separate runtime, transport, and server-integration packages. UADP and JSON encoding, discovery, retained metadata, runtime configuration, diagnostics, and Security Key Service integration support publisher/subscriber workflows. Transport and cryptographic capabilities remain platform-dependent; in particular, the DTLS Curve25519 / Curve448 profiles are not registered. See PubSub for supported profiles, transports, and examples.

High availability and redundancy

Server redundancy metadata and ManagedSession failover support OPC UA redundancy workflows. Optional distributed stores add address-space, session, and subscription mirroring with eventual or strong consistency choices. Kubernetes integration provides deployment, discovery, and readiness support. See High Availability, Replica-Consistent NodeIds, and Kubernetes for configuration and deployment constraints.

Tooling and diagnostics

The MCP server exposes client operations and packet-capture tools to agents. ITelemetryContext supplies application logging, metrics, and tracing, with audit and redaction support. A migration analyzer and code fixer assist with mechanical API updates. See MCP Server and Diagnostics.

Further reading