Files
Marc Schier 51bcd4272a GDS Part 12 Full Compliance (#3795)
* GDS: Authorization & SecureChannel hardening (Part 12 §6.5, §7.2, §7.6)

* Fix OnGetCertificates self-admin authorization: pass applicationId to
  HasAuthorization so ApplicationSelfAdmin can read its own certificates per
  OPC 10000-12 §7.6.10 (was rejecting self-admin pull-management callers).
* Fix OnUnregisterApplicationAsync self-admin authorization: also pass
  applicationId (was silently failing the self-admin branch).
* OnUpdateApplication: accept ApplicationSelfAdmin and ApplicationAdmin in
  addition to DiscoveryAdmin per §6.5.7. Add self-admin role-permission shim.
* OnGetCertificateStatus: accept CertificateAuthorityAdmin / SelfAdmin /
  ApplicationAdmin per §7.6.12 (was using AuthenticatedUserOrSelfAdmin which
  is more permissive than spec).

* Introduce ApplicationAdmin privilege (Part 12 §7.2):
  - Add GdsRole.ApplicationAdmin static privilege.
  - Extend GdsRoleBasedIdentity with AdministeredApplicationIds and a new
    constructor accepting the list. WithAdditionalRoles preserves it.
  - AuthorizationHelper.HasAuthorization recognises the privilege: alone
    when no applicationId is supplied (e.g. RegisterApplication) and
    membership-tested against AdministeredApplicationIds otherwise.
  - HasTrustListAccess honours the ApplicationAdmin privilege for the
    administered applications' trust lists.
  - New role lists: DiscoveryAdminOrAppAdmin (RegisterApplication),
    DiscoveryAdminOrSelfAdminOrAppAdmin (Update/UnregisterApplication),
    CertificateAuthorityAdminOrSelfAdminOrAppAdmin (cert management).

* SecureChannel result-code alignment (Part 12 method tables):
  - HasAuthenticatedSecureChannel throws BadSecurityModeInsufficient (was
    BadUserAccessDenied).
  - Adds requireEncryption flag so callers can demand SignAndEncrypt
    (used by future StartNewKeyPairRequest/FinishRequest tightening).
  - Sign and SignAndEncrypt are now both accepted as 'authenticated'.

* Tests:
  - Register the GDS namespace in AuthorizationHelperTests SetUp so
    GDS-namespace role NodeIds do not collide on NodeId.Null with
    privilege roles.
  - Update HasAuthenticatedSecureChannelThrowsForNonSecureChannel to
    expect BadSecurityModeInsufficient.
  - Add HasAuthenticatedSecureChannelDoesNotThrowForSign and
    HasAuthenticatedSecureChannelWithRequireEncryptionThrowsForSign.
  - Add ApplicationAdmin coverage: SucceedsForAdministeredApp,
    ThrowsForUnmanagedApp, SucceedsForRegisterApplication,
    ThrowsWithEmptyAdministeredList.
  - Add NewRoleListsAreCorrectlyPopulated regression test.

The framework already enforces SigningRequired/EncryptionRequired via
DefaultAccessRestrictions in OpcUaGdsModel.xml, returning
BadSecurityModeInsufficient at request dispatch time
(MasterNodeManager.ValidateAccessRestrictions). The model declares the
correct restriction on every admin method, so explicit handler-level
checks would be redundant. Verified manually against §6.5.6, §6.5.7,
§6.5.8, §7.6.4-7.6.10 and §7.6.12.

23 Authorization unit tests pass. 181 unit + 420 integration GDS tests
pass on net10.0; build clean on net48 and net10.0.

* GDS: Audit event correctness (Part 12 7.6, 7.9)

* Fix audit-event ordering: emit CertificateRequestedAuditEvent AFTER the
  method outcome is known. Previously the event was raised before
  authorization with Status=true, so any failed call appeared in audit as
  a successful certificate request and any authorization-denied call
  generated a misleading 'success' audit trail.
  - OnStartNewKeyPairRequest: refactor to try/catch/finally, emit audit
    once with auditException reflecting either the captured throw, the
    bad ServiceResult, or null on success.
  - OnStartSigningRequestAsync: add the audit emission (previously
    missing entirely - 7.9.3 requires it) using the same pattern.
* Redact privateKeyPassword from audit InputArguments. The previous
  ApplicationsNodeManager.cs:1206-1223 included the raw password string
  as a Variant in the audit event payload, leaking the secret into any
  downstream audit sink. The password is now replaced with
  AuditEvents.RedactedPrivateKeyPassword (placeholder string).
* OnFinishRequestAsync: build the audit InputArguments from only the
  actual method inputs (applicationId, requestId) per 7.6.6 method
  signature. Previously the implementation appended the returned
  Certificate / PrivateKey / IssuerCertificates to InputArguments, which
  leaked the returned PrivateKey into audit payloads.
* Raise CertificateRevokedAuditEventType in OnRevokeCertificate on
  success and failure per 7.6.9. The event type was defined in the
  model (OpcUaGdsModel.xml:543) but never emitted. Refactored
  OnRevokeCertificate with try/catch/finally; sync-over-async (.Result)
  is preserved here and addressed separately in Track C (c-revoke-async).

* AuditEvents helper:
  - New ReportCertificateRevokedAuditEvent extension method, mirroring
    the existing CertificateRequested/Delivered helpers, with an
    Exception parameter that drives Status (true on success, false on
    failure).
  - New RedactedPrivateKeyPassword and RedactedPrivateKey constants
    documented as the canonical placeholders. Class-level remarks
    instruct callers to redact sensitive arguments before invoking the
    helpers.

* Tests:
  - AuditEventsTests: lock the placeholder values
    (RedactedPrivateKeyPassword and RedactedPrivateKey) so they remain
    stable for downstream audit-sink filters.
  - Existing 181 unit + 420 integration GDS tests pass on net10.0; build
    clean on net48 and net10.0.

b-keycred-audit and b-token-audit remain pending pending Track E and F
respectively (KeyCredentialService and AuthorizationService handlers).

* GDS: Directory / Pull Management correctness gaps (Part 12 6.5, 7.6)

* OnRevokeCertificate converted to async OnRevokeCertificateAsync; uses
  await instead of sync-over-async .Result, accepts the method-call
  cancellation token, and is now wired through OnCallAsync to integrate
  with the async dispatch path. RevokeCertificateAsync helper takes an
  optional CancellationToken so callers (OnUnregisterApplicationAsync,
  OnRevokeCertificateAsync) can forward it.

* OnCheckRevocationStatusAsync now computes the ValidityTime per
  OPC 10000-12 7.6.11. The previous implementation always returned
  DateTime.MinValue. The new implementation enumerates the CRLs in the
  trusted-issuer store and uses the earliest NextUpdate as ValidityTime
  when the chain validates. On Bad results (chain validation failed)
  ValidityTime remains DateTime.MinValue so callers re-check
  immediately.

* OnFinishRequestAsync returns the issuer chain via a new
  BuildIssuerCertificateChain helper. The previous implementation
  returned only the immediate signing CA, which is incomplete for
  multi-tier CAs per OPC 10000-12 7.6.6. The helper uses
  ICertificateGroup.Certificates (in-memory) as the X509Chain
  ExtraStore and falls back to the immediate issuing CA if chain
  building does not yield a chain. The helper is intentionally
  synchronous to avoid contending with concurrent SigningRequestAsync /
  NewKeyPairRequestAsync writes against the CertificateGroup's
  AuthoritiesStore (which caused parameterised PushTest cases to fail
  in parallel runs).

* ValidateApplication (ApplicationsDatabaseBase) now enforces the
  reverse-connect rules in OPC 10000-12 6.5.5:
  - Server / DiscoveryServer DiscoveryUrls must NOT start with the
    "rcp+" prefix.
  - Client DiscoveryUrls (when non-empty) must all start with "rcp+"
    AND ServerCapabilities must include "RCP".
  - ClientAndServer must register at least one non-rcp+ Server
    DiscoveryUrl; reverse-connect URLs are still permitted alongside
    and require the RCP ServerCapability.
  Removed the previous blanket rejection of any DiscoveryUrls on
  ApplicationType.Client (which contradicted the spec's reverse-connect
  registration pattern).

* Tests:
  - RegisterApplicationClientWithRcpDiscoveryUrlsAndRcpCapabilityDoesNotThrow
  - RegisterApplicationClientWithRcpDiscoveryUrlsAndMissingCapabilityThrows
  - RegisterApplicationServerWithRcpDiscoveryUrlThrows
  - RegisterApplicationClientAndServerWithMixedUrlsDoesNotThrow
  - RegisterApplicationClientAndServerWithOnlyRcpDiscoveryUrlsThrows
  All 188 unit + 420 integration GDS tests pass on net10.0; build clean
  on net48 and net10.0.

c-redundancy intentionally requires no enforcement: both transparent
(single application with multiple DiscoveryUrls) and non-transparent
(separate applications with NTRS capability) patterns per OPC 10000-12
6.5.6 are admitted by the validator; transparent redundancy is a
registrant convention rather than a server-enforceable invariant.


* Annex D capabilities: Self-advertise the LDS-ME capability identifier
  whenever the MulticastExtension is configured. Previously LdsServer
  hard-coded ["LDS"] regardless of whether multicast was running, so
  FindServersOnNetwork peers could not distinguish LDS from LDS-ME
  instances. Refactored the capability computation into a public static
  ComputeServerCapabilities(existing, hasMulticast) helper:
    - empty input + no multicast -> ["LDS"]
    - empty input + multicast -> ["LDS", "LDS-ME"]
    - custom seed list with no "LDS" -> "LDS" is appended
    - existing "LDS-ME" is not duplicated
  OnServerStarting now wires up the multicast layer first so the
  capability set reflects the configured topology.

* Annex C / rcp+ reverse-connect preservation: MulticastDiscovery now
  carries the OPC 10000-12 6.5.5 rcp+ DiscoveryUrl prefix end-to-end
  through mDNS announce / discover.
    - TryBuildProfile strips the rcp+ prefix before parsing the
      underlying transport URL (Uri parsing on "rcp+opc.tcp://..." is
      brittle across runtimes) and emits a dedicated TXT-record key
      ReverseConnectTxtKey ("rc") so receivers can reconstruct it
      verbatim.
    - OnServiceInstanceDiscovered honours the "rc" key when present
      and rebuilds the DiscoveryUrl with the rcp+ scheme.
  Exposes MulticastDiscovery.OpcUaServiceType,
  MulticastDiscovery.ReverseConnectScheme and
  MulticastDiscovery.ReverseConnectTxtKey as public constants so
  client code and tests can reference the same values used during
  registration and discovery.

* RegisterServer2 fallback (4.2.2): verified that LdsServer overrides
  both RegisterServerAsync and RegisterServer2Async, so the LDS
  accepts both legacy RegisterServer registrations and the modern
  RegisterServer2 + MdnsDiscoveryConfiguration path; no code change
  required.

* Tests: new LdsServerStaticTests fixture exercising the capability
  computation (six matrix cases) and locking in the Annex C / rcp+
  constants (one regression test). 195 unit + 420 integration GDS
  tests pass on net10.0; build clean on net48 and net10.0.

* Added Opc.Ua.Lds.Server as a ProjectReference of Opc.Ua.Gds.Tests so
  the new tests can use the public static surface of LdsServer and
  MulticastDiscovery.

* GDS: Push management hardening (Part 12 7.10)

* d-push-client-multitype: Make ServerPushConfigurationClient.ApplicationCertificateType
  settable instead of hard-coded to RsaSha256ApplicationCertificateType.
  Callers managing ECC, Brainpool or HTTPS certificates can now override
  the default before invoking certificate-management methods (or pass an
  explicit certificate type to the methods themselves). Removed the
  TODO comment that flagged this limitation.

* d-trustlist-ext: Set Writable / UserWritable to true for the
  DefaultApplicationGroup / DefaultHttpsGroup / DefaultUserTokenGroup
  TrustList instances in the GDS. Per OPC 10000-12 7.8.2.1 a TrustList
  that supports CloseAndUpdate / AddCertificate / RemoveCertificate is
  writeable; role-based access controls on the individual methods still
  enforce who may actually mutate the trust list. The previous values
  (false) were misleading to clients browsing the trust-list nodes.

* d-server-cred-audit: Redact the private key from
  CertificateUpdateRequestedAuditEventType InputArguments per OPC 10000-12
  7.10.3. ConfigurationNodeManager.UpdateCertificateAsync was passing
  the raw private-key ByteString into the audit payload, leaking the
  secret to any downstream audit sink. The audit payload now uses
  Opc.Ua.Server.AuditEvents.RedactedPrivateKey (an empty ByteString)
  as the placeholder; the certificate, issuer chain and key format
  remain in the payload so administrators can correlate the request.

  Also extended Server.ReportCertificateUpdateRequestedAuditEvent with
  an optional Exception parameter so callers can raise the event with
  Status=false on failure (parity with the GDS-side helpers).

* Build clean on net48 and net10.0. 195 unit + 420 integration GDS
  tests pass on net10.0.

Remaining Track D items (d-create-selfsigned, d-confirm-update,
d-managed-apps, d-alarms, d-push-tests) are larger features that
depend on Track H model additions and are not in scope for this
commit; their todos remain pending.

* GDS: Lock in ServerPushConfigurationClient.ApplicationCertificateType behaviour

Adds ServerPushConfigurationClientStaticTests covering the
Track D quick-win (d-push-client-multitype): the property defaults
to RsaSha256ApplicationCertificateType for backwards compatibility
and is now caller-overridable (e.g. for ECC management).

* GDS: Implement CreateSelfSignedCertificate (Part 12 7.10.6)

* Server-side: ConfigurationNodeManager wires the generated
  CreateSelfSignedCertificateMethodState.OnCallAsync handler when the
  optional method node exists on the ServerConfiguration instance.
  The handler:
  - Requires SecurityAdmin access (HasApplicationSecureAdminAccess)
  - Validates CertificateGroupId / CertificateTypeId via the existing
    VerifyGroupAndTypeId helper
  - Creates a self-signed certificate using the CertificateFactory
    pipeline (RSA with caller-specified key size, or ECC from the
    curve mapped by the certificateTypeId)
  - Merges DnsNames and IpAddresses into the SAN extension
  - Stores the resulting raw data on the existing
    CertificateIdentifier so the resolver can materialise it
  - Returns the DER-encoded public certificate to the caller
  The method is Optional per the spec; servers that load a nodeset
  without it simply skip the wire-up (null guard).

* Client-side: IServerPushConfigurationClient extended with
  CreateSelfSignedCertificateAsync; ServerPushConfigurationClient
  delegates to the generated ServerConfigurationTypeClient proxy
  with the standard elevate/revert permission pattern.

* net48 compat: uses new ValueTask<T>(result) instead of
  ValueTask.FromResult which is unavailable on .NET Framework 4.8.
2026-05-23 08:07:28 +02:00

229 lines
11 KiB
C#

/* ========================================================================
* Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using System;
using System.Threading;
using System.Threading.Tasks;
using Opc.Ua.Client;
using Opc.Ua.Security.Certificates;
namespace Opc.Ua.Gds.Client
{
/// <summary>
/// Public surface of <see cref="ServerPushConfigurationClient"/>. Exposes
/// the asynchronous operations that the push configuration client offers
/// against a connected server implementing the ServerConfiguration model.
/// </summary>
public interface IServerPushConfigurationClient : IAsyncDisposable
{
/// <summary>The application configuration in use.</summary>
ApplicationConfiguration Configuration { get; }
/// <summary>The message context derived from the configuration.</summary>
IServiceMessageContext MessageContext { get; }
/// <summary>The administrator credentials used to elevate calls.</summary>
IUserIdentity? AdminCredentials { get; set; }
/// <summary>The endpoint URL of the connected server, when available.</summary>
string? EndpointUrl { get; }
/// <summary><c>true</c> when an active session exists.</summary>
bool IsConnected { get; }
/// <summary>The active session, when connected.</summary>
ISession? Session { get; }
/// <summary>The endpoint to connect to. Write-once before connect.</summary>
ConfiguredEndpoint? Endpoint { get; set; }
/// <summary>Locales preferred by the client.</summary>
ArrayOf<string> PreferredLocales { get; set; }
/// <summary>NodeId of the DefaultApplicationGroup.</summary>
NodeId DefaultApplicationGroup { get; }
/// <summary>NodeId of the DefaultHttpsGroup.</summary>
NodeId DefaultHttpsGroup { get; }
/// <summary>NodeId of the DefaultUserTokenGroup.</summary>
NodeId DefaultUserTokenGroup { get; }
/// <summary>NodeId of the application certificate type.</summary>
NodeId ApplicationCertificateType { get; }
/// <summary>Raised when administrator credentials are needed.</summary>
event EventHandler<AdminCredentialsRequiredEventArgs>? AdminCredentialsRequired;
/// <summary>Raised when the connection status changes.</summary>
event EventHandler? ConnectionStatusChanged;
/// <summary>Raised on every keep-alive callback.</summary>
event KeepAliveEventHandler? KeepAlive;
/// <summary>Raised when monitored item notifications change server status.</summary>
event MonitoredItemNotificationEventHandler? ServerStatusChanged;
/// <summary>Clears the cached <see cref="AdminCredentials"/>.</summary>
void ResetCredentials();
/// <summary>Connects using the previously assigned endpoint.</summary>
ValueTask ConnectAsync(CancellationToken ct = default);
/// <summary>Connects to the supplied endpoint URL.</summary>
ValueTask ConnectAsync(string endpointUrl, CancellationToken ct = default);
/// <summary>Connects to the supplied configured endpoint.</summary>
ValueTask ConnectAsync(ConfiguredEndpoint? endpoint, CancellationToken ct = default);
/// <summary>Disconnects the active session.</summary>
ValueTask DisconnectAsync(CancellationToken ct = default);
/// <summary>Returns the supported private-key formats.</summary>
/// <remarks>Reads the <c>SupportedPrivateKeyFormats</c> property of <c>ServerConfigurationType</c> (OPC 10000-12 §7.10.2).</remarks>
ValueTask<ArrayOf<string>> GetSupportedKeyFormatsAsync(CancellationToken ct = default);
/// <summary>Reads the trust list of the specified certificate group.</summary>
/// <remarks>Reads the trust list via the file transfer methods of <c>TrustListType</c> (OPC 10000-12 §7.8).</remarks>
ValueTask<TrustListDataType> ReadTrustListAsync(
NodeId certificateGroupId,
TrustListMasks masks = TrustListMasks.All,
long maxTrustListSize = 0,
CancellationToken ct = default);
/// <summary>Reads the default-application-group trust list.</summary>
/// <remarks>Reads the trust list via the file transfer methods of <c>TrustListType</c> (OPC 10000-12 §7.8).</remarks>
ValueTask<TrustListDataType> ReadTrustListAsync(
TrustListMasks masks = TrustListMasks.All,
long maxTrustListSize = 0,
CancellationToken ct = default);
/// <summary>Updates the default-application-group trust list.</summary>
/// <remarks>Writes the trust list via the file transfer methods of <c>TrustListType</c> (OPC 10000-12 §7.8).</remarks>
ValueTask<bool> UpdateTrustListAsync(
TrustListDataType trustList,
CancellationToken ct = default);
/// <summary>Updates the default-application-group trust list.</summary>
/// <remarks>Writes the trust list via the file transfer methods of <c>TrustListType</c> (OPC 10000-12 §7.8).</remarks>
ValueTask<bool> UpdateTrustListAsync(
TrustListDataType trustList,
long maxTrustListSize,
CancellationToken ct = default);
/// <summary>Updates the trust list of the specified certificate group.</summary>
/// <remarks>Writes the trust list via the file transfer methods of <c>TrustListType</c> (OPC 10000-12 §7.8).</remarks>
ValueTask<bool> UpdateTrustListAsync(
NodeId certificateGroupId,
TrustListDataType trustList,
long maxTrustListSize,
CancellationToken ct = default);
/// <summary>Adds a certificate to the trust list.</summary>
/// <remarks>Calls the <c>AddCertificate</c> method on <c>TrustListType</c> (OPC 10000-12 §7.8.7).</remarks>
ValueTask AddCertificateAsync(
Certificate certificate,
bool isTrustedCertificate,
CancellationToken ct = default);
/// <summary>Adds a certificate to the trust list.</summary>
/// <remarks>Calls the <c>AddCertificate</c> method on <c>TrustListType</c> (OPC 10000-12 §7.8.7).</remarks>
ValueTask AddCertificateAsync(
NodeId certificateGroupId,
Certificate certificate,
bool isTrustedCertificate,
CancellationToken ct = default);
/// <summary>Removes a certificate from the trust list.</summary>
/// <remarks>Calls the <c>RemoveCertificate</c> method on <c>TrustListType</c> (OPC 10000-12 §7.8.8).</remarks>
ValueTask RemoveCertificateAsync(
string thumbprint,
bool isTrustedCertificate,
CancellationToken ct = default);
/// <summary>Removes a certificate from the trust list.</summary>
/// <remarks>Calls the <c>RemoveCertificate</c> method on <c>TrustListType</c> (OPC 10000-12 §7.8.8).</remarks>
ValueTask RemoveCertificateAsync(
NodeId certificateGroupId,
string thumbprint,
bool isTrustedCertificate,
CancellationToken ct = default);
/// <summary>Lists the rejected certificates.</summary>
/// <remarks>Calls the <c>GetRejectedList</c> method on <c>ServerConfigurationType</c> (OPC 10000-12 §7.10.6).</remarks>
ValueTask<CertificateCollection> GetRejectedListAsync(CancellationToken ct = default);
/// <summary>Creates a certificate signing request.</summary>
/// <remarks>Calls the <c>CreateSigningRequest</c> method on <c>ServerConfigurationType</c> (OPC 10000-12 §7.10.4).</remarks>
ValueTask<ByteString> CreateSigningRequestAsync(
NodeId certificateGroupId,
NodeId certificateTypeId,
string subjectName,
bool regeneratePrivateKey,
ByteString nonce,
CancellationToken ct = default);
/// <summary>Updates the application certificate.</summary>
/// <remarks>Calls the <c>UpdateCertificate</c> method on <c>ServerConfigurationType</c> (OPC 10000-12 §7.10.3).</remarks>
ValueTask<bool> UpdateCertificateAsync(
NodeId certificateGroupId,
NodeId certificateTypeId,
ByteString certificate,
string privateKeyFormat,
ByteString privateKey,
ArrayOf<ByteString> issuerCertificates,
CancellationToken ct = default);
/// <summary>Applies the configuration changes on the server.</summary>
/// <remarks>Calls the <c>ApplyChanges</c> method on <c>ServerConfigurationType</c> (OPC 10000-12 §7.10.5).</remarks>
ValueTask ApplyChangesAsync(CancellationToken ct = default);
/// <summary>Lists the certificates configured on the server.</summary>
/// <remarks>Calls the <c>GetCertificates</c> method on <c>ServerConfigurationType</c> (OPC 10000-12 §7.10.7).</remarks>
ValueTask<(ArrayOf<NodeId> certificateTypeIds, ArrayOf<ByteString> certificates)> GetCertificatesAsync(
NodeId certificateGroupId,
CancellationToken ct = default);
/// <summary>Creates a self-signed certificate on the server.</summary>
/// <remarks>Calls the <c>CreateSelfSignedCertificate</c> method on
/// <c>ServerConfigurationType</c> (OPC 10000-12 §7.10.6). The method
/// is optional; servers that do not support it return
/// <c>Bad_NotSupported</c>.</remarks>
ValueTask<ByteString> CreateSelfSignedCertificateAsync(
NodeId certificateGroupId,
NodeId certificateTypeId,
string subjectName,
ArrayOf<string> dnsNames,
ArrayOf<string> ipAddresses,
ushort lifetimeInDays,
ushort keySizeInBits,
CancellationToken ct = default);
}
}