Skip to content

Commit b2a419c

Browse files
authored
Fix #3931: honor server SecurityConfiguration in GDS push UpdateCertificate
1 parent fbec65a commit b2a419c

1 file changed

Lines changed: 58 additions & 8 deletions

File tree

Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -538,17 +538,67 @@ private async ValueTask<UpdateCertificateMethodStateResult> UpdateCertificateAsy
538538
{
539539
try
540540
{
541-
// verify cert with issuer chain
542-
var certValidator = new CertificateValidator(Server.Telemetry);
543-
var issuerStore = new CertificateTrustList();
544-
var issuerCollection = new CertificateIdentifierCollection();
541+
// Verify chain integrity: build a chain rooted at any of the provided
542+
// issuer certificates and ensure all signatures are valid. We do not
543+
// consult the application's trust list here — the caller is supplying
544+
// the issuer chain as part of the UpdateCertificate input. Policy checks
545+
// (minimum key size, SHA-1, revocation) are intentionally not applied here;
546+
// the server's configured SecurityConfiguration governs certificate
547+
// acceptance on its communication channels.
548+
var chainPolicy = new X509ChainPolicy
549+
{
550+
RevocationFlag = X509RevocationFlag.EntireChain,
551+
RevocationMode = X509RevocationMode.NoCheck,
552+
VerificationFlags =
553+
X509VerificationFlags.AllowUnknownCertificateAuthority |
554+
X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown |
555+
X509VerificationFlags.IgnoreEndRevocationUnknown |
556+
X509VerificationFlags.IgnoreRootRevocationUnknown,
557+
#if NET5_0_OR_GREATER
558+
DisableCertificateDownloads = true,
559+
#endif
560+
UrlRetrievalTimeout = TimeSpan.FromMilliseconds(1)
561+
};
562+
545563
foreach (X509Certificate2 issuerCert in newIssuerCollection)
546564
{
547-
issuerCollection.Add(new CertificateIdentifier(issuerCert));
565+
chainPolicy.ExtraStore.Add(issuerCert);
566+
}
567+
568+
using var chain = new X509Chain { ChainPolicy = chainPolicy };
569+
chain.Build(newCert);
570+
571+
foreach (X509ChainStatus chainStatus in chain.ChainStatus ?? [])
572+
{
573+
if (chainStatus.Status is X509ChainStatusFlags.NoError or
574+
X509ChainStatusFlags.UntrustedRoot)
575+
{
576+
continue;
577+
}
578+
if (chainStatus.Status is X509ChainStatusFlags.NotSignatureValid or
579+
X509ChainStatusFlags.PartialChain or
580+
X509ChainStatusFlags.NotValidForUsage or
581+
X509ChainStatusFlags.InvalidBasicConstraints)
582+
{
583+
throw new ServiceResultException(
584+
StatusCodes.BadSecurityChecksFailed,
585+
Utils.Format(
586+
"Certificate chain validation failed. {0}: {1}",
587+
chainStatus.Status,
588+
chainStatus.StatusInformation));
589+
}
590+
}
591+
592+
if (newIssuerCollection.Count + 1 != chain.ChainElements.Count)
593+
{
594+
throw new ServiceResultException(
595+
StatusCodes.BadSecurityChecksFailed,
596+
"The supplied issuer chain is incomplete.");
548597
}
549-
issuerStore.TrustedCertificates = issuerCollection;
550-
certValidator.Update(issuerStore, issuerStore, null);
551-
await certValidator.ValidateAsync(newCert, ct).ConfigureAwait(false);
598+
}
599+
catch (ServiceResultException)
600+
{
601+
throw;
552602
}
553603
catch (Exception ex)
554604
{

0 commit comments

Comments
 (0)