> 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>
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
- Feature details
- Dependency injection and hosting
- Client
- Source generators and modeling
- Type system and immutability
- Performance, memory, and pooling
- Async, cancellation, and
TimeProvider - Native AOT
- OPC UA companion-spec coverage
- Security and certificates
- Global Discovery Server
- Part 14 PubSub modernization
- High availability and redundancy
- Tooling and diagnostics
- Further reading
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
- Migration Guide — breaking changes, per-API replacements, and the migration analyzer workflow for upgrading from 1.5.378.
- Documentation index — task-based routes into the SDK guides.
- Sample applications — runnable client, server, PubSub, and companion-model examples.
- Profiles and facets — capability and platform overview.