Files
romanett 16c54e52d9 Keep application certificates which do not name a CertificateType (#4348)
# Description

`SecurityConfiguration.ApplicationCertificates`'s setter removed every
identifier whose certificate type is not supported, and
`CertificateIdentifier.CertificateType` defaults to unset. An otherwise
correct identifier passed to the recommended
`AddSecurityConfiguration(ArrayOf<CertificateIdentifier> certIdList,
...)` overload was therefore dropped silently, and validation then
failed with `ApplicationCertificate must be specified.` — pointing at
the wrong cause. The obsolete `AddSecurityConfiguration(string
subjectName, ...)` overload escaped this because it assigns the legacy
singular `ApplicationCertificate` property, whose setter fills in
`RsaSha256ApplicationCertificateType` itself. The result: the
replacement advertised by the `[Obsolete]` message could not be used
without also setting `CertificateType`, which that message does not
mention.

**Changes:**

- `SecurityConfiguration.ApplicationCertificates`
(`src/Opc.Ua.Core/Schema/ApplicationConfiguration.cs`) now treats an
unset `CertificateType` as the default RSA-SHA256 application
certificate type instead of dropping the entry — the same assumption the
legacy single `ApplicationCertificate` element carries, so both paths
agree on what a valid `CertificateIdentifier` is. Fixing the setter also
covers callers that assign `ApplicationCertificates` directly, not just
the builder overload.
- Identifiers dropped for a genuinely unsupported type are no longer
discarded without a trace. They are remembered and reported by
`SecurityConfiguration.Validate`, which has an `ITelemetryContext` —
this replaces the `// TODO: Log when ITelemetry instance is available`
that was exactly this bug:
- a warning naming the rejected certificate types is logged whenever any
were filtered out;
- when the collection ends up empty *because* of the filtering, the
error now reads `No supported application certificate configured: N
certificate identifier(s) were rejected because their CertificateType is
not supported (...)` instead of `ApplicationCertificate must be
specified.`. The original message is kept for the genuinely-empty case.

**Notes for reviewers:**

- Behaviour is unchanged for every configuration that already names a
certificate type; all shipped and test config XMLs set
`CertificateTypeString`.
- `FindApplicationCertificateAsync` already had a fallback treating a
null certificate type as RsaSha256, and `ApplicationInstance` fills in
the type from the created/validated certificate when it is null —
defaulting in the setter is consistent with both and yields the same RSA
certificate.
- The unsupported-type filter itself is untouched; only unset types are
now kept.

## Related Issues

- Fixes #4315

## Checklist

- [x] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- `SecurityConfigurationTests`: an untyped identifier survives as
RsaSha256 and contributes `Basic256Sha256` to
`SupportedSecurityPolicies`; an unsupported type
(`EccCurve25519ApplicationCertificateType`) is removed and reported by
`Validate` with `BadConfigurationError`; a supported entry survives
alongside an unsupported sibling without failing validation.
- `ApplicationConfigurationBuilderTests`: end-to-end reproduction from
the issue, building a client through
`AddSecurityConfiguration(ArrayOf<CertificateIdentifier>, pkiRoot)` +
`CreateAsync()`.
- [ ] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- Ran on net10.0: `Opc.Ua.Configuration.Tests` 225/225 passed,
`Opc.Ua.Core.Tests` 4368 passed / 86 skipped / 0 failed. The full
solution across .NET Framework was not run locally — relying on CI for
that.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 20:27:20 +00:00
..