/* ======================================================================== * 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.Collections.Generic; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace Opc.Ua.Security { /// /// Stores the security settings for an application. /// public partial class SecuredApplication { /// /// Casts a ApplicationType value. /// public static Ua.ApplicationType FromApplicationType(ApplicationType input) { return (Ua.ApplicationType)(int)input; } /// /// Casts a ApplicationType value. /// public static ApplicationType ToApplicationType(Ua.ApplicationType input) { return (ApplicationType)(int)input; } /// /// Creates a CertificateIdentifier object. /// public static CertificateIdentifier ToCertificateIdentifier(Ua.CertificateIdentifier input) { if (input != null && !string.IsNullOrEmpty(input.StoreType) && !string.IsNullOrEmpty(input.StorePath)) { return new CertificateIdentifier { StoreType = input.StoreType, StorePath = input.StorePath, SubjectName = input.SubjectName, Thumbprint = input.Thumbprint, ValidationOptions = (int)input.ValidationOptions, OfflineRevocationList = null, OnlineRevocationList = null }; } return null; } /// /// Creates a CertificateIdentifier object. /// public static Ua.CertificateIdentifier FromCertificateIdentifier( CertificateIdentifier input) { var output = new Ua.CertificateIdentifier(); if (input != null) { output.StoreType = input.StoreType; output.StorePath = input.StorePath; output.SubjectName = input.SubjectName; output.Thumbprint = input.Thumbprint; output.ValidationOptions = (CertificateValidationOptions)input.ValidationOptions; } return output; } /// /// Creates a CertificateStoreIdentifier object. /// public static CertificateStoreIdentifier ToCertificateStoreIdentifier( Ua.CertificateStoreIdentifier input) { if (input != null && !string.IsNullOrEmpty(input.StoreType) && !string.IsNullOrEmpty(input.StorePath)) { return new CertificateStoreIdentifier { StoreType = input.StoreType, StorePath = input.StorePath, ValidationOptions = (int)input.ValidationOptions }; } return null; } /// /// Creates a CertificateTrustList object. /// public static CertificateTrustList FromCertificateStoreIdentifierToTrustList( CertificateStoreIdentifier input) { var output = new CertificateTrustList(); if (input != null) { output.StoreType = input.StoreType; output.StorePath = input.StorePath; output.ValidationOptions = (CertificateValidationOptions)input.ValidationOptions; } return output; } /// /// Creates a CertificateStoreIdentifier object. /// public static Ua.CertificateStoreIdentifier FromCertificateStoreIdentifier( CertificateStoreIdentifier input) { var output = new Ua.CertificateStoreIdentifier(); if (input != null) { output.StoreType = input.StoreType; output.StorePath = input.StorePath; output.ValidationOptions = (CertificateValidationOptions)input.ValidationOptions; } return output; } /// /// Creates a CertificateTrustList object. /// public static CertificateTrustList ToCertificateTrustList(CertificateStoreIdentifier input) { var output = new CertificateTrustList(); if (input != null) { output.StoreType = input.StoreType; output.StorePath = input.StorePath; output.ValidationOptions = (CertificateValidationOptions)input.ValidationOptions; } return output; } /// /// Creates a CertificateList object. /// public static CertificateList ToCertificateList(CertificateIdentifierCollection input) { var output = new CertificateList(); if (input != null) { output.ValidationOptions = 0; output.Certificates = []; for (int ii = 0; ii < input.Count; ii++) { output.Certificates.Add(ToCertificateIdentifier(input[ii])); } } return output; } /// /// Creates a CertificateIdentifierCollection object. /// public static CertificateIdentifierCollection FromCertificateList(CertificateList input) { var output = new CertificateIdentifierCollection(); if (input != null && input.Certificates != null) { for (int ii = 0; ii < input.Certificates.Count; ii++) { output.Add(FromCertificateIdentifier(input.Certificates[ii])); } } return output; } /// /// Creates a ListOfBaseAddresses object. /// public static ListOfBaseAddresses ToListOfBaseAddresses( ServerBaseConfiguration configuration) { var addresses = new ListOfBaseAddresses(); if (configuration != null) { if (configuration.BaseAddresses != null) { for (int ii = 0; ii < configuration.BaseAddresses.Count; ii++) { addresses.Add(configuration.BaseAddresses[ii]); } } if (configuration.AlternateBaseAddresses != null) { for (int ii = 0; ii < configuration.AlternateBaseAddresses.Count; ii++) { addresses.Add(configuration.AlternateBaseAddresses[ii]); } } } return addresses; } /// /// Creates a ListOfBaseAddresses object. /// public static void FromListOfBaseAddresses( ServerBaseConfiguration configuration, ListOfBaseAddresses addresses) { var map = new Dictionary(); if (addresses != null && configuration != null) { configuration.BaseAddresses = []; configuration.AlternateBaseAddresses = null; for (int ii = 0; ii < addresses.Count; ii++) { Uri url = Utils.ParseUri(addresses[ii]); if (url != null) { if (!map.TryAdd(url.Scheme, string.Empty)) { (configuration.AlternateBaseAddresses ??= []).Add(url.ToString()); } else { configuration.BaseAddresses.Add(url.ToString()); } } } } } /// /// Creates a ListOfSecurityProfiles object. /// public static ListOfSecurityProfiles ToListOfSecurityProfiles( ServerSecurityPolicyCollection policies) { var profiles = new ListOfSecurityProfiles { CreateProfile(SecurityPolicies.None), CreateProfile(SecurityPolicies.Basic128Rsa15), CreateProfile(SecurityPolicies.Basic256), CreateProfile(SecurityPolicies.Basic256Sha256), CreateProfile(SecurityPolicies.Aes128_Sha256_RsaOaep), CreateProfile(SecurityPolicies.Aes256_Sha256_RsaPss) }; if (policies != null) { for (int ii = 0; ii < policies.Count; ii++) { for (int jj = 0; jj < profiles.Count; jj++) { if (policies[ii].SecurityPolicyUri == profiles[jj].ProfileUri) { profiles[jj].Enabled = true; } } } } return profiles; } /// /// Creates a ServerSecurityPolicyCollection object. /// public static ServerSecurityPolicyCollection FromListOfSecurityProfiles( ListOfSecurityProfiles profiles) { var policies = new ServerSecurityPolicyCollection(); if (profiles != null) { for (int ii = 0; ii < profiles.Count; ii++) { if (profiles[ii].Enabled) { policies.Add(CreatePolicy(profiles[ii].ProfileUri)); } } } if (policies.Count == 0) { policies.Add(CreatePolicy(SecurityPolicies.None)); } return policies; } /// /// Obsolete version of CalculateSecurityLevel that does not take a logger. /// [Obsolete("Use CalculateSecurityLevel(MessageSecurityMode mode, string policyUri, ILogger logger) instead.")] public static byte CalculateSecurityLevel( MessageSecurityMode mode, string policyUri) { ILogger logger = AmbientMessageContext.Telemetry.CreateLogger(); return CalculateSecurityLevel(mode, policyUri, logger); } /// /// Calculates the security level, given the security mode and policy /// Invalid and none is discouraged /// Just signing is always weaker than any use of encryption /// public static byte CalculateSecurityLevel( MessageSecurityMode mode, string policyUri, ILogger logger) { if ((mode != MessageSecurityMode.Sign && mode != MessageSecurityMode.SignAndEncrypt) || policyUri == null) { return 0; } byte result; switch (policyUri) { case SecurityPolicies.Basic128Rsa15: logger.LogWarning( "Deprecated Security Policy Basic128Rsa15 requested - Not recommended."); result = 2; break; case SecurityPolicies.Basic256: logger.LogWarning( "Deprecated Security Policy Basic256 requested - Not rcommended."); result = 4; break; case SecurityPolicies.Basic256Sha256: result = 6; break; case SecurityPolicies.Aes128_Sha256_RsaOaep: result = 8; break; case SecurityPolicies.Aes256_Sha256_RsaPss: result = 10; break; case SecurityPolicies.ECC_brainpoolP256r1: result = 11; break; case SecurityPolicies.ECC_nistP256: result = 12; break; case SecurityPolicies.ECC_brainpoolP384r1: result = 13; break; case SecurityPolicies.ECC_nistP384: result = 14; break; case SecurityPolicies.None: return 0; default: logger.LogWarning( "Security level requested for unknown Security Policy {Policy}. Returning security level 0", policyUri); return 0; } if (mode == MessageSecurityMode.SignAndEncrypt) { result += 100; } return result; } /// /// Creates a new policy object. /// Always uses sign and encrypt for all security policies except none /// private static ServerSecurityPolicy CreatePolicy(string profileUri) { return new ServerSecurityPolicy { SecurityPolicyUri = profileUri, SecurityMode = profileUri == SecurityPolicies.None ? MessageSecurityMode.None : MessageSecurityMode.SignAndEncrypt }; } /// /// Creates a new policy object. /// private static SecurityProfile CreateProfile(string profileUri) { return new SecurityProfile { ProfileUri = profileUri, Enabled = false }; } /// /// TODO: Holds the application certificates but should be generated and the Opc.Ua.Security namespace automatically /// TODO: Should replace ApplicationCertificateField in the generated Opc.Ua.Security.SecuredApplication class /// public CertificateList ApplicationCertificates { get; set; } } /// /// An identifier for a certificate. /// public partial class CertificateIdentifier { /// /// Gets the certificate associated with the identifier. /// [Obsolete("Use FindAsync()")] public Task Find() { return FindAsync(null); } /// /// Gets the certificate associated with the identifier. /// public Task FindAsync( ITelemetryContext telemetry, CancellationToken ct = default) { Ua.CertificateIdentifier output = SecuredApplication.FromCertificateIdentifier(this); return output.FindAsync(false, telemetry: telemetry, ct: ct); } /// /// Gets the certificate associated with the identifier. /// [Obsolete("Use FindAsync(needPrivateKey)")] public Task Find(bool needPrivateKey) { return FindAsync(needPrivateKey, null); } /// /// Gets the certificate associated with the identifier. /// public Task FindAsync( bool needPrivateKey, ITelemetryContext telemetry, CancellationToken ct = default) { Ua.CertificateIdentifier output = SecuredApplication.FromCertificateIdentifier(this); return output.FindAsync(needPrivateKey, telemetry: telemetry, ct: ct); } /// /// Opens the certificate store. /// public ICertificateStore OpenStore(ITelemetryContext telemetry) { Ua.CertificateIdentifier output = SecuredApplication.FromCertificateIdentifier(this); return output.OpenStore(telemetry); } } /// /// An identifier for a certificate store. /// public partial class CertificateStoreIdentifier { /// /// Opens the certificate store. /// public ICertificateStore OpenStore(ITelemetryContext telemetry) { Ua.CertificateStoreIdentifier output = SecuredApplication .FromCertificateStoreIdentifier(this); return output.OpenStore(telemetry); } } }