/// <summary> /// Check for untrusted certificates and only accept them if the user has accepted them /// </summary> private void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e) { if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted) { // Session is not accessible from here so we need to iterate through all key-value-pairs foreach (KeyValuePair <string, OpcSessionCacheData> pair in OpcSessionCache.ToArray()) { // try processing each entry try { string hostName = pair.Value.EndpointURL.Substring("opc.tcp://".Length); if (hostName.Contains(":")) { hostName = hostName.Substring(0, hostName.IndexOf(':')); } // Look up by cert thumbprint if (string.Equals(pair.Value.CertThumbprint, e.Certificate.Thumbprint, StringComparison.InvariantCultureIgnoreCase)) { // check if the current session user has confirmed trust if (pair.Value.Trusted) { // In this case, we accept the cert e.Accept = true; break; } } // Update our cache data if (e.Certificate.Subject.Contains(hostName)) { OpcSessionCacheData newValue = new OpcSessionCacheData { OPCSession = pair.Value.OPCSession, EndpointURL = pair.Value.EndpointURL, Trusted = pair.Value.Trusted, CertThumbprint = e.Certificate.Thumbprint }; OpcSessionCache.TryUpdate(pair.Key, newValue, pair.Value); break; } } catch (Exception) { // do nothing } } } }
/// <summary> /// Establishes an OPC UA session to the given OPC UA server with implicit trust. /// </summary> public async Task <Session> GetSessionWithImplicitTrust(string sessionID, string appUri) { // Try to connect to Server Session session = null; try { session = await GetSessionAsync(sessionID, appUri); } catch (ServiceResultException exception) when((exception.InnerResult != null) && (exception.InnerResult.StatusCode == StatusCodes.BadCertificateUntrusted)) { // Check that there is a session already in our cache data OpcSessionCacheData entry; if (OpcSessionCache.TryGetValue(sessionID, out entry)) { if (string.Equals(entry.EndpointURL, appUri, StringComparison.InvariantCultureIgnoreCase)) { // We always trust the Server for which we have generated the URL ourselves OpcSessionCacheData newValue = new OpcSessionCacheData { CertThumbprint = entry.CertThumbprint, OPCSession = entry.OPCSession, EndpointURL = entry.EndpointURL, Trusted = true }; OpcSessionCache.TryUpdate(sessionID, newValue, entry); // try again session = await GetSessionAsync(sessionID, appUri); } } } catch (ServiceResultException exception) when((exception.InnerResult != null) && (exception.InnerResult.StatusCode != StatusCodes.BadCertificateUntrusted)) { // any other reason than untrusted certificate Trace.TraceError($"ServiceResultException occured: '{exception.InnerException.Message}'"); throw; } return(session); }
/// <summary> /// Checks if there is an active OPC UA session for the provided browser session. If the persisted OPC UA session does not exist, /// a new OPC UA session to the given endpoint URL is established. /// </summary> public async Task <Session> GetSessionAsync(string sessionID, string endpointURL) { if (string.IsNullOrEmpty(sessionID) || string.IsNullOrEmpty(endpointURL)) { return(null); } OpcSessionCacheData entry; if (OpcSessionCache.TryGetValue(sessionID, out entry)) { if (entry.OPCSession != null) { if (entry.OPCSession.Connected) { return(entry.OPCSession); } try { entry.OPCSession.Close(500); } catch { } entry.OPCSession = null; } } else { // create a new entry OpcSessionCacheData newEntry = new OpcSessionCacheData { EndpointURL = endpointURL }; OpcSessionCache.TryAdd(sessionID, newEntry); } Uri endpointURI = new Uri(endpointURL); EndpointDescriptionCollection endpointCollection = DiscoverEndpoints(_configuration, endpointURI, 10); EndpointDescription selectedEndpoint = SelectUaTcpEndpoint(endpointCollection); EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(_configuration); ConfiguredEndpoint endpoint = new ConfiguredEndpoint(selectedEndpoint.Server, endpointConfiguration); endpoint.Update(selectedEndpoint); // Check if we have a cached endpoint with the same URL and use that one instead foreach (ConfiguredEndpoint cachedEndpoint in OpcCachedEndpoints) { if (cachedEndpoint.EndpointUrl.AbsoluteUri.Equals(endpointURL, StringComparison.InvariantCultureIgnoreCase)) { endpoint = cachedEndpoint; break; } } Session session = await Session.Create( _configuration, endpoint, true, false, sessionID, 60000, new UserIdentity(new AnonymousIdentityToken()), null); if (session != null) { session.KeepAlive += new KeepAliveEventHandler(StandardClient_KeepAlive); // Update our cache data if (OpcSessionCache.TryGetValue(sessionID, out entry)) { if (string.Equals(entry.EndpointURL, endpointURL, StringComparison.InvariantCultureIgnoreCase)) { OpcSessionCacheData newValue = new OpcSessionCacheData { CertThumbprint = entry.CertThumbprint, EndpointURL = entry.EndpointURL, Trusted = entry.Trusted, OPCSession = session }; OpcSessionCache.TryUpdate(sessionID, newValue, entry); } } } return(session); }