Improve error handling and stuck process issues
This commit is contained in:
@@ -392,38 +392,56 @@ public static class CertificateConfigurationHelper
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if CA itself is expired
|
||||
if (caPair.Certificate.NotAfter < DateTime.UtcNow)
|
||||
try
|
||||
{
|
||||
Log.WriteErrorMessage(LOGTAG, "CAExpired", null, "Cannot renew certificate: CA certificate has expired.");
|
||||
result.RenewalFailedReason = "CA certificate has expired.";
|
||||
return result;
|
||||
// Check if CA itself is expired
|
||||
if (caPair.Certificate.NotAfter < DateTime.UtcNow)
|
||||
{
|
||||
Log.WriteErrorMessage(LOGTAG, "CAExpired", null, "Cannot renew certificate: CA certificate has expired.");
|
||||
result.RenewalFailedReason = "CA certificate has expired.";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parse hostnames
|
||||
var hostnameList = CertificateRenewalChecker.ParseHostnames(hostnames);
|
||||
Log.WriteInformationMessage(LOGTAG, "GeneratingCertificate", $"Generating new server certificate with hostnames: {string.Join(", ", hostnameList)}");
|
||||
|
||||
var newServerPair = CertificateGenerator.GenerateServerCertificate(caPair.Certificate, caPair.PrivateKey, hostnameList);
|
||||
|
||||
try
|
||||
{
|
||||
// Generate new password for PFX
|
||||
var pfxPassword = CertificateStorageHelper.GeneratePfxPassword();
|
||||
|
||||
// Create PFX bundle
|
||||
var pfxBytes = CertificateGenerator.CreatePfxBundle(newServerPair, caPair, pfxPassword);
|
||||
var pfxBase64 = Convert.ToBase64String(pfxBytes);
|
||||
|
||||
Log.WriteInformationMessage(LOGTAG, "CertificateRenewed", $"Server certificate renewed successfully. New certificate expires: {newServerPair.Certificate.NotAfter:yyyy-MM-dd}");
|
||||
|
||||
result.Renewed = true;
|
||||
result.RenewedCertificate = new ServerCertificateData
|
||||
{
|
||||
ServerCertificate = pfxBase64,
|
||||
Password = pfxPassword,
|
||||
Autogenerated = true
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Dispose server certificate key material promptly
|
||||
newServerPair.PrivateKey.Dispose();
|
||||
newServerPair.Certificate.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Parse hostnames
|
||||
var hostnameList = CertificateRenewalChecker.ParseHostnames(hostnames);
|
||||
Log.WriteInformationMessage(LOGTAG, "GeneratingCertificate", $"Generating new server certificate with hostnames: {string.Join(", ", hostnameList)}");
|
||||
|
||||
var newServerPair = CertificateGenerator.GenerateServerCertificate(caPair.Certificate, caPair.PrivateKey, hostnameList);
|
||||
|
||||
// Generate new password for PFX
|
||||
var pfxPassword = CertificateStorageHelper.GeneratePfxPassword();
|
||||
|
||||
// Create PFX bundle
|
||||
var pfxBytes = CertificateGenerator.CreatePfxBundle(newServerPair, caPair, pfxPassword);
|
||||
var pfxBase64 = Convert.ToBase64String(pfxBytes);
|
||||
|
||||
Log.WriteInformationMessage(LOGTAG, "CertificateRenewed", $"Server certificate renewed successfully. New certificate expires: {newServerPair.Certificate.NotAfter:yyyy-MM-dd}");
|
||||
|
||||
result.Renewed = true;
|
||||
result.RenewedCertificate = new ServerCertificateData
|
||||
finally
|
||||
{
|
||||
ServerCertificate = pfxBase64,
|
||||
Password = pfxPassword,
|
||||
Autogenerated = true
|
||||
};
|
||||
|
||||
return result;
|
||||
// Dispose CA certificate key material promptly
|
||||
caPair.PrivateKey.Dispose();
|
||||
caPair.Certificate.Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -521,67 +539,85 @@ public static class CertificateConfigurationHelper
|
||||
Log.WriteInformationMessage(LOGTAG, "GeneratingCA", "Generating CA certificate...");
|
||||
var caPair = CertificateGenerator.GenerateCACertificate();
|
||||
|
||||
// Generate server certificate
|
||||
Log.WriteInformationMessage(LOGTAG, "GeneratingServerCert", "Generating server certificate...");
|
||||
var serverPair = CertificateGenerator.GenerateServerCertificate(caPair.Certificate, caPair.PrivateKey, hostnameList);
|
||||
|
||||
// Generate separate passwords for CA key encryption and server PFX
|
||||
var caPassword = CertificateStorageHelper.GeneratePfxPassword();
|
||||
var pfxPassword = CertificateStorageHelper.GeneratePfxPassword();
|
||||
|
||||
// Install CA in trust store if not skipped
|
||||
if (!skipTrustInstallation)
|
||||
try
|
||||
{
|
||||
Log.WriteInformationMessage(LOGTAG, "InstallingCA", "Installing CA certificate in system trust store...");
|
||||
var installResult = InstallCATrust(caPair.Certificate, storeLocation, linuxCertDirectory, macOSKeychainPath);
|
||||
result.TrustInstallationStatus = installResult.Status;
|
||||
// Generate server certificate
|
||||
Log.WriteInformationMessage(LOGTAG, "GeneratingServerCert", "Generating server certificate...");
|
||||
var serverPair = CertificateGenerator.GenerateServerCertificate(caPair.Certificate, caPair.PrivateKey, hostnameList);
|
||||
|
||||
switch (installResult.Status)
|
||||
try
|
||||
{
|
||||
case CATrustInstallationStatus.Success:
|
||||
Log.WriteInformationMessage(LOGTAG, "CAInstalled", "CA certificate installed successfully.");
|
||||
break;
|
||||
case CATrustInstallationStatus.AlreadyInstalled:
|
||||
Log.WriteInformationMessage(LOGTAG, "CAAlreadyInstalled", "CA certificate was already installed.");
|
||||
break;
|
||||
case CATrustInstallationStatus.NotSupported:
|
||||
Log.WriteWarningMessage(LOGTAG, "NoTrustInstaller", null, "No trust installer available for this platform.");
|
||||
break;
|
||||
case CATrustInstallationStatus.RequiresElevation:
|
||||
Log.WriteWarningMessage(LOGTAG, "CARequiresElevation", null, "Administrator/root privileges required to install CA certificate.");
|
||||
break;
|
||||
case CATrustInstallationStatus.Failed:
|
||||
Log.WriteWarningMessage(LOGTAG, "CAInstallFailed", null, "Failed to install CA certificate.");
|
||||
break;
|
||||
// Generate separate passwords for CA key encryption and server PFX
|
||||
var caPassword = CertificateStorageHelper.GeneratePfxPassword();
|
||||
var pfxPassword = CertificateStorageHelper.GeneratePfxPassword();
|
||||
|
||||
// Install CA in trust store if not skipped
|
||||
if (!skipTrustInstallation)
|
||||
{
|
||||
Log.WriteInformationMessage(LOGTAG, "InstallingCA", "Installing CA certificate in system trust store...");
|
||||
var installResult = InstallCATrust(caPair.Certificate, storeLocation, linuxCertDirectory, macOSKeychainPath);
|
||||
result.TrustInstallationStatus = installResult.Status;
|
||||
|
||||
switch (installResult.Status)
|
||||
{
|
||||
case CATrustInstallationStatus.Success:
|
||||
Log.WriteInformationMessage(LOGTAG, "CAInstalled", "CA certificate installed successfully.");
|
||||
break;
|
||||
case CATrustInstallationStatus.AlreadyInstalled:
|
||||
Log.WriteInformationMessage(LOGTAG, "CAAlreadyInstalled", "CA certificate was already installed.");
|
||||
break;
|
||||
case CATrustInstallationStatus.NotSupported:
|
||||
Log.WriteWarningMessage(LOGTAG, "NoTrustInstaller", null, "No trust installer available for this platform.");
|
||||
break;
|
||||
case CATrustInstallationStatus.RequiresElevation:
|
||||
Log.WriteWarningMessage(LOGTAG, "CARequiresElevation", null, "Administrator/root privileges required to install CA certificate.");
|
||||
break;
|
||||
case CATrustInstallationStatus.Failed:
|
||||
Log.WriteWarningMessage(LOGTAG, "CAInstallFailed", null, "Failed to install CA certificate.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.WriteInformationMessage(LOGTAG, "SkippingCATrust", "Skipping CA trust installation.");
|
||||
}
|
||||
|
||||
// Serialize certificates
|
||||
Log.WriteInformationMessage(LOGTAG, "StoringCertificates", "Serializing certificates...");
|
||||
var (caCertBase64, caKeyEncrypted) = CertificateStorageHelper.SerializeCACertificatePair(caPair, caPassword);
|
||||
var pfxBytes = CertificateGenerator.CreatePfxBundle(serverPair, caPair, pfxPassword);
|
||||
var pfxBase64 = Convert.ToBase64String(pfxBytes);
|
||||
|
||||
Log.WriteInformationMessage(LOGTAG, "CertificatesGenerated", "HTTPS certificates generated successfully.");
|
||||
|
||||
result.Success = true;
|
||||
result.CACertificate = new CACertificateData
|
||||
{
|
||||
CACertificate = caCertBase64,
|
||||
CAKey = caKeyEncrypted,
|
||||
CAPassword = caPassword
|
||||
};
|
||||
result.ServerCertificate = new ServerCertificateData
|
||||
{
|
||||
ServerCertificate = pfxBase64,
|
||||
Password = pfxPassword,
|
||||
Autogenerated = true
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Dispose server certificate key material promptly
|
||||
serverPair.PrivateKey.Dispose();
|
||||
serverPair.Certificate.Dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
Log.WriteInformationMessage(LOGTAG, "SkippingCATrust", "Skipping CA trust installation.");
|
||||
// Dispose CA certificate key material promptly
|
||||
caPair.PrivateKey.Dispose();
|
||||
caPair.Certificate.Dispose();
|
||||
}
|
||||
|
||||
// Serialize certificates
|
||||
Log.WriteInformationMessage(LOGTAG, "StoringCertificates", "Serializing certificates...");
|
||||
var (caCertBase64, caKeyEncrypted) = CertificateStorageHelper.SerializeCACertificatePair(caPair, caPassword);
|
||||
var pfxBytes = CertificateGenerator.CreatePfxBundle(serverPair, caPair, pfxPassword);
|
||||
var pfxBase64 = Convert.ToBase64String(pfxBytes);
|
||||
|
||||
Log.WriteInformationMessage(LOGTAG, "CertificatesGenerated", "HTTPS certificates generated successfully.");
|
||||
|
||||
result.Success = true;
|
||||
result.CACertificate = new CACertificateData
|
||||
{
|
||||
CACertificate = caCertBase64,
|
||||
CAKey = caKeyEncrypted,
|
||||
CAPassword = caPassword
|
||||
};
|
||||
result.ServerCertificate = new ServerCertificateData
|
||||
{
|
||||
ServerCertificate = pfxBase64,
|
||||
Password = pfxPassword,
|
||||
Autogenerated = true
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,9 +222,24 @@ public class LinuxCATrustInstaller : ICATrustInstaller
|
||||
if (process == null)
|
||||
return (-1, string.Empty, $"Failed to start process: {fileName}");
|
||||
|
||||
// Read stdout/stderr to completion before waiting for exit to avoid
|
||||
// deadlocks when the process output fills the OS pipe buffer.
|
||||
var outputTask = process.StandardOutput.ReadToEndAsync();
|
||||
var errorTask = process.StandardError.ReadToEndAsync();
|
||||
|
||||
if (!Task.WaitAll([outputTask, errorTask], PROCESS_TIMEOUT))
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore kill errors
|
||||
}
|
||||
return (-1, string.Empty, $"Process timed out after {PROCESS_TIMEOUT}");
|
||||
}
|
||||
|
||||
process.WaitForExit(PROCESS_TIMEOUT);
|
||||
if (!process.HasExited)
|
||||
{
|
||||
@@ -239,9 +254,6 @@ public class LinuxCATrustInstaller : ICATrustInstaller
|
||||
return (-1, string.Empty, $"Process timed out after {PROCESS_TIMEOUT}");
|
||||
}
|
||||
|
||||
var output = outputTask.Result;
|
||||
var error = errorTask.Result;
|
||||
|
||||
return (process.ExitCode, output, error);
|
||||
return (process.ExitCode, outputTask.Result, errorTask.Result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,8 +142,10 @@ public class MacOSCATrustInstaller : ICATrustInstaller
|
||||
|
||||
try
|
||||
{
|
||||
// Find and delete certificate
|
||||
var result = RunSecurityCommand("delete-certificate", "-c", caCertificate.Subject, KeychainPath);
|
||||
// Delete certificate by SHA-1 hash to avoid accidentally removing
|
||||
// other certificates that share the same subject name.
|
||||
var hash = caCertificate.GetCertHashString();
|
||||
var result = RunSecurityCommand("delete-certificate", "-Z", hash, KeychainPath);
|
||||
|
||||
if (result.ExitCode == 0)
|
||||
return TrustUninstallationResult.Success;
|
||||
@@ -181,9 +183,24 @@ public class MacOSCATrustInstaller : ICATrustInstaller
|
||||
if (process == null)
|
||||
return (-1, string.Empty, "Failed to start security process");
|
||||
|
||||
// Read stdout/stderr to completion before waiting for exit to avoid
|
||||
// deadlocks when the process output fills the OS pipe buffer.
|
||||
var outputTask = process.StandardOutput.ReadToEndAsync();
|
||||
var errorTask = process.StandardError.ReadToEndAsync();
|
||||
|
||||
if (!Task.WaitAll([outputTask, errorTask], PROCESS_TIMEOUT))
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore kill errors
|
||||
}
|
||||
return (-1, string.Empty, $"Security process timed out after {PROCESS_TIMEOUT}");
|
||||
}
|
||||
|
||||
process.WaitForExit(PROCESS_TIMEOUT);
|
||||
if (!process.HasExited)
|
||||
{
|
||||
@@ -198,9 +215,6 @@ public class MacOSCATrustInstaller : ICATrustInstaller
|
||||
return (-1, string.Empty, $"Security process timed out after {PROCESS_TIMEOUT}");
|
||||
}
|
||||
|
||||
var output = outputTask.Result;
|
||||
var error = errorTask.Result;
|
||||
|
||||
return (process.ExitCode, output, error);
|
||||
return (process.ExitCode, outputTask.Result, errorTask.Result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user