From 11e4075203098bbf03958778b819faec212b330b Mon Sep 17 00:00:00 2001 From: Sang Au Date: Mon, 21 Oct 2024 22:44:17 +0700 Subject: [PATCH] enh: fix code analysis Signed-off-by: Sang Au --- .../CreateEcdsaCommandOptions.cs | 3 ++- .../CommandOptions/CreateRsaCommandOptions.cs | 4 +++- src/CertGen/CommandOptions/GlobalOptions.cs | 5 ++-- src/CertGen/Handlers.cs | 24 ++++++++++++------- .../Helpers/ExportCertificateHelper.cs | 18 +++++++------- src/CertGen/Helpers/GenerateSslHelper.cs | 15 +++++++----- src/Directory.Build.props | 6 ++++- 7 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/CertGen/CommandOptions/CreateEcdsaCommandOptions.cs b/src/CertGen/CommandOptions/CreateEcdsaCommandOptions.cs index 52b81d6..38913b4 100644 --- a/src/CertGen/CommandOptions/CreateEcdsaCommandOptions.cs +++ b/src/CertGen/CommandOptions/CreateEcdsaCommandOptions.cs @@ -1,5 +1,6 @@ using System.CommandLine; using System.CommandLine.Binding; +using System.Diagnostics.CodeAnalysis; namespace Sisa.Security; @@ -27,7 +28,7 @@ public sealed class CreateEcdsaCommandOptionsBinder( commonName ) { - protected override CreateEcdsaCommandOptions GetBoundValue(BindingContext bindingContext) => + protected override CreateEcdsaCommandOptions GetBoundValue([NotNull] BindingContext bindingContext) => base.GetBoundValue(bindingContext) with { NamedCurve = bindingContext.ParseResult.GetValueForOption(namedCurve) diff --git a/src/CertGen/CommandOptions/CreateRsaCommandOptions.cs b/src/CertGen/CommandOptions/CreateRsaCommandOptions.cs index 0834751..c662846 100644 --- a/src/CertGen/CommandOptions/CreateRsaCommandOptions.cs +++ b/src/CertGen/CommandOptions/CreateRsaCommandOptions.cs @@ -1,5 +1,6 @@ using System.CommandLine; using System.CommandLine.Binding; +using System.Diagnostics.CodeAnalysis; namespace Sisa.Security; @@ -27,7 +28,8 @@ public sealed class CreateRsaCommandOptionsBinder( commonName ) { - protected override CreateRsaCommandOptions GetBoundValue(BindingContext bindingContext) => + protected override CreateRsaCommandOptions GetBoundValue([NotNull + ] BindingContext bindingContext) => base.GetBoundValue(bindingContext) with { KeySize = bindingContext.ParseResult.GetValueForOption(keySize) diff --git a/src/CertGen/CommandOptions/GlobalOptions.cs b/src/CertGen/CommandOptions/GlobalOptions.cs index 1a01c9f..4e203db 100644 --- a/src/CertGen/CommandOptions/GlobalOptions.cs +++ b/src/CertGen/CommandOptions/GlobalOptions.cs @@ -1,5 +1,6 @@ using System.CommandLine; using System.CommandLine.Binding; +using System.Diagnostics.CodeAnalysis; namespace Sisa.Security; @@ -9,7 +10,7 @@ public record GlobalOptions public Algorithm Algorithm { get; set; } - public string[] DnsNames { get; set; } = []; + public IReadOnlyCollection DnsNames { get; set; } = []; public string? PfxPassword { get; set; } public string? OrganizationName { get; set; } @@ -31,7 +32,7 @@ public abstract class GlobalOptionsBinder( ) : BinderBase where TOptions : GlobalOptions, new() { - protected override TOptions GetBoundValue(BindingContext bindingContext) => + protected override TOptions GetBoundValue([NotNull] BindingContext bindingContext) => new() { CertName = bindingContext.ParseResult.GetValueForOption(certName)!, diff --git a/src/CertGen/Handlers.cs b/src/CertGen/Handlers.cs index 7f67907..2cef3a7 100644 --- a/src/CertGen/Handlers.cs +++ b/src/CertGen/Handlers.cs @@ -8,8 +8,8 @@ namespace Sisa.Security.Handlers; public static class CommandHandler { - private static readonly string RootCAName = "root-ca"; - private static readonly string RootCACommonName = "Sisa Development Root CA"; + private static string RootCAName { get; } = "root-ca"; + private static string RootCACommonName { get; } = "Sisa Development Root CA"; public static Command Initialize() { @@ -197,11 +197,13 @@ private static async Task CreateEcdsaCommandHandleAsync(CreateEcdsaCommandOption Console.WriteLine("Root CA exists"); Console.WriteLine("Loading root CA certificate"); +#pragma warning disable CA2000 // Dispose objects before losing scope rootCa = GenerateSslHelper.LoadRootCACertificate(rootCaFilePath, rootCaKeyFilePath); +#pragma warning restore CA2000 // Dispose objects before losing scope if (rootCa == null) { - await Console.Error.WriteLineAsync("Failed to load root CA certificate"); + await Console.Error.WriteLineAsync("Failed to load root CA certificate").ConfigureAwait(false); return; } @@ -209,13 +211,15 @@ private static async Task CreateEcdsaCommandHandleAsync(CreateEcdsaCommandOption else { var caSubjectName = GenerateSslHelper.BuildSubjectName(options.OrganizationName!, options.OrganizationUnitName!, RootCACommonName); +#pragma warning disable CA2000 // Dispose objects before losing scope rootCa = GenerateSslHelper.CreateEcdsaRootCACertificate(caSubjectName); +#pragma warning restore CA2000 // Dispose objects before losing scope } HashAlgorithmName hashAlgorithm = GenerateSslHelper.GetHashAlgorithm(options.Algorithm); var certSubjectName = GenerateSslHelper.BuildSubjectName(options.OrganizationName!, options.OrganizationUnitName!, options.CommonName!); - var cert = GenerateSslHelper.CreateEcdsaSelfSignCertificate( + using var cert = GenerateSslHelper.CreateEcdsaSelfSignCertificate( rootCa, certSubjectName, hashAlgorithm, @@ -267,11 +271,13 @@ private static async Task CreateRsaCommandHandleAsync(CreateRsaCommandOptions op Console.WriteLine("Root CA exists"); Console.WriteLine("Loading root CA certificate"); +#pragma warning disable CA2000 // Dispose objects before losing scope rootCa = GenerateSslHelper.LoadRootCACertificate(rootCaFilePath, rootCaKeyFilePath); +#pragma warning restore CA2000 // Dispose objects before losing scope if (rootCa == null) { - await Console.Error.WriteLineAsync("Failed to load root CA certificate"); + await Console.Error.WriteLineAsync("Failed to load root CA certificate").ConfigureAwait(false); return; } @@ -282,14 +288,16 @@ private static async Task CreateRsaCommandHandleAsync(CreateRsaCommandOptions op Console.WriteLine("Creating root CA certificate"); var caSubjectName = GenerateSslHelper.BuildSubjectName(options.OrganizationName!, options.OrganizationUnitName!, RootCACommonName); +#pragma warning disable CA2000 // Dispose objects before losing scope rootCa = GenerateSslHelper.CreateRsaRootCACertificate(caSubjectName); +#pragma warning restore CA2000 // Dispose objects before losing scope } HashAlgorithmName hashAlgorithm = GenerateSslHelper.GetHashAlgorithm(options.Algorithm); var certSubjectName = GenerateSslHelper.BuildSubjectName(options.OrganizationName!, options.OrganizationUnitName!, options.CommonName!); Console.WriteLine("Creating self-signed certificate"); - var cert = GenerateSslHelper.CreateRsaSelfSignCertificate( + using var cert = GenerateSslHelper.CreateRsaSelfSignCertificate( rootCa, certSubjectName, hashAlgorithm, @@ -300,7 +308,7 @@ private static async Task CreateRsaCommandHandleAsync(CreateRsaCommandOptions op ExportCertificateHelper.EnsureOutFolderExists(); var keyFileName = $"{options.CertName}-rsa-key.pem"; - var certFileName = $"{options.CertName}-rsa-key.pem"; + var certFileName = $"{options.CertName}-rsa-cert.pem"; List tasks = []; @@ -330,6 +338,6 @@ private static async Task CreateRsaCommandHandleAsync(CreateRsaCommandOptions op tasks.Add(ExportCertificateHelper.ExportPfxAsync(cert, pfxFilePath, options.PfxPassword)); } - await Task.WhenAll(tasks); + await Task.WhenAll(tasks).ConfigureAwait(false); } } diff --git a/src/CertGen/Helpers/ExportCertificateHelper.cs b/src/CertGen/Helpers/ExportCertificateHelper.cs index e8a3fef..920a385 100644 --- a/src/CertGen/Helpers/ExportCertificateHelper.cs +++ b/src/CertGen/Helpers/ExportCertificateHelper.cs @@ -1,12 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography.X509Certificates; namespace Sisa.Security.Helpers; public static class ExportCertificateHelper { - private static readonly string OutFolder = "gen"; + private static string OutFolder { get; } = "gen"; - public static async Task ExportPfxAsync(X509Certificate2 certificate, string filePath, string password) + public static async Task ExportPfxAsync([NotNull] X509Certificate2 certificate, string filePath, string password) { byte[] pfxBytes = certificate.Export(X509ContentType.Pfx, password); @@ -15,28 +17,28 @@ public static async Task ExportPfxAsync(X509Certificate2 certificate, string fil Console.WriteLine("Certificate exported to {0}.", filePath); } - public static async Task ExportCertificatePemAsync(X509Certificate2 certificate, string filePath) + public static async Task ExportCertificatePemAsync([NotNull] X509Certificate2 certificate, string filePath) { await File.WriteAllTextAsync(filePath, certificate.ExportCertificatePem()); Console.WriteLine("Certificate exported to {0}.", filePath); } - public static async Task ExportRsaPrivateKeyPemAsync(X509Certificate2 certificate, string filePath) + public static async Task ExportRsaPrivateKeyPemAsync([NotNull] X509Certificate2 certificate, string filePath) { await File.WriteAllTextAsync(filePath, certificate.GetRSAPrivateKey()!.ExportRSAPrivateKeyPem()); Console.WriteLine("RSA private key exported to {0}.", filePath); } - public static async Task ExportRsaPublicKeyPemAsync(X509Certificate2 certificate, string filePath) + public static async Task ExportRsaPublicKeyPemAsync([NotNull] X509Certificate2 certificate, string filePath) { await File.WriteAllTextAsync(filePath, certificate.GetRSAPublicKey()!.ExportRSAPublicKeyPem()); Console.WriteLine("RSA public key exported to {0}.", filePath); } - public static async Task ExportEcdsaPrivateKeyPemAsync(X509Certificate2 certificate, string filePath) + public static async Task ExportEcdsaPrivateKeyPemAsync([NotNull] X509Certificate2 certificate, string filePath) { await File.WriteAllTextAsync(filePath, certificate.GetECDsaPrivateKey()!.ExportECPrivateKeyPem()); @@ -66,7 +68,7 @@ public static void EnsureOutFolderExists() } } - public static string BuildExportFilePath(string fileName) + public static string BuildExportFilePath([NotNull] string fileName) { string currentDirectory = Environment.CurrentDirectory; @@ -76,7 +78,7 @@ public static string BuildExportFilePath(string fileName) while (File.Exists(filePath)) { - filePath = Path.Combine(currentDirectory, OutFolder, fileName.Replace(".", $"-{i}.")); + filePath = Path.Combine(currentDirectory, OutFolder, fileName.Replace(".", $"-{i}.", StringComparison.OrdinalIgnoreCase)); i++; } diff --git a/src/CertGen/Helpers/GenerateSslHelper.cs b/src/CertGen/Helpers/GenerateSslHelper.cs index 47003df..96a0049 100644 --- a/src/CertGen/Helpers/GenerateSslHelper.cs +++ b/src/CertGen/Helpers/GenerateSslHelper.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; @@ -13,8 +14,10 @@ public static class GenerateSslHelper return cert; } - catch + catch(CryptographicException ex) { + Console.WriteLine(ex.Message); + return null; } } @@ -35,7 +38,7 @@ public static X509Certificate2 CreateRsaRootCACertificate(string subjectName) return CreateRootCACertificate(certificateRequest); } - public static X509Certificate2 CreateEcdsaSelfSignCertificate(X509Certificate2 issuerCertificate, string subjectName, HashAlgorithmName hashAlgorithm, NamedCurve namedCurve, string[] dnsNames) + public static X509Certificate2 CreateEcdsaSelfSignCertificate(X509Certificate2 issuerCertificate, string subjectName, HashAlgorithmName hashAlgorithm, NamedCurve namedCurve, [NotNull] IReadOnlyCollection dnsNames) { var curve = namedCurve switch { @@ -47,17 +50,17 @@ public static X509Certificate2 CreateEcdsaSelfSignCertificate(X509Certificate2 i using ECDsa key = ECDsa.Create(curve); CertificateRequest certificateRequest = new(subjectName, key, hashAlgorithm); - var signedCert = CreateSelfSignCertificate(issuerCertificate, certificateRequest, dnsNames); + using var signedCert = CreateSelfSignCertificate(issuerCertificate, certificateRequest, dnsNames); return signedCert.CopyWithPrivateKey(key); } - public static X509Certificate2 CreateRsaSelfSignCertificate(X509Certificate2 issuerCertificate, string subjectName, HashAlgorithmName hashAlgorithm, int keySize, string[] dnsNames) + public static X509Certificate2 CreateRsaSelfSignCertificate(X509Certificate2 issuerCertificate, string subjectName, HashAlgorithmName hashAlgorithm, int keySize, [NotNull] IReadOnlyCollection dnsNames) { using RSA key = RSA.Create(keySize); CertificateRequest certificateRequest = new(subjectName, key, hashAlgorithm, RSASignaturePadding.Pkcs1); - var signedCert = CreateSelfSignCertificate(issuerCertificate, certificateRequest, dnsNames); + using var signedCert = CreateSelfSignCertificate(issuerCertificate, certificateRequest, dnsNames); return signedCert.CopyWithPrivateKey(key); } @@ -95,7 +98,7 @@ private static X509Certificate2 CreateRootCACertificate(CertificateRequest certi return certificate; } - private static X509Certificate2 CreateSelfSignCertificate(X509Certificate2 issuerCertificate, CertificateRequest certificateRequest, string[] dnsNames) + private static X509Certificate2 CreateSelfSignCertificate(X509Certificate2 issuerCertificate, CertificateRequest certificateRequest, IReadOnlyCollection dnsNames) { DateTimeOffset currentDate = DateTimeOffset.UtcNow; diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 3928580..804d7b5 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,8 +4,12 @@ net8.0 enable enable + All + latest + true true - $(NoWarn);NU5104 + true + $(NoWarn);NU5104,CA2007,CA1303 true true $(MSBuildThisFileDirectory)artifacts