diff --git a/README.md b/README.md index 29fe454..1b5bc6b 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,26 @@ Lazvard Message is an AMQP server simulator that is **unofficially** compatible ## Setup -Download the last version of the Lazvard from release page or clone and build the project, the application will create the default config file if couldn't find it on the first run. -The config file is in TOML format. Before running Lazvard, you need to define all the queues, topics, and subscriptions in the config file. The AMQP server require a valid and trusted X.509 certificate (PFX - PKCS #12). In Windows and macOS, the application can create and trust a certificate (using a copy of the Microsoft .NET CLI - certificate manager). However, for Linux, you will need to manually set the certificate as trusted. +You need to have .NET 8 installed on your operating system to run this project. You can download it from [here](https://dotnet.microsoft.com/en-us/download). -It's important to note that Lazvard is stateless. Therefore, once you close it, all messages and information will be lost. + +Since this application is not signed, you may encounter issues running it. The simplest way to run the project is to clone and build it on your operating system: +` +git clone https://github.com/PejmanNik/lazvard-message.git +cd lazvard-message +dotnet run --project ./src/Lazvard.Message.Cli +` + +Alternatively, you can download the latest version of Lazvard from the release page. At least on Windows, you will need to manually trust the application in Microsoft's SmartScreen upon first run. + +` +wget https://github.com/PejmanNik/lazvard-message/releases/download/v0.1.0/win-x64.zip +` + +The application will create a default config file if it's not found on the first run. This config file is in TOML format. Before running Lazvard, you need to define all the queues, topics, and subscriptions in the config file. The AMQP server requires a valid and trusted X.509 certificate (PFX - PKCS #12). On Windows and macOS, the application can create and trust certificates (powered by dotnet dev-certs). However, for Linux, you will need to manually set the certificate as trusted. + +It's important to note that Lazvard is stateless, meaning that once you close it, all messages and information will be lost. ## Different behavior diff --git a/src/Lazvard.Message.Amqp.Server/BrokerConfig.cs b/src/Lazvard.Message.Amqp.Server/BrokerConfig.cs index 80a70b2..656e4ac 100644 --- a/src/Lazvard.Message.Amqp.Server/BrokerConfig.cs +++ b/src/Lazvard.Message.Amqp.Server/BrokerConfig.cs @@ -33,7 +33,7 @@ public TopicConfig(string name, IEnumerable subscriptio public class BrokerConfig { public int Port { get; set; } = AmqpConstants.DefaultSecurePort; - public string IP { get; set; } = "127.0.0.1"; + public string IP { get; set; } = "localhost"; public uint MaxFrameSize { get; set; } = 64 * 1024; public uint ConnectionIdleTimeOut { get; set; } = 4 * 60 * 1000; public uint MaxMessageSize { get; set; } = 64 * 1024 * 1024; diff --git a/src/Lazvard.Message.Amqp.Server/Lazvard.Message.Amqp.Server.csproj b/src/Lazvard.Message.Amqp.Server/Lazvard.Message.Amqp.Server.csproj index 427ffc6..e4d1d55 100644 --- a/src/Lazvard.Message.Amqp.Server/Lazvard.Message.Amqp.Server.csproj +++ b/src/Lazvard.Message.Amqp.Server/Lazvard.Message.Amqp.Server.csproj @@ -15,13 +15,9 @@ Pejman Nikram README.md git - 1.1.0 + 1.2.0 - - - - True diff --git a/src/Lazvard.Message.Cli/CertificateGeneration/CertificateExportFormat.cs b/src/Lazvard.Message.Cli/CertificateGeneration/CertificateExportFormat.cs index 9a7d6a5..70705da 100644 --- a/src/Lazvard.Message.Cli/CertificateGeneration/CertificateExportFormat.cs +++ b/src/Lazvard.Message.Cli/CertificateGeneration/CertificateExportFormat.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Lazvard.Message.Cli.CertificateGeneration; +namespace Microsoft.AspNetCore.Certificates.Generation; internal enum CertificateKeyExportFormat { diff --git a/src/Lazvard.Message.Cli/CertificateGeneration/CertificateManager.cs b/src/Lazvard.Message.Cli/CertificateGeneration/CertificateManager.cs index 727cfd1..272c4c3 100644 --- a/src/Lazvard.Message.Cli/CertificateGeneration/CertificateManager.cs +++ b/src/Lazvard.Message.Cli/CertificateGeneration/CertificateManager.cs @@ -4,12 +4,15 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Tracing; +using System.Linq; +using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; +#nullable enable -namespace Lazvard.Message.Cli.CertificateGeneration; +namespace Microsoft.AspNetCore.Certificates.Generation; internal abstract class CertificateManager { @@ -17,7 +20,7 @@ internal abstract class CertificateManager // OID used for HTTPS certs internal const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1"; - internal const string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate"; + internal static string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate"; private const string ServerAuthenticationEnhancedKeyUsageOid = "1.3.6.1.5.5.7.3.1"; private const string ServerAuthenticationEnhancedKeyUsageOidFriendlyName = "Server Authentication"; @@ -75,7 +78,7 @@ public IList ListCertificates( { using var store = new X509Store(storeName, location); store.Open(OpenFlags.ReadOnly); - PopulateCertificatesFromStore(store, certificates); + PopulateCertificatesFromStore(store, certificates, requireExportable); IEnumerable matchingCertificates = certificates; matchingCertificates = matchingCertificates .Where(c => HasOid(c, AspNetHttpsOid)); @@ -93,7 +96,7 @@ public IList ListCertificates( var now = DateTimeOffset.Now; var validCertificates = matchingCertificates .Where(c => IsValidCertificate(c, now, requireExportable)) - .OrderByDescending(c => GetCertificateVersion(c)) + .OrderByDescending(GetCertificateVersion) .ToArray(); if (Log.IsEnabled()) @@ -158,7 +161,7 @@ bool IsValidCertificate(X509Certificate2 certificate, DateTimeOffset currentDate GetCertificateVersion(certificate) >= AspNetHttpsCertificateVersion; } - protected virtual void PopulateCertificatesFromStore(X509Store store, List certificates) + protected virtual void PopulateCertificatesFromStore(X509Store store, List certificates, bool requireExportable) { certificates.AddRange(store.Certificates.OfType()); } @@ -546,6 +549,14 @@ internal static void ExportCertificate(X509Certificate2 certificate, string path try { Log.WriteCertificateToDisk(path); + + // Create a temp file with the correct Unix file mode before moving it to the expected path. + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var tempFilename = Path.GetTempFileName(); + File.Move(tempFilename, path, overwrite: true); + } + File.WriteAllBytes(path, bytes); } catch (Exception ex) when (Log.IsEnabled()) @@ -566,6 +577,14 @@ internal static void ExportCertificate(X509Certificate2 certificate, string path { var keyPath = Path.ChangeExtension(path, ".key"); Log.WritePemKeyToDisk(keyPath); + + // Create a temp file with the correct Unix file mode before moving it to the expected path. + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var tempFilename = Path.GetTempFileName(); + File.Move(tempFilename, keyPath, overwrite: true); + } + File.WriteAllBytes(keyPath, pemEnvelope); } catch (Exception ex) when (Log.IsEnabled()) diff --git a/src/Lazvard.Message.Cli/CertificateGeneration/CertificatePurpose.cs b/src/Lazvard.Message.Cli/CertificateGeneration/CertificatePurpose.cs index de5a138..7abe411 100644 --- a/src/Lazvard.Message.Cli/CertificateGeneration/CertificatePurpose.cs +++ b/src/Lazvard.Message.Cli/CertificateGeneration/CertificatePurpose.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Lazvard.Message.Cli.CertificateGeneration; +namespace Microsoft.AspNetCore.Certificates.Generation; internal enum CertificatePurpose { diff --git a/src/Lazvard.Message.Cli/CertificateGeneration/EnsureCertificateResult.cs b/src/Lazvard.Message.Cli/CertificateGeneration/EnsureCertificateResult.cs index a3ae5f8..842b84a 100644 --- a/src/Lazvard.Message.Cli/CertificateGeneration/EnsureCertificateResult.cs +++ b/src/Lazvard.Message.Cli/CertificateGeneration/EnsureCertificateResult.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Lazvard.Message.Cli.CertificateGeneration; +namespace Microsoft.AspNetCore.Certificates.Generation; internal enum EnsureCertificateResult { diff --git a/src/Lazvard.Message.Cli/CertificateGeneration/ImportCertificateResult.cs b/src/Lazvard.Message.Cli/CertificateGeneration/ImportCertificateResult.cs index 0d0ce0e..53706d8 100644 --- a/src/Lazvard.Message.Cli/CertificateGeneration/ImportCertificateResult.cs +++ b/src/Lazvard.Message.Cli/CertificateGeneration/ImportCertificateResult.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Lazvard.Message.Cli.CertificateGeneration; +namespace Microsoft.AspNetCore.Certificates.Generation; internal enum ImportCertificateResult { diff --git a/src/Lazvard.Message.Cli/CertificateGeneration/MacOSCertificateManager.cs b/src/Lazvard.Message.Cli/CertificateGeneration/MacOSCertificateManager.cs index bb7a8de..30237e4 100644 --- a/src/Lazvard.Message.Cli/CertificateGeneration/MacOSCertificateManager.cs +++ b/src/Lazvard.Message.Cli/CertificateGeneration/MacOSCertificateManager.cs @@ -4,11 +4,12 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; -namespace Lazvard.Message.Cli.CertificateGeneration; +namespace Microsoft.AspNetCore.Certificates.Generation; internal sealed class MacOSCertificateManager : CertificateManager { @@ -372,14 +373,14 @@ protected override IList GetCertificatesToRemove(StoreName sto return ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: false); } - protected override void PopulateCertificatesFromStore(X509Store store, List certificates) + protected override void PopulateCertificatesFromStore(X509Store store, List certificates, bool requireExportable) { if (store.Name! == StoreName.My.ToString() && store.Location == StoreLocation.CurrentUser && Directory.Exists(MacOSUserHttpsCertificateLocation)) { var certsFromDisk = GetCertsFromDisk(); var certsFromStore = new List(); - base.PopulateCertificatesFromStore(store, certsFromStore); + base.PopulateCertificatesFromStore(store, certsFromStore, requireExportable); // Certs created by pre-.NET 7. var onlyOnKeychain = certsFromStore.Except(certsFromDisk, ThumbprintComparer.Instance); @@ -387,10 +388,13 @@ protected override void PopulateCertificatesFromStore(X509Store store, List ReadCertificate(string path, string password) + static CertificateHandler() + { + CertificateManager.AspNetHttpsOidFriendlyName = oidFriendlyName; + } + + public static Result ReadCertificateFromFile(string path, string password) { try { - return new X509Certificate2(Path, password); + return new X509Certificate2(path, password); } catch (Exception e) { @@ -22,31 +26,56 @@ public static Result ReadCertificate(string path, string passw } } - public static Result CreateAndTrustCertificate(string serverIp, string password) + public static Result ReadCertificateFromStore() { - var rsaKey = RSA.Create(2048); - var req = new CertificateRequest("cn=localhost", rsaKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + try + { + var cert = CertificateManager.Instance + .ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: true, requireExportable: false) + .FirstOrDefault(); - var sanBuilder = new SubjectAlternativeNameBuilder(); - sanBuilder.AddIpAddress(IPAddress.Parse(serverIp)); - sanBuilder.AddDnsName("localhost"); - req.CertificateExtensions.Add(sanBuilder.Build()); + if (cert is null) + { + return Result.Fail(); + } - var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1)); + var status = CertificateManager.Instance.CheckCertificateState(cert, interactive: false); + if (!status.Success) + { + return Result.Fail(); + } - //Create PFX(PKCS #12) with private key - File.WriteAllBytes(Path, cert.Export(X509ContentType.Pfx, password)); + if (!CertificateManager.Instance.IsTrusted(cert)) + { + return Result.Fail(); + } - if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS()) + return cert; + } + catch (Exception e) { - return Result.Success(); + return Result.Fail(e.Message); } + } - var manager = CertificateManager.Instance; + public static Result CreateAndTrustCertificate() + { try { - manager.TrustCertificate(cert); - return Result.Success(); + var manager = CertificateManager.Instance; + var now = DateTimeOffset.Now; + var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate( + notBefore: now, + notAfter: now.AddYears(1), + isInteractive: false, + trust: !RuntimeInformation.IsOSPlatform(OSPlatform.Linux)); + + if (result == EnsureCertificateResult.Succeeded + || result == EnsureCertificateResult.NewHttpsCertificateTrusted + || result == EnsureCertificateResult.ExistingHttpsCertificateTrusted) + return Result.Success(); + + return Result.Fail(result.ToString()); } catch (Exception) { diff --git a/src/Lazvard.Message.Cli/CliStartup.cs b/src/Lazvard.Message.Cli/CliStartup.cs index 4568249..bf8a7fb 100644 --- a/src/Lazvard.Message.Cli/CliStartup.cs +++ b/src/Lazvard.Message.Cli/CliStartup.cs @@ -1,4 +1,5 @@ -using System.Security.Cryptography.X509Certificates; +using Lazvard.Message.Amqp.Server.Helpers; +using System.Security.Cryptography.X509Certificates; namespace Lazvard.Message.Cli; @@ -9,16 +10,20 @@ public class CliStartup if (!Configuration.Exists()) { Console.WriteLine("Can't find config file, creating the init config file..."); + Console.WriteLine(); - var (certificatePath, certificatePassword, isTrusted) = AddCertificate("127.0.0.1"); - await Configuration.CreateDefaultConfigAsync(certificatePath, certificatePassword); - Console.WriteLine(); - Console.WriteLine("config file successfully created!"); + await Configuration.CreateDefaultConfigAsync(); + Console.WriteLine("default config file successfully created!"); - if (!isTrusted) + var certificateStoreResult = CertificateHandler.ReadCertificateFromStore(); + if (!certificateStoreResult.IsSuccess) { - Environment.Exit(0); + var addCertificateResult = AddCertificate(); + if (!addCertificateResult.IsSuccess) + { + Environment.Exit(0); + } } } @@ -30,39 +35,54 @@ public class CliStartup Environment.Exit(0); } - if (string.IsNullOrEmpty(config.Value.CertificatePath)) + var cert = ReadCertificate( + config.Value.UseBuiltInCertificateManager, + config.Value.CertificatePath, + config.Value.CertificatePassword); + + if (!cert.IsSuccess) { - Console.WriteLine("There is no certificate path in the config file"); + Console.WriteLine($"Can't load the certificate. please check the certificate or use the built-in certificate manager"); + Console.WriteLine($"In order to troubleshoot the built-in certificate manager please use dotnet dev-certs documentation"); + Console.WriteLine($"Error: {cert.Error}"); - var (certificatePath, certificatePassword, isTrusted) = AddCertificate("127.0.0.1"); - config.Value.CertificatePath = certificatePath; - config.Value.CertificatePassword = certificatePassword; + Environment.Exit(0); + } - await Configuration.WriteAsync(config.Value); + return (config.Value, cert.Value); + } - if (!isTrusted) - { - Environment.Exit(0); - } + private static Result ReadCertificate( + bool useBuiltInCertificateManager, + string certificatePath, + string certificatePassword) + { + if (!useBuiltInCertificateManager) + { + return CertificateHandler.ReadCertificateFromFile(certificatePath, certificatePassword); } - var cert = CertificateHandler.ReadCertificate(config.Value.CertificatePath, config.Value.CertificatePassword); - if (!cert.IsSuccess) + var certificateStoreResult = CertificateHandler.ReadCertificateFromStore(); + if (!certificateStoreResult.IsSuccess) { - Console.WriteLine($"Can't load the certificate. please check the certificate or removed the certificatePath from config file"); - Console.WriteLine($"Error: {cert.Error}"); + var trustCertificateResult = CertificateHandler.CreateAndTrustCertificate(); + if (trustCertificateResult.IsSuccess) + { + return CertificateHandler.ReadCertificateFromStore(); + } - Environment.Exit(0); + return trustCertificateResult; } - return (config.Value, cert.Value); + return certificateStoreResult; } - public static (string certificatePath, string certificatePasswor, bool isTrusted) AddCertificate(string ip) + public static Result AddCertificate() { Console.WriteLine(); Console.WriteLine("ServiceBus needs a valid certificate to start HTTPs server."); - Console.WriteLine("You can add the certificate info in the config file, or create a new one"); + Console.WriteLine("You can add the certificate info in the config file,"); + Console.WriteLine("or create a new certificate using built-in certificate manager (powered by dotnet dev-certs)"); Console.WriteLine(); Console.Write("Do you want to create and trust a self signed certificate (y/n)? "); @@ -70,25 +90,23 @@ public static (string certificatePath, string certificatePasswor, bool isTrusted var answer = Console.ReadLine(); if (answer?.StartsWith("y", StringComparison.OrdinalIgnoreCase) == true) { - var password = $"{Guid.NewGuid()}-{Guid.NewGuid()}"; - var certificate = CertificateHandler.CreateAndTrustCertificate(ip, password); + Console.WriteLine($"A confirmation prompt will be displayed if the certificate was not previously trusted. Click yes on the prompt to trust the certificate"); + var certificate = CertificateHandler.CreateAndTrustCertificate(); if (!certificate.IsSuccess) { - Console.WriteLine("Failed to create and trust certificate"); - return (string.Empty, string.Empty, false); + Console.WriteLine($"Failed to create and trust certificate, error: {certificate.Error}"); + Console.WriteLine($"In order to troubleshoot the issue, please use dotnet dev-certs documentation"); + return Result.Fail(); } else if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS()) { - Console.WriteLine("Unfortunately, it is not possible to add the created certificate (whose path can be found in the config file) to the trusted certificate store in the Linux operating system"); - Console.WriteLine(); - Console.WriteLine("You need to manually add this certificate as a trusted root certificate to your operation system and start the application again"); - - return (CertificateHandler.Path, password, false); + Console.WriteLine("Unfortunately, rusting the certificate on Linux distributions automatically is not supported. For instructions on how to manually trust the certificate on your Linux distribution, go to https://aka.ms/dev-certs-trust"); + return Result.Fail(); } else { - return (CertificateHandler.Path, password, true); + return Result.Success(); } } @@ -97,6 +115,6 @@ public static (string certificatePath, string certificatePasswor, bool isTrusted Console.WriteLine("You need to manually create a certificate and add it as a trusted root certificate to your operation system, add it to the config file and start the application again"); Console.WriteLine(); - return (string.Empty, string.Empty, false); + return Result.Fail(); } } diff --git a/src/Lazvard.Message.Cli/Configuration.cs b/src/Lazvard.Message.Cli/Configuration.cs index 5d3423b..a2a7731 100644 --- a/src/Lazvard.Message.Cli/Configuration.cs +++ b/src/Lazvard.Message.Cli/Configuration.cs @@ -7,6 +7,7 @@ namespace Lazvard.Message.Cli; public class CliConfig : BrokerConfig { + public bool UseBuiltInCertificateManager { get; set; } = true; public string CertificatePath { get; set; } = string.Empty; public string CertificatePassword { get; set; } = string.Empty; } @@ -41,10 +42,15 @@ public static async Task WriteAsync(CliConfig config) Value = config.Port, Comment = "Port to listen on" }, + [nameof(CliConfig.UseBuiltInCertificateManager)] = new TomlString + { + Value = config.CertificatePath, + Comment = "Use built-in certificate manager (dotnet dev-certs) instead of CertificatePath" + }, [nameof(CliConfig.CertificatePath)] = new TomlString { Value = config.CertificatePath, - Comment = "The path to trusted X.509 certificate (PFX - PKCS #12)" + Comment = "The path to trusted X.509 certificate (PFX - PKCS #12), required only when UseBuiltInCertificateManager is false" }, [nameof(CliConfig.CertificatePassword)] = new TomlString { @@ -151,12 +157,10 @@ private static TomlTable BuildSubscription(TopicSubscriptionConfig config) }; } - public static async Task CreateDefaultConfigAsync(string certificatePath, string certificatePassword) + public static async Task CreateDefaultConfigAsync() { var config = new CliConfig { - CertificatePath = certificatePath, - CertificatePassword = certificatePassword, Topics = new TopicConfig[] { new TopicConfig("topic-1", new[] diff --git a/src/Lazvard.Message.Cli/Lazvard.Message.Cli.csproj b/src/Lazvard.Message.Cli/Lazvard.Message.Cli.csproj index cfd6c5f..e799b53 100644 --- a/src/Lazvard.Message.Cli/Lazvard.Message.Cli.csproj +++ b/src/Lazvard.Message.Cli/Lazvard.Message.Cli.csproj @@ -6,7 +6,7 @@ enable enable true - true + false True snupkg False @@ -20,9 +20,9 @@ Pejman Nikram lightweight AMQP server - Azure Service Bus simulator amqp;service-bus;azure-service-bus;amqp-server - 0.1.0 - 0.1.0 - 0.1.0 + 0.2.0 + 0.2.0 + 0.2.0 diff --git a/test/Lazvard.Message.Amqp.Server.IntegrationTests/ClientFixture.cs b/test/Lazvard.Message.Amqp.Server.IntegrationTests/ClientFixture.cs index 64bf1aa..dd9b387 100644 --- a/test/Lazvard.Message.Amqp.Server.IntegrationTests/ClientFixture.cs +++ b/test/Lazvard.Message.Amqp.Server.IntegrationTests/ClientFixture.cs @@ -8,7 +8,7 @@ public sealed class ClientFixture : IAsyncDisposable public ClientFixture() { - string connectionString = "Endpoint=sb://127.0.0.1/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=1"; + string connectionString = "Endpoint=sb://localhost/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=1"; Client = new(connectionString); } diff --git a/test/Lazvard.Message.Amqp.Server.IntegrationTests/ServerFixture.cs b/test/Lazvard.Message.Amqp.Server.IntegrationTests/ServerFixture.cs index 93f0be9..3a67617 100644 --- a/test/Lazvard.Message.Amqp.Server.IntegrationTests/ServerFixture.cs +++ b/test/Lazvard.Message.Amqp.Server.IntegrationTests/ServerFixture.cs @@ -10,7 +10,6 @@ internal sealed class ServerFixture : IDisposable { private readonly Broker broker; private readonly CancellationTokenSource source; - private const string certificatePassword = "P@55w0rd"; public ServerFixture(IMessageSink testOutputHelper) { @@ -39,11 +38,11 @@ public ServerFixture(IMessageSink testOutputHelper) }, }; - var cert = CertificateHandler.ReadCertificate(CertificateHandler.Path, certificatePassword); + var cert = CertificateHandler.ReadCertificateFromStore(); if (!cert.IsSuccess) { - CertificateHandler.CreateAndTrustCertificate("127.0.0.1", certificatePassword); - cert = CertificateHandler.ReadCertificate(CertificateHandler.Path, certificatePassword); + CertificateHandler.CreateAndTrustCertificate(); + cert = CertificateHandler.ReadCertificateFromStore(); } if (!cert.IsSuccess)