diff --git a/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs b/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs index 860756bb54..d2502edfba 100644 --- a/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs +++ b/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs @@ -80,7 +80,7 @@ ApplicationConfiguration configuration ServerCertificateGroup defaultApplicationGroup = new ServerCertificateGroup { NodeId = Opc.Ua.ObjectIds.ServerConfiguration_CertificateGroups_DefaultApplicationGroup, BrowseName = Opc.Ua.BrowseNames.DefaultApplicationGroup, - CertificateTypes = new NodeId[]{}, + CertificateTypes = new NodeId[] { }, ApplicationCertificates = new CertificateIdentifierCollection(), IssuerStore = new CertificateStoreIdentifier(configuration.SecurityConfiguration.TrustedIssuerCertificates.StorePath), TrustedStore = new CertificateStoreIdentifier(configuration.SecurityConfiguration.TrustedPeerCertificates.StorePath) @@ -397,7 +397,7 @@ private ServiceResult UpdateCertificate( // identify the existing certificate to be updated // it should be of the same type and same subject name as the new certificate - CertificateIdentifier existingCertIdentifier = certificateGroup.ApplicationCertificates.FirstOrDefault(cert => + CertificateIdentifier existingCertIdentifier = certificateGroup.ApplicationCertificates.FirstOrDefault(cert => X509Utils.CompareDistinguishedName(cert.Certificate.Subject, newCert.Subject) && cert.CertificateType == certificateTypeId); @@ -451,8 +451,8 @@ private ServiceResult UpdateCertificate( { // verify cert with issuer chain CertificateValidator certValidator = new CertificateValidator(); -// TODO: why? -// certValidator.MinimumCertificateKeySize = 1024; + // TODO: why? + // certValidator.MinimumCertificateKeySize = 1024; CertificateTrustList issuerStore = new CertificateTrustList(); CertificateIdentifierCollection issuerCollection = new CertificateIdentifierCollection(); foreach (var issuerCert in newIssuerCollection) @@ -677,7 +677,15 @@ private ServiceResult ApplyChanges( // give the client some time to receive the response // before the certificate update may disconnect all sessions await Task.Delay(1000).ConfigureAwait(false); - await m_configuration.CertificateValidator.UpdateCertificateAsync(m_configuration.SecurityConfiguration).ConfigureAwait(false); + try + { + await m_configuration.CertificateValidator.UpdateCertificateAsync(m_configuration.SecurityConfiguration).ConfigureAwait(false); + } + catch (Exception ex) + { + Utils.LogCritical(ex, "Failed to sucessfully Apply Changes: Error updating application instance certificates. Server could be in faulted state."); + throw ex; + } } ); } diff --git a/Libraries/Opc.Ua.Server/Server/StandardServer.cs b/Libraries/Opc.Ua.Server/Server/StandardServer.cs index 45dd363f88..1ee4c896ba 100644 --- a/Libraries/Opc.Ua.Server/Server/StandardServer.cs +++ b/Libraries/Opc.Ua.Server/Server/StandardServer.cs @@ -488,7 +488,7 @@ public override ResponseHeader CreateSession( // check if complete chain should be sent. if (InstanceCertificateTypesProvider.SendCertificateChain) { - serverCertificate = InstanceCertificateTypesProvider.LoadCertificateChainRaw(instanceCertificate); + serverCertificate = InstanceCertificateTypesProvider.LoadCertificateChainRawAsync(instanceCertificate).GetAwaiter().GetResult(); } else { diff --git a/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsServiceHost.cs b/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsServiceHost.cs index 1ef99083af..3ed00e08ae 100644 --- a/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsServiceHost.cs +++ b/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsServiceHost.cs @@ -125,7 +125,7 @@ CertificateTypesProvider certificateTypesProvider // check if complete chain should be sent. if (certificateTypesProvider.SendCertificateChain) { - description.ServerCertificate = certificateTypesProvider.LoadCertificateChainRaw(instanceCertificate); + description.ServerCertificate = certificateTypesProvider.LoadCertificateChainRawAsync(instanceCertificate).GetAwaiter().GetResult(); } } diff --git a/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsTransportListener.cs b/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsTransportListener.cs index 0f46ebdbca..2da30e11a9 100644 --- a/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsTransportListener.cs +++ b/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsTransportListener.cs @@ -477,10 +477,9 @@ public void CertificateUpdate( foreach (EndpointDescription description in m_descriptions) { - ServerBase.SetServerCertificateInEndpointDescription(description, - m_serverCertProvider.SendCertificateChain, + ServerBase.SetServerCertificateInEndpointDescriptionAsync(description, certificateTypeProvider, - false); + false).GetAwaiter().GetResult(); } Start(); diff --git a/Stack/Opc.Ua.Core/Security/Certificates/CertificateTypesProvider.cs b/Stack/Opc.Ua.Core/Security/Certificates/CertificateTypesProvider.cs index 9e4c587569..9d472af10d 100644 --- a/Stack/Opc.Ua.Core/Security/Certificates/CertificateTypesProvider.cs +++ b/Stack/Opc.Ua.Core/Security/Certificates/CertificateTypesProvider.cs @@ -122,7 +122,7 @@ public X509Certificate2 GetInstanceCertificate(string securityPolicyUri) /// Loads the cached certificate chain blob of a certificate for use in a secure channel as raw byte array. /// /// The application certificate. - public byte[] LoadCertificateChainRaw(X509Certificate2 certificate) + public async Task LoadCertificateChainRawAsync(X509Certificate2 certificate) { if (certificate == null) { @@ -134,7 +134,13 @@ public byte[] LoadCertificateChainRaw(X509Certificate2 certificate) return result.Item2; } - return certificate.RawData; + // load certificate chain. + Tuple dictionaryValue = await LoadCertificateChainFromStoreAsync(certificate).ConfigureAwait(false); + + // update cached values + m_certificateChain[certificate.Thumbprint] = dictionaryValue; + + return dictionaryValue.Item2; } /// @@ -154,51 +160,44 @@ public async Task LoadCertificateChainAsync(X509Cert } // load certificate chain. - var certificateChain = new X509Certificate2Collection(certificate); - var issuers = new List(); - if (await m_certificateValidator.GetIssuers(certificate, issuers).ConfigureAwait(false)) - { - for (int i = 0; i < issuers.Count; i++) - { - certificateChain.Add(issuers[i].Certificate); - } - } - - byte[] certificateChainRaw = Utils.CreateCertificateChainBlob(certificateChain); - var dictionaryValue = new Tuple(certificateChain, certificateChainRaw); + Tuple dictionaryValue = await LoadCertificateChainFromStoreAsync(certificate).ConfigureAwait(false); // update cached values m_certificateChain[certificate.Thumbprint] = dictionaryValue; - return certificateChain; + return dictionaryValue.Item1; } /// - /// Loads the certificate chain for an application certificate from cache. + /// Update the security configuration of the cert type provider. /// - /// The application certificate. - public X509Certificate2Collection LoadCertificateChain(X509Certificate2 certificate) + /// The new security configuration. + public async Task UpdateAsync(SecurityConfiguration securityConfiguration) { - if (certificate == null) - { - return null; - } - - if (m_certificateChain.TryGetValue(certificate.Thumbprint, out var certificateChainTuple)) - { - return certificateChainTuple.Item1; - } - - return null; + m_securityConfiguration = securityConfiguration; + //ToDo intialize internal CertificateValidator after Certificate Update to clear cache of old application certificates + await Task.CompletedTask.ConfigureAwait(false); } /// - /// Update the security configuration of the cert type provider. + /// Builds the chain using the Issuer and Trusted Stores of the certificateValidator /// - /// The new security configuration. - public void Update(SecurityConfiguration securityConfiguration) + /// the certificate to load the chain for + /// + private async Task> LoadCertificateChainFromStoreAsync(X509Certificate2 certificate) { - m_securityConfiguration = securityConfiguration; + var certificateChain = new X509Certificate2Collection(certificate); + var issuers = new List(); + if (await m_certificateValidator.GetIssuers(certificate, issuers).ConfigureAwait(false)) + { + for (int i = 0; i < issuers.Count; i++) + { + certificateChain.Add(issuers[i].Certificate); + } + } + + byte[] certificateChainRaw = Utils.CreateCertificateChainBlob(certificateChain); + return new Tuple(certificateChain, certificateChainRaw); } CertificateValidator m_certificateValidator; diff --git a/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs b/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs index eb4f7de894..ffcbf0a807 100644 --- a/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs +++ b/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs @@ -21,10 +21,10 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; -using Microsoft.Extensions.Logging; using Opc.Ua.Bindings; using System.Net.Sockets; using Opc.Ua.Security.Certificates; +using System.Threading.Tasks; namespace Opc.Ua { @@ -604,12 +604,10 @@ public static bool RequireEncryption(EndpointDescription description) /// Sets the Server Certificate in an Endpoint description if the description requires encryption. /// /// the endpoint Description to set the server certificate - /// true if the certificate chain shall be sent /// The provider to get the server certificate per certificate type. /// only set certificate if the endpoint does require Encryption - public static void SetServerCertificateInEndpointDescription( + public static async Task SetServerCertificateInEndpointDescriptionAsync( EndpointDescription description, - bool sendCertificateChain, CertificateTypesProvider certificateTypesProvider, bool checkRequireEncryption = true) { @@ -617,9 +615,9 @@ public static void SetServerCertificateInEndpointDescription( { X509Certificate2 serverCertificate = certificateTypesProvider.GetInstanceCertificate(description.SecurityPolicyUri); // check if complete chain should be sent. - if (sendCertificateChain) + if (certificateTypesProvider.SendCertificateChain) { - description.ServerCertificate = certificateTypesProvider.LoadCertificateChainRaw(serverCertificate); + description.ServerCertificate = await certificateTypesProvider.LoadCertificateChainRawAsync(serverCertificate).ConfigureAwait(false); } else { @@ -796,7 +794,16 @@ protected virtual EndpointBase GetEndpointInstance(ServerBase server) /// protected virtual void OnCertificateUpdate(object sender, CertificateUpdateEventArgs e) { - InstanceCertificateTypesProvider.Update(e.SecurityConfiguration); + InstanceCertificateTypesProvider.UpdateAsync(e.SecurityConfiguration).GetAwaiter().GetResult(); + + //update certificate in the endpoint descriptions + foreach (EndpointDescription endpointDescription in m_endpoints) + { + SetServerCertificateInEndpointDescriptionAsync( + endpointDescription, + InstanceCertificateTypesProvider).GetAwaiter().GetResult(); + } + foreach (var listener in TransportListeners) { listener.CertificateUpdate(e.CertificateValidator, InstanceCertificateTypesProvider); diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/TcpServiceHost.cs b/Stack/Opc.Ua.Core/Stack/Tcp/TcpServiceHost.cs index fa12741ec1..fa671e1e5d 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/TcpServiceHost.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/TcpServiceHost.cs @@ -76,7 +76,6 @@ public List CreateServiceHost( uri.Host = computerName; } - bool sendCertificateChain = instanceCertificateTypesProvider.SendCertificateChain; ITransportListener listener = this.Create(); if (listener != null) { @@ -96,10 +95,9 @@ public List CreateServiceHost( }; description.UserIdentityTokens = serverBase.GetUserTokenPolicies(configuration, description); - ServerBase.SetServerCertificateInEndpointDescription( + ServerBase.SetServerCertificateInEndpointDescriptionAsync( description, - sendCertificateChain, - instanceCertificateTypesProvider); + instanceCertificateTypesProvider).GetAwaiter().GetResult(); listenerEndpoints.Add(description); } diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportListener.cs b/Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportListener.cs index f651ab3d30..5469ff6875 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportListener.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportListener.cs @@ -490,7 +490,7 @@ CertificateTypesProvider certificateTypesProvider X509Certificate2 serverCertificate = certificateTypesProvider.GetInstanceCertificate(description.SecurityPolicyUri); if (certificateTypesProvider.SendCertificateChain) { - byte[] serverCertificateChainRaw = certificateTypesProvider.LoadCertificateChainRaw(serverCertificate); + byte[] serverCertificateChainRaw = certificateTypesProvider.LoadCertificateChainRawAsync(serverCertificate).GetAwaiter().GetResult(); description.ServerCertificate = serverCertificateChainRaw; } else diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Asymmetric.cs b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Asymmetric.cs index a950d14cb1..4377c95734 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Asymmetric.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Asymmetric.cs @@ -898,7 +898,7 @@ protected void ReadAsymmetricMessageHeader( if (loadChain) { - m_serverCertificateChain = m_serverCertificateTypesProvider?.LoadCertificateChain(receiverCertificate); + m_serverCertificateChain = m_serverCertificateTypesProvider?.LoadCertificateChainAsync(receiverCertificate).GetAwaiter().GetResult(); } } else @@ -930,7 +930,7 @@ protected void ReviseSecurityMode(bool firstCall, MessageSecurityMode requestedM m_securityMode = endpoint.SecurityMode; m_selectedEndpoint = endpoint; m_serverCertificate = m_serverCertificateTypesProvider.GetInstanceCertificate(m_securityPolicyUri); - m_serverCertificateChain = m_serverCertificateTypesProvider.LoadCertificateChain(m_serverCertificate); + m_serverCertificateChain = m_serverCertificateTypesProvider.LoadCertificateChainAsync(m_serverCertificate).GetAwaiter().GetResult(); supported = true; break; }