/* ======================================================================== * 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.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Opc.Ua.Security.Certificates; 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(ArrayOf input) { var output = new CertificateList(); if (!input.IsEmpty) { output.ValidationOptions = 0; output.Certificates = []; for (int ii = 0; ii < input.Count; ii++) { CertificateIdentifier? converted = ToCertificateIdentifier(input[ii]); if (converted != null) { output.Certificates.Add(converted); } } } return output; } /// /// Creates an ArrayOf<CertificateIdentifier> object. /// public static ArrayOf FromCertificateList(CertificateList? input) { var output = new List(); if (input != null && input.Certificates != null) { for (int ii = 0; ii < input.Certificates.Count; ii++) { output.Add(FromCertificateIdentifier(input.Certificates[ii])); } } return output.ToArrayOf(); } /// /// Creates a ListOfBaseAddresses object. /// public static ListOfBaseAddresses ToListOfBaseAddresses( ServerBaseConfiguration configuration) { var addresses = new ListOfBaseAddresses(); if (configuration != null) { if (!configuration.BaseAddresses.IsEmpty) { for (int ii = 0; ii < configuration.BaseAddresses.Count; ii++) { addresses.Add(configuration.BaseAddresses[ii]); } } if (!configuration.AlternateBaseAddresses.IsEmpty) { 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) { List baseAddresses = []; List alternateBaseAddresses = []; for (int ii = 0; ii < addresses.Count; ii++) { Uri? url = Utils.ParseUri(addresses[ii]); if (url != null) { if (!map.TryAdd(url.Scheme, string.Empty)) { alternateBaseAddresses.Add(url.ToString()); } else { baseAddresses.Add(url.ToString()); } } } configuration.BaseAddresses = baseAddresses; configuration.AlternateBaseAddresses = alternateBaseAddresses; } } /// /// Creates a ListOfSecurityProfiles object. /// public static ListOfSecurityProfiles ToListOfSecurityProfiles( ArrayOf 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.IsEmpty) { 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 an ArrayOf<ServerSecurityPolicy> object. /// public static ArrayOf FromListOfSecurityProfiles( ListOfSecurityProfiles? profiles) { var policies = new List(); if (profiles != null) { for (int ii = 0; ii < profiles.Count; ii++) { if (profiles[ii].Enabled && !string.IsNullOrEmpty(profiles[ii].ProfileUri)) { policies.Add(CreatePolicy(profiles[ii].ProfileUri!)); } } } if (policies.Count == 0) { policies.Add(CreatePolicy(SecurityPolicies.None)); } return policies.ToArrayOf(); } /// /// 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 recommended."); result = 4; break; case SecurityPolicies.ECC_nistP256: case SecurityPolicies.ECC_nistP384: logger.LogWarning( "Deprecated Security Policy {PolicyUri} requested - Use ECC_nistP[256/384]_AES.", policyUri); result = 4; break; case SecurityPolicies.ECC_brainpoolP256r1: case SecurityPolicies.ECC_brainpoolP384r1: logger.LogWarning( "Deprecated Security Policy {PolicyUri} requested - Use ECC_brainpoolP[256/384]r1_AES.", policyUri); 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.RSA_DH_AesGcm: case SecurityPolicies.RSA_DH_ChaChaPoly: case SecurityPolicies.ECC_brainpoolP256r1_AesGcm: case SecurityPolicies.ECC_brainpoolP256r1_ChaChaPoly: case SecurityPolicies.ECC_nistP256_AesGcm: case SecurityPolicies.ECC_nistP256_ChaChaPoly: result = 12; break; case SecurityPolicies.ECC_nistP384_AesGcm: case SecurityPolicies.ECC_nistP384_ChaChaPoly: case SecurityPolicies.ECC_brainpoolP384r1_AesGcm: case SecurityPolicies.ECC_brainpoolP384r1_ChaChaPoly: 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 CertificateIdentifierResolver.ResolveAsync( output, registry: null, needPrivateKey: false, applicationUri: null, telemetry, 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 CertificateIdentifierResolver.ResolveAsync( output, registry: null, needPrivateKey, applicationUri: null, telemetry, ct); } /// /// Opens the certificate store. /// public ICertificateStore OpenStore(ITelemetryContext telemetry) { Ua.CertificateIdentifier output = SecuredApplication.FromCertificateIdentifier(this); return CertificateIdentifierResolver.OpenStore(output, 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); } } }