Добавьте файлы проекта.
This commit is contained in:
parent
9b62db45fa
commit
010f86fffc
|
|
@ -0,0 +1,3 @@
|
|||
<Solution>
|
||||
<Project Path="Russain SSL Installer/Russain SSL Installer.csproj" />
|
||||
</Solution>
|
||||
|
|
@ -0,0 +1,552 @@
|
|||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
|
||||
namespace CertificateChecker;
|
||||
|
||||
/// <summary>
|
||||
/// Основной класс для проверки сертификатов
|
||||
/// </summary>
|
||||
public class CertificateChecker
|
||||
{
|
||||
/// <summary>
|
||||
/// Загружает сертификаты из Resources.resx
|
||||
/// </summary>
|
||||
public List<X509Certificate2> LoadCertificatesFromResources()
|
||||
{
|
||||
var certificates = new List<X509Certificate2>();
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
Console.WriteLine(" 📂 Поиск сертификатов в ресурсах...");
|
||||
Console.WriteLine($" Сборка: {assembly.GetName().Name}");
|
||||
|
||||
try
|
||||
{
|
||||
// Получаем ВСЕ ресурсы из сборки
|
||||
var resourceNames = assembly.GetManifestResourceNames();
|
||||
|
||||
Console.WriteLine($" Найдено ресурсов: {resourceNames.Length}");
|
||||
|
||||
if (resourceNames.Length == 0)
|
||||
{
|
||||
Console.WriteLine(" ⚠️ Ресурсы не найдены. Проверьте файл Resources.resx");
|
||||
return certificates;
|
||||
}
|
||||
|
||||
// Ищем ресурсы, которые содержат "Resources" в названии
|
||||
var resourcesFile = resourceNames.FirstOrDefault(r => r.Contains("Resources"));
|
||||
|
||||
if (string.IsNullOrEmpty(resourcesFile))
|
||||
{
|
||||
Console.WriteLine(" ⚠️ Файл Resources.resx не найден");
|
||||
Console.WriteLine($" Доступные ресурсы: {string.Join(", ", resourceNames)}");
|
||||
return certificates;
|
||||
}
|
||||
|
||||
Console.WriteLine($" Найден файл ресурсов: {resourcesFile}");
|
||||
|
||||
// Создаем ResourceManager для найденного файла
|
||||
var resourceManagerName = Path.GetFileNameWithoutExtension(resourcesFile);
|
||||
Console.WriteLine($" Имя ResourceManager: {resourceManagerName}");
|
||||
|
||||
var resourceManager = new ResourceManager(resourceManagerName, assembly);
|
||||
|
||||
// Получаем все ресурсы
|
||||
var resourceSet = resourceManager.GetResourceSet(System.Globalization.CultureInfo.InvariantCulture, true, true);
|
||||
|
||||
if (resourceSet == null)
|
||||
{
|
||||
Console.WriteLine(" ❌ Не удалось загрузить ResourceSet");
|
||||
return certificates;
|
||||
}
|
||||
|
||||
// Собираем все ключи ресурсов
|
||||
var keys = new List<string>();
|
||||
foreach (DictionaryEntry entry in resourceSet)
|
||||
{
|
||||
keys.Add(entry.Key.ToString());
|
||||
}
|
||||
|
||||
Console.WriteLine($" Найдено ключей в ресурсах: {keys.Count}");
|
||||
Console.WriteLine($" Ключи: {string.Join(", ", keys)}");
|
||||
|
||||
// Фильтруем ключи, которые содержат "cert", "ca", "root", "sub"
|
||||
var certKeys = keys
|
||||
.Where(key => key.ToLower().Contains("cert") ||
|
||||
key.ToLower().Contains("ca") ||
|
||||
key.ToLower().Contains("root") ||
|
||||
key.ToLower().Contains("sub"))
|
||||
.ToList();
|
||||
|
||||
if (!certKeys.Any())
|
||||
{
|
||||
Console.WriteLine(" ⚠️ Сертификаты в ресурсах не найдены");
|
||||
Console.WriteLine($" Ищите ключи содержащие: cert, ca, root, sub");
|
||||
return certificates;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n Найдено сертификатов: {certKeys.Count}");
|
||||
|
||||
foreach (var key in certKeys)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"\n 📄 Загрузка: {key}");
|
||||
|
||||
// Получаем значение ресурса
|
||||
var value = resourceManager.GetObject(key);
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
Console.WriteLine($" ❌ Ресурс {key} пустой");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Проверяем тип ресурса
|
||||
if (value is byte[] bytes)
|
||||
{
|
||||
if (bytes.Length == 0)
|
||||
{
|
||||
Console.WriteLine($" ❌ Ресурс {key} пустой (0 байт)");
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine($" Размер: {bytes.Length} байт");
|
||||
|
||||
try
|
||||
{
|
||||
// Пробуем загрузить как сертификат
|
||||
var certificate = new X509Certificate2(bytes);
|
||||
|
||||
// Устанавливаем понятное имя
|
||||
if (string.IsNullOrEmpty(certificate.FriendlyName))
|
||||
{
|
||||
certificate.FriendlyName = key.Replace("_", " ").Trim();
|
||||
}
|
||||
|
||||
certificates.Add(certificate);
|
||||
|
||||
Console.WriteLine($" ✅ Загружен сертификат: {certificate.FriendlyName}");
|
||||
Console.WriteLine($" Субъект: {certificate.Subject}");
|
||||
Console.WriteLine($" Отпечаток: {certificate.Thumbprint}");
|
||||
Console.WriteLine($" Действителен до: {certificate.NotAfter:dd.MM.yyyy}");
|
||||
Console.WriteLine($" Закрытый ключ: {(certificate.HasPrivateKey ? "✅ Есть" : "❌ Нет")}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($" ⚠️ Не удалось загрузить {key} как сертификат: {ex.Message}");
|
||||
Console.WriteLine($" Возможно это не сертификат или требуется пароль");
|
||||
|
||||
// Пробуем с паролем для PFX
|
||||
if (key.ToLower().Contains("pfx") || key.ToLower().Contains("p12") || key.ToLower().Contains("gost"))
|
||||
{
|
||||
Console.Write(" 🔑 Введите пароль для {0}: ", key);
|
||||
var password = Console.ReadLine();
|
||||
|
||||
if (!string.IsNullOrEmpty(password))
|
||||
{
|
||||
try
|
||||
{
|
||||
var certificate = new X509Certificate2(bytes, password);
|
||||
certificate.FriendlyName = key.Replace("_", " ").Trim();
|
||||
certificates.Add(certificate);
|
||||
|
||||
Console.WriteLine($" ✅ Загружен с паролем: {certificate.FriendlyName}");
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
Console.WriteLine($" ❌ Ошибка загрузки с паролем: {ex2.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($" ⚠️ Ресурс {key} имеет тип {value.GetType().Name}, ожидался byte[]");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($" ❌ Ошибка обработки {key}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($" ❌ Общая ошибка: {ex.Message}");
|
||||
Console.WriteLine($" {ex.StackTrace}");
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n ИТОГО загружено сертификатов: {certificates.Count}");
|
||||
return certificates;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, установлен ли сертификат на ПК
|
||||
/// </summary>
|
||||
public async Task<CertificateCheckResult> CheckCertificateInstalledAsync(X509Certificate2 certificate)
|
||||
{
|
||||
var result = new CertificateCheckResult
|
||||
{
|
||||
FriendlyName = certificate.FriendlyName,
|
||||
Subject = certificate.Subject,
|
||||
Issuer = certificate.Issuer,
|
||||
SerialNumber = certificate.SerialNumber,
|
||||
NotBefore = certificate.NotBefore,
|
||||
NotAfter = certificate.NotAfter,
|
||||
Thumbprint = certificate.Thumbprint,
|
||||
IsInstalled = false,
|
||||
FoundInStores = new List<string>(),
|
||||
HasPrivateKey = certificate.HasPrivateKey
|
||||
};
|
||||
|
||||
// Проверяем в разных хранилищах
|
||||
var stores = new[]
|
||||
{
|
||||
(StoreName.My, StoreLocation.CurrentUser, "Личные (CurrentUser)"),
|
||||
(StoreName.My, StoreLocation.LocalMachine, "Личные (LocalMachine)"),
|
||||
(StoreName.Root, StoreLocation.CurrentUser, "Доверенные корневые (CurrentUser)"),
|
||||
(StoreName.Root, StoreLocation.LocalMachine, "Доверенные корневые (LocalMachine)"),
|
||||
(StoreName.TrustedPeople, StoreLocation.CurrentUser, "Доверенные лица (CurrentUser)"),
|
||||
(StoreName.TrustedPeople, StoreLocation.LocalMachine, "Доверенные лица (LocalMachine)"),
|
||||
(StoreName.CertificateAuthority, StoreLocation.CurrentUser, "Промежуточные (CurrentUser)"),
|
||||
(StoreName.CertificateAuthority, StoreLocation.LocalMachine, "Промежуточные (LocalMachine)"),
|
||||
(StoreName.AddressBook, StoreLocation.CurrentUser, "Адресная книга (CurrentUser)"),
|
||||
(StoreName.AddressBook, StoreLocation.LocalMachine, "Адресная книга (LocalMachine)")
|
||||
};
|
||||
|
||||
foreach (var (storeName, storeLocation, displayName) in stores)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var store = new X509Store(storeName, storeLocation);
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
|
||||
// Ищем по отпечатку
|
||||
var foundCerts = store.Certificates.Find(X509FindType.FindByThumbprint, certificate.Thumbprint, false);
|
||||
|
||||
if (foundCerts.Count > 0)
|
||||
{
|
||||
result.IsInstalled = true;
|
||||
result.StoreName = storeName;
|
||||
result.StoreLocation = storeLocation;
|
||||
result.FoundCertificate = foundCerts[0];
|
||||
result.FoundInStores.Add(displayName);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Некоторые хранилища могут быть недоступны
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка цепочки сертификатов
|
||||
result.ChainValidationResult = await ValidateCertificateChainAsync(certificate);
|
||||
|
||||
// Проверка срока действия
|
||||
result.IsValid = DateTime.Now >= certificate.NotBefore &&
|
||||
DateTime.Now <= certificate.NotAfter;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверка цепочки сертификатов
|
||||
/// </summary>
|
||||
private async Task<ChainValidationResult> ValidateCertificateChainAsync(X509Certificate2 certificate)
|
||||
{
|
||||
var result = new ChainValidationResult();
|
||||
|
||||
try
|
||||
{
|
||||
using var chain = new X509Chain();
|
||||
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
|
||||
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
|
||||
|
||||
var isValid = chain.Build(certificate);
|
||||
|
||||
result.IsValid = isValid;
|
||||
result.Status = chain.ChainStatus
|
||||
.Select(s => new ChainStatusInfo
|
||||
{
|
||||
Status = s.Status,
|
||||
StatusInformation = s.StatusInformation
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.ErrorMessage = ex.Message;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет сертификат по отпечатку
|
||||
/// </summary>
|
||||
public bool IsCertificateInstalled(string thumbprint)
|
||||
{
|
||||
var stores = new[]
|
||||
{
|
||||
(StoreName.My, StoreLocation.CurrentUser),
|
||||
(StoreName.My, StoreLocation.LocalMachine),
|
||||
(StoreName.Root, StoreLocation.CurrentUser),
|
||||
(StoreName.Root, StoreLocation.LocalMachine)
|
||||
};
|
||||
|
||||
foreach (var (storeName, storeLocation) in stores)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var store = new X509Store(storeName, storeLocation);
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
|
||||
if (certificates.Count > 0)
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Пропускаем недоступные хранилища
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Устанавливает сертификат в указанное хранилище
|
||||
/// </summary>
|
||||
public async Task<bool> InstallCertificateAsync(X509Certificate2 certificate, StoreName storeName, StoreLocation storeLocation, string friendlyName = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"\n 📥 Установка сертификата {friendlyName} в {storeLocation}\\{storeName}...");
|
||||
|
||||
using var store = new X509Store(storeName, storeLocation);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
|
||||
// Проверяем, не установлен ли уже
|
||||
var existing = store.Certificates.Find(X509FindType.FindByThumbprint, certificate.Thumbprint, false);
|
||||
if (existing.Count > 0)
|
||||
{
|
||||
Console.WriteLine($" ⚠️ Сертификат уже установлен в {storeLocation}\\{storeName}");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Устанавливаем понятное имя
|
||||
if (!string.IsNullOrEmpty(friendlyName))
|
||||
{
|
||||
certificate.FriendlyName = friendlyName;
|
||||
}
|
||||
|
||||
store.Add(certificate);
|
||||
store.Close();
|
||||
|
||||
Console.WriteLine($" ✅ Сертификат успешно установлен в {storeLocation}\\{storeName}");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($" ❌ Ошибка установки: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Устанавливает сертификат с автоматическим выбором хранилища
|
||||
/// </summary>
|
||||
public async Task<InstallResult> InstallCertificateWithAutoStoreAsync(X509Certificate2 certificate)
|
||||
{
|
||||
var result = new InstallResult
|
||||
{
|
||||
Certificate = certificate,
|
||||
Success = false,
|
||||
Messages = new List<string>()
|
||||
};
|
||||
|
||||
// Определяем куда устанавливать в зависимости от типа сертификата
|
||||
var subject = certificate.Subject.ToLower();
|
||||
var isRoot = subject.Contains("root") || subject.Contains("ca") && !subject.Contains("sub");
|
||||
var isCA = subject.Contains("ca");
|
||||
var isPersonal = subject.Contains("client") || subject.Contains("user") || subject.Contains("person");
|
||||
|
||||
List<(StoreName store, StoreLocation location, string description)> storesToInstall;
|
||||
|
||||
if (isRoot)
|
||||
{
|
||||
// Корневой сертификат
|
||||
storesToInstall = new List<(StoreName, StoreLocation, string)>
|
||||
{
|
||||
(StoreName.Root, StoreLocation.LocalMachine, "Доверенные корневые (LocalMachine)"),
|
||||
(StoreName.Root, StoreLocation.CurrentUser, "Доверенные корневые (CurrentUser)")
|
||||
};
|
||||
result.InstallType = "Корневой сертификат";
|
||||
}
|
||||
else if (isCA && !isRoot)
|
||||
{
|
||||
// Промежуточный сертификат
|
||||
storesToInstall = new List<(StoreName, StoreLocation, string)>
|
||||
{
|
||||
(StoreName.CertificateAuthority, StoreLocation.LocalMachine, "Промежуточные (LocalMachine)"),
|
||||
(StoreName.CertificateAuthority, StoreLocation.CurrentUser, "Промежуточные (CurrentUser)")
|
||||
};
|
||||
result.InstallType = "Промежуточный сертификат";
|
||||
}
|
||||
else if (isPersonal)
|
||||
{
|
||||
// Личный сертификат
|
||||
storesToInstall = new List<(StoreName, StoreLocation, string)>
|
||||
{
|
||||
(StoreName.My, StoreLocation.CurrentUser, "Личные (CurrentUser)"),
|
||||
(StoreName.My, StoreLocation.LocalMachine, "Личные (LocalMachine)")
|
||||
};
|
||||
result.InstallType = "Личный сертификат";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Неопределенный тип - устанавливаем в личные
|
||||
storesToInstall = new List<(StoreName, StoreLocation, string)>
|
||||
{
|
||||
(StoreName.My, StoreLocation.CurrentUser, "Личные (CurrentUser)")
|
||||
};
|
||||
result.InstallType = "Неопределенный тип (личный)";
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n 📌 Тип сертификата: {result.InstallType}");
|
||||
Console.WriteLine($" 📌 Субъект: {certificate.Subject}");
|
||||
|
||||
// Устанавливаем в каждое хранилище
|
||||
foreach (var (store, location, description) in storesToInstall)
|
||||
{
|
||||
try
|
||||
{
|
||||
var success = await InstallCertificateAsync(certificate, store, location, certificate.FriendlyName);
|
||||
if (success)
|
||||
{
|
||||
result.Success = true;
|
||||
result.InstalledStores.Add($"{location}\\{store}");
|
||||
result.Messages.Add($"✅ Установлен в {description}");
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Messages.Add($"❌ Не удалось установить в {description}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Messages.Add($"❌ Ошибка при установке в {description}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Если не удалось установить никуда, пробуем в CurrentUser
|
||||
if (!result.Success)
|
||||
{
|
||||
Console.WriteLine($" ⚠️ Пробуем установить в CurrentUser...");
|
||||
try
|
||||
{
|
||||
var success = await InstallCertificateAsync(certificate, StoreName.My, StoreLocation.CurrentUser, certificate.FriendlyName);
|
||||
if (success)
|
||||
{
|
||||
result.Success = true;
|
||||
result.InstalledStores.Add($"CurrentUser\\My");
|
||||
result.Messages.Add($"✅ Установлен в Личные (CurrentUser)");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Messages.Add($"❌ Ошибка при установке в CurrentUser: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет сертификат из указанного хранилища
|
||||
/// </summary>
|
||||
public async Task<bool> DeleteCertificateAsync(string thumbprint, StoreName storeName, StoreLocation storeLocation)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"\n 🗑️ Удаление сертификата из {storeLocation}\\{storeName}...");
|
||||
|
||||
using var store = new X509Store(storeName, storeLocation);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
|
||||
var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
|
||||
if (certificates.Count == 0)
|
||||
{
|
||||
Console.WriteLine($" ⚠️ Сертификат не найден в {storeLocation}\\{storeName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
store.Remove(certificates[0]);
|
||||
store.Close();
|
||||
|
||||
Console.WriteLine($" ✅ Сертификат удален из {storeLocation}\\{storeName}");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($" ❌ Ошибка удаления: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Результат проверки сертификата
|
||||
/// </summary>
|
||||
public class CertificateCheckResult
|
||||
{
|
||||
public string FriendlyName { get; set; } = string.Empty;
|
||||
public string Subject { get; set; } = string.Empty;
|
||||
public string Issuer { get; set; } = string.Empty;
|
||||
public string SerialNumber { get; set; } = string.Empty;
|
||||
public DateTime NotBefore { get; set; }
|
||||
public DateTime NotAfter { get; set; }
|
||||
public string Thumbprint { get; set; } = string.Empty;
|
||||
public bool IsInstalled { get; set; }
|
||||
public bool IsValid { get; set; }
|
||||
public bool HasPrivateKey { get; set; }
|
||||
public StoreName StoreName { get; set; }
|
||||
public StoreLocation StoreLocation { get; set; }
|
||||
public X509Certificate2? FoundCertificate { get; set; }
|
||||
public List<string> FoundInStores { get; set; } = new();
|
||||
public ChainValidationResult ChainValidationResult { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Результат установки сертификата
|
||||
/// </summary>
|
||||
public class InstallResult
|
||||
{
|
||||
public X509Certificate2 Certificate { get; set; } = null!;
|
||||
public bool Success { get; set; }
|
||||
public string InstallType { get; set; } = string.Empty;
|
||||
public List<string> InstalledStores { get; set; } = new();
|
||||
public List<string> Messages { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Результат проверки цепочки сертификатов
|
||||
/// </summary>
|
||||
public class ChainValidationResult
|
||||
{
|
||||
public bool IsValid { get; set; }
|
||||
public string? ErrorMessage { get; set; }
|
||||
public List<ChainStatusInfo> Status { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Информация о статусе цепочки
|
||||
/// </summary>
|
||||
public class ChainStatusInfo
|
||||
{
|
||||
public X509ChainStatusFlags Status { get; set; }
|
||||
public string? StatusInformation { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,807 @@
|
|||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
|
||||
namespace CertificateChecker;
|
||||
|
||||
class Program
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
Console.OutputEncoding = Encoding.UTF8;
|
||||
Console.Title = "Certificate Manager";
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.Clear();
|
||||
Console.WriteLine("╔══════════════════════════════════════════════════════════════════════════╗");
|
||||
Console.WriteLine("║ УПРАВЛЕНИЕ СЕРТИФИКАТАМИ ║");
|
||||
Console.WriteLine("╚══════════════════════════════════════════════════════════════════════════╝\n");
|
||||
|
||||
Console.WriteLine(" 📋 ГЛАВНОЕ МЕНЮ:");
|
||||
Console.WriteLine(new string('─', 80));
|
||||
Console.WriteLine(" 1. 🔍 Проверить сертификаты");
|
||||
Console.WriteLine(" 2. 📥 Установить сертификаты");
|
||||
Console.WriteLine(" 3. 🗑️ Удалить сертификаты");
|
||||
Console.WriteLine(" 4. 📊 Показать все установленные сертификаты");
|
||||
Console.WriteLine(" 5. 🚪 Выход");
|
||||
Console.WriteLine(new string('─', 80));
|
||||
Console.Write("\n Ваш выбор: ");
|
||||
|
||||
var choice = Console.ReadLine();
|
||||
|
||||
switch (choice)
|
||||
{
|
||||
case "1":
|
||||
await CheckCertificatesAsync();
|
||||
break;
|
||||
case "2":
|
||||
await InstallCertificatesAsync();
|
||||
break;
|
||||
case "3":
|
||||
await DeleteCertificatesAsync();
|
||||
break;
|
||||
case "4":
|
||||
await ShowAllInstalledCertificatesAsync();
|
||||
break;
|
||||
case "5":
|
||||
Console.WriteLine("\n До свидания! 👋");
|
||||
return;
|
||||
default:
|
||||
Console.WriteLine("\n ❌ Неверный выбор. Нажмите любую клавишу...");
|
||||
Console.ReadKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверка сертификатов
|
||||
/// </summary>
|
||||
static async Task CheckCertificatesAsync()
|
||||
{
|
||||
Console.Clear();
|
||||
Console.WriteLine("╔══════════════════════════════════════════════════════════════════════════╗");
|
||||
Console.WriteLine("║ ПРОВЕРКА СЕРТИФИКАТОВ ║");
|
||||
Console.WriteLine("╚══════════════════════════════════════════════════════════════════════════╝\n");
|
||||
|
||||
var checker = new CertificateChecker();
|
||||
|
||||
Console.WriteLine("📂 Загрузка сертификатов из ресурсов...\n");
|
||||
var certificates = checker.LoadCertificatesFromResources();
|
||||
|
||||
if (!certificates.Any())
|
||||
{
|
||||
Console.WriteLine("\n❌ Сертификаты не найдены в ресурсах.");
|
||||
Console.WriteLine("\n💡 Проверьте:");
|
||||
Console.WriteLine(" 1. Что файл Resources.resx существует");
|
||||
Console.WriteLine(" 2. Что сертификаты добавлены как ресурсы (тип: byte[])");
|
||||
Console.WriteLine(" 3. Что ключи ресурсов содержат: cert, ca, root или sub");
|
||||
Console.WriteLine("\nНажмите любую клавишу для возврата в меню...");
|
||||
Console.ReadKey();
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n✅ Загружено сертификатов: {certificates.Count}\n");
|
||||
Console.WriteLine(new string('═', 100));
|
||||
|
||||
// Проверяем каждый сертификат
|
||||
var results = new List<CertificateCheckResult>();
|
||||
|
||||
for (int i = 0; i < certificates.Count; i++)
|
||||
{
|
||||
var cert = certificates[i];
|
||||
Console.WriteLine($"\n🔍 Проверка сертификата #{i + 1}: {cert.FriendlyName}");
|
||||
Console.WriteLine(new string('─', 80));
|
||||
|
||||
var result = await checker.CheckCertificateInstalledAsync(cert);
|
||||
results.Add(result);
|
||||
|
||||
Console.WriteLine($" 📋 Имя: {result.FriendlyName}");
|
||||
Console.WriteLine($" 🔑 Отпечаток: {result.Thumbprint}");
|
||||
Console.WriteLine($" 📅 Действителен: {result.NotBefore:dd.MM.yyyy} → {result.NotAfter:dd.MM.yyyy}");
|
||||
Console.WriteLine($" 📊 Статус: {(result.IsInstalled ? "✅ УСТАНОВЛЕН" : "❌ НЕ УСТАНОВЛЕН")}");
|
||||
|
||||
if (result.IsInstalled)
|
||||
{
|
||||
Console.WriteLine($" 📁 Хранилище: {result.StoreLocation}\\{result.StoreName}");
|
||||
Console.WriteLine($" 🔒 Закрытый ключ: {(result.HasPrivateKey ? "✅ Есть" : "❌ Нет")}");
|
||||
Console.WriteLine($" ✅ Цепочка: {(result.ChainValidationResult.IsValid ? "Действительна" : "Проблемы")}");
|
||||
|
||||
if (result.FoundInStores.Any())
|
||||
{
|
||||
Console.WriteLine($" 📂 Найден в хранилищах:");
|
||||
foreach (var store in result.FoundInStores)
|
||||
{
|
||||
Console.WriteLine($" • {store}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($" ⚠️ Сертификат не найден в системных хранилищах");
|
||||
if (result.HasPrivateKey)
|
||||
{
|
||||
Console.WriteLine($" 💡 Сертификат содержит закрытый ключ и может быть импортирован");
|
||||
}
|
||||
}
|
||||
|
||||
var daysUntilExpiry = (result.NotAfter - DateTime.Now).Days;
|
||||
if (daysUntilExpiry < 0)
|
||||
{
|
||||
Console.WriteLine($" ⚠️ СЕРТИФИКАТ ПРОСРОЧЕН на {Math.Abs(daysUntilExpiry)} дней!");
|
||||
}
|
||||
else if (daysUntilExpiry < 30)
|
||||
{
|
||||
Console.WriteLine($" ⚠️ Сертификат истекает через {daysUntilExpiry} дней");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($" ℹ️ Сертификат действителен еще {daysUntilExpiry} дней");
|
||||
}
|
||||
}
|
||||
|
||||
// Выводим итоговую таблицу
|
||||
Console.WriteLine("\n" + new string('═', 100));
|
||||
Console.WriteLine("\n📊 ИТОГОВАЯ ТАБЛИЦА ПРОВЕРЕННЫХ СЕРТИФИКАТОВ");
|
||||
Console.WriteLine(new string('═', 100));
|
||||
|
||||
PrintCertificateTable(results);
|
||||
|
||||
// Статистика
|
||||
var installedCount = results.Count(r => r.IsInstalled);
|
||||
var validCount = results.Count(r => r.IsValid);
|
||||
var expiredCount = results.Count(r => r.NotAfter < DateTime.Now);
|
||||
|
||||
Console.WriteLine("\n" + new string('═', 100));
|
||||
Console.WriteLine("\n📈 СТАТИСТИКА:");
|
||||
Console.WriteLine($" • Всего сертификатов: {results.Count}");
|
||||
Console.WriteLine($" • Установлено: {installedCount} ({(results.Count > 0 ? installedCount * 100 / results.Count : 0)}%)");
|
||||
Console.WriteLine($" • Не установлено: {results.Count - installedCount} ({(results.Count > 0 ? (results.Count - installedCount) * 100 / results.Count : 0)}%)");
|
||||
Console.WriteLine($" • Действительные: {validCount} ({(results.Count > 0 ? validCount * 100 / results.Count : 0)}%)");
|
||||
Console.WriteLine($" • Просроченные: {expiredCount} ({(results.Count > 0 ? expiredCount * 100 / results.Count : 0)}%)");
|
||||
|
||||
Console.WriteLine("\nНажмите любую клавишу для возврата в меню...");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установка сертификатов
|
||||
/// </summary>
|
||||
static async Task InstallCertificatesAsync()
|
||||
{
|
||||
Console.Clear();
|
||||
Console.WriteLine("╔══════════════════════════════════════════════════════════════════════════╗");
|
||||
Console.WriteLine("║ УСТАНОВКА СЕРТИФИКАТОВ ║");
|
||||
Console.WriteLine("╚══════════════════════════════════════════════════════════════════════════╝\n");
|
||||
|
||||
var checker = new CertificateChecker();
|
||||
|
||||
Console.WriteLine("📂 Загрузка сертификатов из ресурсов...\n");
|
||||
var certificates = checker.LoadCertificatesFromResources();
|
||||
|
||||
if (!certificates.Any())
|
||||
{
|
||||
Console.WriteLine("\n❌ Сертификаты не найдены в ресурсах.");
|
||||
Console.WriteLine("\nНажмите любую клавишу для возврата в меню...");
|
||||
Console.ReadKey();
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n✅ Загружено сертификатов: {certificates.Count}\n");
|
||||
|
||||
// Проверяем, какие сертификаты уже установлены
|
||||
var results = new List<CertificateCheckResult>();
|
||||
foreach (var cert in certificates)
|
||||
{
|
||||
var result = await checker.CheckCertificateInstalledAsync(cert);
|
||||
results.Add(result);
|
||||
}
|
||||
|
||||
var missing = results.Where(r => !r.IsInstalled).ToList();
|
||||
|
||||
if (!missing.Any())
|
||||
{
|
||||
Console.WriteLine("✅ Все сертификаты уже установлены!");
|
||||
Console.WriteLine("\nНажмите любую клавишу для возврата в меню...");
|
||||
Console.ReadKey();
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"🔧 Обнаружено {missing.Count} сертификатов для установки:\n");
|
||||
|
||||
for (int i = 0; i < missing.Count; i++)
|
||||
{
|
||||
var cert = missing[i];
|
||||
Console.WriteLine($" {i + 1}. {cert.FriendlyName}");
|
||||
Console.WriteLine($" Субъект: {cert.Subject}");
|
||||
Console.WriteLine($" Действителен до: {cert.NotAfter:dd.MM.yyyy}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
Console.WriteLine(" Выберите действие:");
|
||||
Console.WriteLine(" [1] Установить все");
|
||||
Console.WriteLine(" [2] Выборочная установка");
|
||||
Console.WriteLine(" [3] Отмена");
|
||||
Console.Write("\n Ваш выбор: ");
|
||||
|
||||
var choice = Console.ReadLine();
|
||||
|
||||
if (choice == "1")
|
||||
{
|
||||
await InstallAllCertificatesAsync(checker, certificates, results);
|
||||
}
|
||||
else if (choice == "2")
|
||||
{
|
||||
await InstallSelectedCertificatesAsync(checker, certificates, results);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\n Установка отменена.");
|
||||
}
|
||||
|
||||
Console.WriteLine("\nНажмите любую клавишу для возврата в меню...");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удаление сертификатов
|
||||
/// </summary>
|
||||
static async Task DeleteCertificatesAsync()
|
||||
{
|
||||
Console.Clear();
|
||||
Console.WriteLine("╔══════════════════════════════════════════════════════════════════════════╗");
|
||||
Console.WriteLine("║ УДАЛЕНИЕ СЕРТИФИКАТОВ ║");
|
||||
Console.WriteLine("╚══════════════════════════════════════════════════════════════════════════╝\n");
|
||||
|
||||
var checker = new CertificateChecker();
|
||||
|
||||
Console.WriteLine("📂 Загрузка сертификатов из ресурсов...\n");
|
||||
var certificates = checker.LoadCertificatesFromResources();
|
||||
|
||||
if (!certificates.Any())
|
||||
{
|
||||
Console.WriteLine("\n❌ Сертификаты не найдены в ресурсах.");
|
||||
Console.WriteLine("\nНажмите любую клавишу для возврата в меню...");
|
||||
Console.ReadKey();
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n✅ Загружено сертификатов: {certificates.Count}\n");
|
||||
|
||||
// Проверяем, какие сертификаты установлены
|
||||
var results = new List<CertificateCheckResult>();
|
||||
foreach (var cert in certificates)
|
||||
{
|
||||
var result = await checker.CheckCertificateInstalledAsync(cert);
|
||||
results.Add(result);
|
||||
}
|
||||
|
||||
var installed = results.Where(r => r.IsInstalled).ToList();
|
||||
|
||||
if (!installed.Any())
|
||||
{
|
||||
Console.WriteLine("⚠️ Нет установленных сертификатов для удаления!");
|
||||
Console.WriteLine("\nНажмите любую клавишу для возврата в меню...");
|
||||
Console.ReadKey();
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"🔧 Обнаружено {installed.Count} установленных сертификатов:\n");
|
||||
|
||||
for (int i = 0; i < installed.Count; i++)
|
||||
{
|
||||
var cert = installed[i];
|
||||
Console.WriteLine($" {i + 1}. {cert.FriendlyName}");
|
||||
Console.WriteLine($" Субъект: {cert.Subject}");
|
||||
Console.WriteLine($" Хранилище: {cert.StoreLocation}\\{cert.StoreName}");
|
||||
Console.WriteLine($" Действителен до: {cert.NotAfter:dd.MM.yyyy}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
Console.WriteLine(" ⚠️ ВНИМАНИЕ! Удаление сертификатов может нарушить работу приложений!");
|
||||
Console.WriteLine(" Выберите действие:");
|
||||
Console.WriteLine(" [1] Удалить все");
|
||||
Console.WriteLine(" [2] Выборочное удаление");
|
||||
Console.WriteLine(" [3] Отмена");
|
||||
Console.Write("\n Ваш выбор: ");
|
||||
|
||||
var choice = Console.ReadLine();
|
||||
|
||||
if (choice == "1")
|
||||
{
|
||||
await DeleteAllCertificatesAsync(checker, certificates, results);
|
||||
}
|
||||
else if (choice == "2")
|
||||
{
|
||||
await DeleteSelectedCertificatesAsync(checker, certificates, results);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\n Удаление отменено.");
|
||||
}
|
||||
|
||||
Console.WriteLine("\nНажмите любую клавишу для возврата в меню...");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Показать все установленные сертификаты
|
||||
/// </summary>
|
||||
static async Task ShowAllInstalledCertificatesAsync()
|
||||
{
|
||||
Console.Clear();
|
||||
Console.WriteLine("╔══════════════════════════════════════════════════════════════════════════╗");
|
||||
Console.WriteLine("║ ВСЕ УСТАНОВЛЕННЫЕ СЕРТИФИКАТЫ ║");
|
||||
Console.WriteLine("╚══════════════════════════════════════════════════════════════════════════╝\n");
|
||||
|
||||
var checker = new CertificateChecker();
|
||||
var allCerts = new List<CertificateCheckResult>();
|
||||
|
||||
// Проверяем все хранилища
|
||||
var stores = new[]
|
||||
{
|
||||
(StoreName.My, StoreLocation.CurrentUser, "Личные (CurrentUser)"),
|
||||
(StoreName.My, StoreLocation.LocalMachine, "Личные (LocalMachine)"),
|
||||
(StoreName.Root, StoreLocation.CurrentUser, "Доверенные корневые (CurrentUser)"),
|
||||
(StoreName.Root, StoreLocation.LocalMachine, "Доверенные корневые (LocalMachine)"),
|
||||
(StoreName.TrustedPeople, StoreLocation.CurrentUser, "Доверенные лица (CurrentUser)"),
|
||||
(StoreName.TrustedPeople, StoreLocation.LocalMachine, "Доверенные лица (LocalMachine)"),
|
||||
(StoreName.CertificateAuthority, StoreLocation.CurrentUser, "Промежуточные (CurrentUser)"),
|
||||
(StoreName.CertificateAuthority, StoreLocation.LocalMachine, "Промежуточные (LocalMachine)")
|
||||
};
|
||||
|
||||
Console.WriteLine("📂 Сканирование хранилищ...\n");
|
||||
|
||||
foreach (var (storeName, storeLocation, displayName) in stores)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var store = new X509Store(storeName, storeLocation);
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
|
||||
var certs = store.Certificates;
|
||||
if (certs.Count > 0)
|
||||
{
|
||||
Console.WriteLine($"📁 {displayName} ({certs.Count} сертификатов):");
|
||||
foreach (var cert in certs)
|
||||
{
|
||||
var result = new CertificateCheckResult
|
||||
{
|
||||
FriendlyName = string.IsNullOrEmpty(cert.FriendlyName) ? cert.Subject : cert.FriendlyName,
|
||||
Subject = cert.Subject,
|
||||
Issuer = cert.Issuer,
|
||||
Thumbprint = cert.Thumbprint,
|
||||
NotAfter = cert.NotAfter,
|
||||
NotBefore = cert.NotBefore,
|
||||
IsInstalled = true,
|
||||
StoreName = storeName,
|
||||
StoreLocation = storeLocation,
|
||||
HasPrivateKey = cert.HasPrivateKey,
|
||||
FoundCertificate = cert
|
||||
};
|
||||
allCerts.Add(result);
|
||||
}
|
||||
|
||||
foreach (var cert in certs)
|
||||
{
|
||||
var name = string.IsNullOrEmpty(cert.FriendlyName) ? "Без имени" : cert.FriendlyName;
|
||||
Console.WriteLine($" • {name}");
|
||||
Console.WriteLine($" Отпечаток: {cert.Thumbprint.Substring(0, 16)}...");
|
||||
Console.WriteLine($" Действителен до: {cert.NotAfter:dd.MM.yyyy}");
|
||||
Console.WriteLine($" Закрытый ключ: {(cert.HasPrivateKey ? "✅ Есть" : "❌ Нет")}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Пропускаем недоступные хранилища
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n📊 Всего найдено: {allCerts.Count} сертификатов");
|
||||
|
||||
// Статистика
|
||||
var withPrivateKey = allCerts.Count(c => c.HasPrivateKey);
|
||||
var expired = allCerts.Count(c => c.NotAfter < DateTime.Now);
|
||||
|
||||
Console.WriteLine($" • С закрытым ключом: {withPrivateKey}");
|
||||
Console.WriteLine($" • Просроченных: {expired}");
|
||||
|
||||
Console.WriteLine("\nНажмите любую клавишу для возврата в меню...");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Установка всех сертификатов
|
||||
/// </summary>
|
||||
static async Task InstallAllCertificatesAsync(CertificateChecker checker, List<X509Certificate2> certificates, List<CertificateCheckResult> results)
|
||||
{
|
||||
Console.WriteLine("\n🔧 НАЧАЛО УСТАНОВКИ ВСЕХ СЕРТИФИКАТОВ...");
|
||||
Console.WriteLine(new string('─', 80));
|
||||
|
||||
var successCount = 0;
|
||||
var failCount = 0;
|
||||
|
||||
for (int i = 0; i < certificates.Count; i++)
|
||||
{
|
||||
var cert = certificates[i];
|
||||
var result = results[i];
|
||||
|
||||
if (!result.IsInstalled)
|
||||
{
|
||||
Console.WriteLine($"\n 📥 Установка сертификата: {cert.FriendlyName}");
|
||||
Console.WriteLine($" Субъект: {cert.Subject}");
|
||||
|
||||
var installResult = await checker.InstallCertificateWithAutoStoreAsync(cert);
|
||||
|
||||
if (installResult.Success)
|
||||
{
|
||||
successCount++;
|
||||
Console.WriteLine($" ✅ Успешно установлен");
|
||||
foreach (var msg in installResult.Messages)
|
||||
{
|
||||
Console.WriteLine($" {msg}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
failCount++;
|
||||
Console.WriteLine($" ❌ Ошибка установки");
|
||||
foreach (var msg in installResult.Messages)
|
||||
{
|
||||
Console.WriteLine($" {msg}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n 📊 ИТОГО УСТАНОВЛЕНО: {successCount}, ОШИБОК: {failCount}");
|
||||
|
||||
if (failCount == 0)
|
||||
{
|
||||
Console.WriteLine("\n 🎉 Все сертификаты успешно установлены!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"\n ⚠️ {failCount} сертификатов не удалось установить.");
|
||||
Console.WriteLine(" Попробуйте запустить приложение от имени администратора.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выборочная установка сертификатов
|
||||
/// </summary>
|
||||
static async Task InstallSelectedCertificatesAsync(CertificateChecker checker, List<X509Certificate2> certificates, List<CertificateCheckResult> results)
|
||||
{
|
||||
var missing = results.Where(r => !r.IsInstalled).ToList();
|
||||
|
||||
if (!missing.Any())
|
||||
{
|
||||
Console.WriteLine("\n Нет сертификатов для установки.");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("\n🔧 ВЫБЕРИТЕ СЕРТИФИКАТЫ ДЛЯ УСТАНОВКИ:");
|
||||
Console.WriteLine(" Введите номера через запятую (например: 1,3,5) или 'all' для всех");
|
||||
Console.WriteLine(" Доступные сертификаты:");
|
||||
|
||||
for (int i = 0; i < missing.Count; i++)
|
||||
{
|
||||
Console.WriteLine($" [{i + 1}] {missing[i].FriendlyName}");
|
||||
}
|
||||
|
||||
Console.Write("\n Ваш выбор: ");
|
||||
var input = Console.ReadLine();
|
||||
|
||||
List<int> selectedIndexes = new List<int>();
|
||||
|
||||
if (input?.ToLower() == "all")
|
||||
{
|
||||
selectedIndexes = Enumerable.Range(0, missing.Count).ToList();
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(input))
|
||||
{
|
||||
var parts = input.Split(',');
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (int.TryParse(part.Trim(), out int num) && num > 0 && num <= missing.Count)
|
||||
{
|
||||
selectedIndexes.Add(num - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedIndexes.Any())
|
||||
{
|
||||
Console.WriteLine(" ❌ Не выбрано ни одного сертификата.");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n Установка {selectedIndexes.Count} сертификатов...");
|
||||
|
||||
var successCount = 0;
|
||||
var failCount = 0;
|
||||
|
||||
foreach (var index in selectedIndexes)
|
||||
{
|
||||
var result = missing[index];
|
||||
var cert = certificates.FirstOrDefault(c => c.Thumbprint == result.Thumbprint);
|
||||
|
||||
if (cert == null)
|
||||
{
|
||||
Console.WriteLine($" ❌ Сертификат {result.FriendlyName} не найден");
|
||||
failCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n 📥 Установка: {cert.FriendlyName}");
|
||||
|
||||
var installResult = await checker.InstallCertificateWithAutoStoreAsync(cert);
|
||||
|
||||
if (installResult.Success)
|
||||
{
|
||||
successCount++;
|
||||
Console.WriteLine($" ✅ Успешно установлен");
|
||||
}
|
||||
else
|
||||
{
|
||||
failCount++;
|
||||
Console.WriteLine($" ❌ Ошибка установки");
|
||||
foreach (var msg in installResult.Messages)
|
||||
{
|
||||
Console.WriteLine($" {msg}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n 📊 УСТАНОВЛЕНО: {successCount}, ОШИБОК: {failCount}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удаление всех сертификатов
|
||||
/// </summary>
|
||||
static async Task DeleteAllCertificatesAsync(CertificateChecker checker, List<X509Certificate2> certificates, List<CertificateCheckResult> results)
|
||||
{
|
||||
Console.WriteLine("\n🗑️ НАЧАЛО УДАЛЕНИЯ ВСЕХ СЕРТИФИКАТОВ...");
|
||||
Console.WriteLine(new string('─', 80));
|
||||
|
||||
var successCount = 0;
|
||||
var failCount = 0;
|
||||
|
||||
for (int i = 0; i < certificates.Count; i++)
|
||||
{
|
||||
var cert = certificates[i];
|
||||
var result = results[i];
|
||||
|
||||
if (result.IsInstalled)
|
||||
{
|
||||
Console.WriteLine($"\n 🗑️ Удаление сертификата: {cert.FriendlyName}");
|
||||
Console.WriteLine($" Хранилище: {result.StoreLocation}\\{result.StoreName}");
|
||||
|
||||
try
|
||||
{
|
||||
using var store = new X509Store(result.StoreName, result.StoreLocation);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
|
||||
var foundCerts = store.Certificates.Find(X509FindType.FindByThumbprint, cert.Thumbprint, false);
|
||||
if (foundCerts.Count > 0)
|
||||
{
|
||||
store.Remove(foundCerts[0]);
|
||||
successCount++;
|
||||
Console.WriteLine($" ✅ Успешно удален из {result.StoreLocation}\\{result.StoreName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($" ⚠️ Сертификат не найден в хранилище");
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
failCount++;
|
||||
Console.WriteLine($" ❌ Ошибка удаления: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n 📊 ИТОГО УДАЛЕНО: {successCount}, ОШИБОК: {failCount}");
|
||||
|
||||
if (failCount == 0)
|
||||
{
|
||||
Console.WriteLine("\n 🎉 Все сертификаты успешно удалены!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"\n ⚠️ {failCount} сертификатов не удалось удалить.");
|
||||
Console.WriteLine(" Попробуйте запустить приложение от имени администратора.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выборочное удаление сертификатов
|
||||
/// </summary>
|
||||
static async Task DeleteSelectedCertificatesAsync(CertificateChecker checker, List<X509Certificate2> certificates, List<CertificateCheckResult> results)
|
||||
{
|
||||
var installed = results.Where(r => r.IsInstalled).ToList();
|
||||
|
||||
if (!installed.Any())
|
||||
{
|
||||
Console.WriteLine("\n Нет сертификатов для удаления.");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("\n🗑️ ВЫБЕРИТЕ СЕРТИФИКАТЫ ДЛЯ УДАЛЕНИЯ:");
|
||||
Console.WriteLine(" Введите номера через запятую (например: 1,3,5) или 'all' для всех");
|
||||
Console.WriteLine(" Установленные сертификаты:");
|
||||
|
||||
for (int i = 0; i < installed.Count; i++)
|
||||
{
|
||||
Console.WriteLine($" [{i + 1}] {installed[i].FriendlyName}");
|
||||
Console.WriteLine($" Хранилище: {installed[i].StoreLocation}\\{installed[i].StoreName}");
|
||||
}
|
||||
|
||||
Console.Write("\n Ваш выбор: ");
|
||||
var input = Console.ReadLine();
|
||||
|
||||
List<int> selectedIndexes = new List<int>();
|
||||
|
||||
if (input?.ToLower() == "all")
|
||||
{
|
||||
selectedIndexes = Enumerable.Range(0, installed.Count).ToList();
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(input))
|
||||
{
|
||||
var parts = input.Split(',');
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (int.TryParse(part.Trim(), out int num) && num > 0 && num <= installed.Count)
|
||||
{
|
||||
selectedIndexes.Add(num - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedIndexes.Any())
|
||||
{
|
||||
Console.WriteLine(" ❌ Не выбрано ни одного сертификата.");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n Удаление {selectedIndexes.Count} сертификатов...");
|
||||
|
||||
var successCount = 0;
|
||||
var failCount = 0;
|
||||
|
||||
foreach (var index in selectedIndexes)
|
||||
{
|
||||
var result = installed[index];
|
||||
var cert = certificates.FirstOrDefault(c => c.Thumbprint == result.Thumbprint);
|
||||
|
||||
if (cert == null)
|
||||
{
|
||||
Console.WriteLine($" ❌ Сертификат {result.FriendlyName} не найден");
|
||||
failCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n 🗑️ Удаление: {cert.FriendlyName}");
|
||||
Console.WriteLine($" Хранилище: {result.StoreLocation}\\{result.StoreName}");
|
||||
|
||||
try
|
||||
{
|
||||
using var store = new X509Store(result.StoreName, result.StoreLocation);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
|
||||
var foundCerts = store.Certificates.Find(X509FindType.FindByThumbprint, cert.Thumbprint, false);
|
||||
if (foundCerts.Count > 0)
|
||||
{
|
||||
store.Remove(foundCerts[0]);
|
||||
successCount++;
|
||||
Console.WriteLine($" ✅ Успешно удален");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($" ⚠️ Сертификат не найден в хранилище");
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
failCount++;
|
||||
Console.WriteLine($" ❌ Ошибка удаления: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n 📊 УДАЛЕНО: {successCount}, ОШИБОК: {failCount}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выводит таблицу всех проверенных сертификатов
|
||||
/// </summary>
|
||||
static void PrintCertificateTable(List<CertificateCheckResult> results)
|
||||
{
|
||||
var colWidth = new Dictionary<string, int>
|
||||
{
|
||||
["#"] = 4,
|
||||
["Имя"] = 30,
|
||||
["Отпечаток"] = 12,
|
||||
["Действителен до"] = 14,
|
||||
["Статус"] = 12,
|
||||
["Ключ"] = 10,
|
||||
["Цепочка"] = 10,
|
||||
["Хранилище"] = 20
|
||||
};
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
colWidth["Имя"] = Math.Max(colWidth["Имя"], result.FriendlyName.Length + 2);
|
||||
colWidth["Отпечаток"] = Math.Max(colWidth["Отпечаток"], Math.Min(result.Thumbprint.Length, 14) + 2);
|
||||
colWidth["Хранилище"] = Math.Max(colWidth["Хранилище"],
|
||||
result.IsInstalled ? $"{result.StoreLocation}\\{result.StoreName}".Length + 2 : 12);
|
||||
}
|
||||
|
||||
var header = $"│ {"#".PadRight(colWidth["#"])} │ " +
|
||||
$"{"Имя".PadRight(colWidth["Имя"])} │ " +
|
||||
$"{"Отпечаток".PadRight(colWidth["Отпечаток"])} │ " +
|
||||
$"{"Действителен до".PadRight(colWidth["Действителен до"])} │ " +
|
||||
$"{"Статус".PadRight(colWidth["Статус"])} │ " +
|
||||
$"{"Ключ".PadRight(colWidth["Ключ"])} │ " +
|
||||
$"{"Цепочка".PadRight(colWidth["Цепочка"])} │ " +
|
||||
$"{"Хранилище".PadRight(colWidth["Хранилище"])} │";
|
||||
|
||||
var separator = $"├{"─".PadRight(colWidth["#"] + 1, '─')}┼{"─".PadRight(colWidth["Имя"] + 1, '─')}┼{"─".PadRight(colWidth["Отпечаток"] + 1, '─')}┼{"─".PadRight(colWidth["Действителен до"] + 1, '─')}┼{"─".PadRight(colWidth["Статус"] + 1, '─')}┼{"─".PadRight(colWidth["Ключ"] + 1, '─')}┼{"─".PadRight(colWidth["Цепочка"] + 1, '─')}┼{"─".PadRight(colWidth["Хранилище"] + 1, '─')}┤";
|
||||
var topBorder = $"┌{"─".PadRight(colWidth["#"] + 1, '─')}┬{"─".PadRight(colWidth["Имя"] + 1, '─')}┬{"─".PadRight(colWidth["Отпечаток"] + 1, '─')}┬{"─".PadRight(colWidth["Действителен до"] + 1, '─')}┬{"─".PadRight(colWidth["Статус"] + 1, '─')}┬{"─".PadRight(colWidth["Ключ"] + 1, '─')}┬{"─".PadRight(colWidth["Цепочка"] + 1, '─')}┬{"─".PadRight(colWidth["Хранилище"] + 1, '─')}┐";
|
||||
var bottomBorder = $"└{"─".PadRight(colWidth["#"] + 1, '─')}┴{"─".PadRight(colWidth["Имя"] + 1, '─')}┴{"─".PadRight(colWidth["Отпечаток"] + 1, '─')}┴{"─".PadRight(colWidth["Действителен до"] + 1, '─')}┴{"─".PadRight(colWidth["Статус"] + 1, '─')}┴{"─".PadRight(colWidth["Ключ"] + 1, '─')}┴{"─".PadRight(colWidth["Цепочка"] + 1, '─')}┴{"─".PadRight(colWidth["Хранилище"] + 1, '─')}┘";
|
||||
|
||||
Console.WriteLine(topBorder);
|
||||
Console.WriteLine(header);
|
||||
Console.WriteLine(separator);
|
||||
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
var result = results[i];
|
||||
|
||||
var status = result.IsInstalled ? "✅ Уст." : "❌ Нет";
|
||||
var hasKey = result.HasPrivateKey ? "✅ Есть" : "❌ Нет";
|
||||
var chain = result.ChainValidationResult.IsValid ? "✅ Ок" : "⚠️ Пробл.";
|
||||
var thumbprint = result.Thumbprint.Length > 14 ?
|
||||
result.Thumbprint.Substring(0, 12) + "..." :
|
||||
result.Thumbprint;
|
||||
var store = result.IsInstalled ?
|
||||
$"{result.StoreLocation}\\{result.StoreName}" :
|
||||
"Не установлен";
|
||||
|
||||
if (!result.IsInstalled)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
}
|
||||
else if (result.NotAfter < DateTime.Now)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
}
|
||||
else if (!result.ChainValidationResult.IsValid)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
}
|
||||
|
||||
var row = $"│ {(i + 1).ToString().PadRight(colWidth["#"])} │ " +
|
||||
$"{result.FriendlyName.PadRight(colWidth["Имя"])} │ " +
|
||||
$"{thumbprint.PadRight(colWidth["Отпечаток"])} │ " +
|
||||
$"{result.NotAfter:dd.MM.yyyy}".PadRight(colWidth["Действителен до"]) + " │ " +
|
||||
$"{status.PadRight(colWidth["Статус"])} │ " +
|
||||
$"{hasKey.PadRight(colWidth["Ключ"])} │ " +
|
||||
$"{chain.PadRight(colWidth["Цепочка"])} │ " +
|
||||
$"{store.PadRight(colWidth["Хранилище"])} │";
|
||||
|
||||
Console.WriteLine(row);
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
Console.WriteLine(bottomBorder);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// Этот код создан программой.
|
||||
// Исполняемая версия:4.0.30319.42000
|
||||
//
|
||||
// Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае
|
||||
// повторной генерации кода.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Russain_SSL_Installer.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Класс ресурса со строгой типизацией для поиска локализованных строк и т.д.
|
||||
/// </summary>
|
||||
// Этот класс создан автоматически классом StronglyTypedResourceBuilder
|
||||
// с помощью такого средства, как ResGen или Visual Studio.
|
||||
// Чтобы добавить или удалить член, измените файл .ResX и снова запустите ResGen
|
||||
// с параметром /str или перестройте свой проект VS.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает кэшированный экземпляр ResourceManager, использованный этим классом.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Russain_SSL_Installer.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Перезаписывает свойство CurrentUICulture текущего потока для всех
|
||||
/// обращений к ресурсу с помощью этого класса ресурса со строгой типизацией.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поиск локализованного ресурса типа System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] russian_trusted_root_ca {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("russian_trusted_root_ca", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поиск локализованного ресурса типа System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] russian_trusted_root_ca_gost_2025 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("russian_trusted_root_ca_gost_2025", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поиск локализованного ресурса типа System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] russian_trusted_sub_ca {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("russian_trusted_sub_ca", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поиск локализованного ресурса типа System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] russian_trusted_sub_ca_2024 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("russian_trusted_sub_ca_2024", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поиск локализованного ресурса типа System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] russian_trusted_sub_ca_gost_2025 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("russian_trusted_sub_ca_gost_2025", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="russian_trusted_root_ca" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\russian_trusted_root_ca.cer;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<comment>Root</comment>
|
||||
</data>
|
||||
<data name="russian_trusted_root_ca_gost_2025" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\russian_trusted_root_ca_gost_2025.cer;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<comment>Root</comment>
|
||||
</data>
|
||||
<data name="russian_trusted_sub_ca" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\russian_trusted_sub_ca.cer;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<comment>Sub</comment>
|
||||
</data>
|
||||
<data name="russian_trusted_sub_ca_2024" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\russian_trusted_sub_ca_2024.cer;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<comment>Sub</comment>
|
||||
</data>
|
||||
<data name="russian_trusted_sub_ca_gost_2025" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\russian_trusted_sub_ca_gost_2025.cer;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<comment>Sub</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFwjCCA6qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx
|
||||
PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu
|
||||
ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg
|
||||
Q0EwHhcNMjIwMzAxMjEwNDE1WhcNMzIwMjI3MjEwNDE1WjBwMQswCQYDVQQGEwJS
|
||||
VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg
|
||||
YW5kIENvbW11bmljYXRpb25zMSAwHgYDVQQDDBdSdXNzaWFuIFRydXN0ZWQgUm9v
|
||||
dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMfFOZ8pUAL3+r2n
|
||||
qqE0Zp52selXsKGFYoG0GM5bwz1bSFtCt+AZQMhkWQheI3poZAToYJu69pHLKS6Q
|
||||
XBiwBC1cvzYmUYKMYZC7jE5YhEU2bSL0mX7NaMxMDmH2/NwuOVRj8OImVa5s1F4U
|
||||
zn4Kv3PFlDBjjSjXKVY9kmjUBsXQrIHeaqmUIsPIlNWUnimXS0I0abExqkbdrXbX
|
||||
YwCOXhOO2pDUx3ckmJlCMUGacUTnylyQW2VsJIyIGA8V0xzdaeUXg0VZ6ZmNUr5Y
|
||||
Ber/EAOLPb8NYpsAhJe2mXjMB/J9HNsoFMBFJ0lLOT/+dQvjbdRZoOT8eqJpWnVD
|
||||
U+QL/qEZnz57N88OWM3rabJkRNdU/Z7x5SFIM9FrqtN8xewsiBWBI0K6XFuOBOTD
|
||||
4V08o4TzJ8+Ccq5XlCUW2L48pZNCYuBDfBh7FxkB7qDgGDiaftEkZZfApRg2E+M9
|
||||
G8wkNKTPLDc4wH0FDTijhgxR3Y4PiS1HL2Zhw7bD3CbslmEGgfnnZojNkJtcLeBH
|
||||
BLa52/dSwNU4WWLubaYSiAmA9IUMX1/RpfpxOxd4Ykmhz97oFbUaDJFipIggx5sX
|
||||
ePAlkTdWnv+RWBxlJwMQ25oEHmRguNYf4Zr/Rxr9cS93Y+mdXIZaBEE0KS2iLRqa
|
||||
OiWBki9IMQU4phqPOBAaG7A+eP8PAgMBAAGjZjBkMB0GA1UdDgQWBBTh0YHlzlpf
|
||||
BKrS6badZrHF+qwshzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qwshzAS
|
||||
BgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
|
||||
AAOCAgEAALIY1wkilt/urfEVM5vKzr6utOeDWCUczmWX/RX4ljpRdgF+5fAIS4vH
|
||||
tmXkqpSCOVeWUrJV9QvZn6L227ZwuE15cWi8DCDal3Ue90WgAJJZMfTshN4OI8cq
|
||||
W9E4EG9wglbEtMnObHlms8F3CHmrw3k6KmUkWGoa+/ENmcVl68u/cMRl1JbW2bM+
|
||||
/3A+SAg2c6iPDlehczKx2oa95QW0SkPPWGuNA/CE8CpyANIhu9XFrj3RQ3EqeRcS
|
||||
AQQod1RNuHpfETLU/A2gMmvn/w/sx7TB3W5BPs6rprOA37tutPq9u6FTZOcG1Oqj
|
||||
C/B7yTqgI7rbyvox7DEXoX7rIiEqyNNUguTk/u3SZ4VXE2kmxdmSh3TQvybfbnXV
|
||||
4JbCZVaqiZraqc7oZMnRoWrXRG3ztbnbes/9qhRGI7PqXqeKJBztxRTEVj8ONs1d
|
||||
WN5szTwaPIvhkhO3CO5ErU2rVdUr89wKpNXbBODFKRtgxUT70YpmJ46VVaqdAhOZ
|
||||
D9EUUn4YaeLaS8AjSF/h7UkjOibNc4qVDiPP+rkehFWM66PVnP1Msh93tc+taIfC
|
||||
EYVMxjh8zNbFuoc7fzvvrFILLe7ifvEIUqSVIC/AzplM/Jxw7buXFeGP1qVCBEHq
|
||||
391d/9RAfaZ12zkwFsl+IKwE/OZxW8AHa9i1p4GO0YSNuczzEm4=
|
||||
-----END CERTIFICATE-----
|
||||
Binary file not shown.
|
|
@ -0,0 +1,41 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIHQjCCBSqgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx
|
||||
PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu
|
||||
ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg
|
||||
Q0EwHhcNMjIwMzAyMTEyNTE5WhcNMjcwMzA2MTEyNTE5WjBvMQswCQYDVQQGEwJS
|
||||
VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg
|
||||
YW5kIENvbW11bmljYXRpb25zMR8wHQYDVQQDDBZSdXNzaWFuIFRydXN0ZWQgU3Vi
|
||||
IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9YPqBKOk19NFymrE
|
||||
wehzrhBEgT2atLezpduB24mQ7CiOa/HVpFCDRZzdxqlh8drku408/tTmWzlNH/br
|
||||
HuQhZ/miWKOf35lpKzjyBd6TPM23uAfJvEOQ2/dnKGGJbsUo1/udKSvxQwVHpVv3
|
||||
S80OlluKfhWPDEXQpgyFqIzPoxIQTLZ0deirZwMVHarZ5u8HqHetRuAtmO2ZDGQn
|
||||
vVOJYAjls+Hiueq7Lj7Oce7CQsTwVZeP+XQx28PAaEZ3y6sQEt6rL06ddpSdoTMp
|
||||
BnCqTbxW+eWMyjkIn6t9GBtUV45yB1EkHNnj2Ex4GwCiN9T84QQjKSr+8f0psGrZ
|
||||
vPbCbQAwNFJjisLixnjlGPLKa5vOmNwIh/LAyUW5DjpkCx004LPDuqPpFsKXNKpa
|
||||
L2Dm6uc0x4Jo5m+gUTVORB6hOSzWnWDj2GWfomLzzyjG81DRGFBpco/O93zecsIN
|
||||
3SL2Ysjpq1zdoS01CMYxie//9zWvYwzI25/OZigtnpCIrcd2j1Y6dMUFQAzAtHE+
|
||||
qsXflSL8HIS+IJEFIQobLlYhHkoE3avgNx5jlu+OLYe0dF0Ykx1PGNjbwqvTX37R
|
||||
Cn32NMjlotW2QcGEZhDKj+3urZizp5xdTPZitA+aEjZM/Ni71VOdiOP0igbw6asZ
|
||||
2fxdozZ1TnSSYNYvNATwthNmZysCAwEAAaOCAeUwggHhMBIGA1UdEwEB/wQIMAYB
|
||||
Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTR4XENCy2BTm6KSo9MI7NM
|
||||
XqtpCzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qwshzCBxwYIKwYBBQUH
|
||||
AQEEgbowgbcwOwYIKwYBBQUHMAKGL2h0dHA6Ly9yb3N0ZWxlY29tLnJ1L2NkcC9y
|
||||
b290Y2Ffc3NsX3JzYTIwMjIuY3J0MDsGCCsGAQUFBzAChi9odHRwOi8vY29tcGFu
|
||||
eS5ydC5ydS9jZHAvcm9vdGNhX3NzbF9yc2EyMDIyLmNydDA7BggrBgEFBQcwAoYv
|
||||
aHR0cDovL3JlZXN0ci1wa2kucnUvY2RwL3Jvb3RjYV9zc2xfcnNhMjAyMi5jcnQw
|
||||
gbAGA1UdHwSBqDCBpTA1oDOgMYYvaHR0cDovL3Jvc3RlbGVjb20ucnUvY2RwL3Jv
|
||||
b3RjYV9zc2xfcnNhMjAyMi5jcmwwNaAzoDGGL2h0dHA6Ly9jb21wYW55LnJ0LnJ1
|
||||
L2NkcC9yb290Y2Ffc3NsX3JzYTIwMjIuY3JsMDWgM6Axhi9odHRwOi8vcmVlc3Ry
|
||||
LXBraS5ydS9jZHAvcm9vdGNhX3NzbF9yc2EyMDIyLmNybDANBgkqhkiG9w0BAQsF
|
||||
AAOCAgEARBVzZls79AdiSCpar15dA5Hr/rrT4WbrOfzlpI+xrLeRPrUG6eUWIW4v
|
||||
Sui1yx3iqGLCjPcKb+HOTwoRMbI6ytP/ndp3TlYua2advYBEhSvjs+4vDZNwXr/D
|
||||
anbwIWdurZmViQRBDFebpkvnIvru/RpWud/5r624Wp8voZMRtj/cm6aI9LtvBfT9
|
||||
cfzhOaexI/99c14dyiuk1+6QhdwKaCRTc1mdfNQmnfWNRbfWhWBlK3h4GGE9JK33
|
||||
Gk8ZS8DMrkdAh0xby4xAQ/mSWAfWrBmfzlOqGyoB1U47WTOeqNbWkkoAP2ys94+s
|
||||
Jg4NTkiDVtXRF6nr6fYi0bSOvOFg0IQrMXO2Y8gyg9ARdPJwKtvWX8VPADCYMiWH
|
||||
h4n8bZokIrImVKLDQKHY4jCsND2HHdJfnrdL2YJw1qFskNO4cSNmZydw0Wkgjv9k
|
||||
F+KxqrDKlB8MZu2Hclph6v/CZ0fQ9YuE8/lsHZ0Qc2HyiSMnvjgK5fDc3TD4fa8F
|
||||
E8gMNurM+kV8PT8LNIM+4Zs+LKEV8nqRWBaxkIVJGekkVKO8xDBOG/aN62AZKHOe
|
||||
GcyIdu7yNMMRihGVZCYr8rYiJoKiOzDqOkPkLOPdhtVlgnhowzHDxMHND/E2WA5p
|
||||
ZHuNM/m0TXt2wTTPL7JH2YC0gPz/BvvSzjksgzU5rLbRyUKQkgU=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIG6DCCBNCgAwIBAgICEAUwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx
|
||||
PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu
|
||||
ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg
|
||||
Q0EwHhcNMjQwNzE1MTI1MDQxWhcNMjkwNzE5MTI1MDQxWjBvMQswCQYDVQQGEwJS
|
||||
VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg
|
||||
YW5kIENvbW11bmljYXRpb25zMR8wHQYDVQQDDBZSdXNzaWFuIFRydXN0ZWQgU3Vi
|
||||
IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1j0rkZECOt1S8o7I
|
||||
JY+4YKAxuEa5xaHKHXT2EpkuC/0krqMOjUy2oPIRNgR5g8X0Jl6jamxeGLc4Q1tf
|
||||
ju6or9oSRYThIUhRsFDQNBiBBEXoBgWxTfiKB2eyT97+pz5TBtBiRCPaLGRHYLRb
|
||||
9Jz2HkJlxbtNPjtDrF5DPHym+mZ1M1z3hIQYAqJwLpsEBnsw/VxWMlxqHoeewd0h
|
||||
uJMd71KQ5vOKlz7KrIZ6EobNNa6wItuvsfj3kYCK7O78uLHGXXFxdr8Hae9lMUmC
|
||||
8F7AFwa+bO1LRlTlqW7rE3rLf+jj70N01N8T3o22v14YBaFBWQWncAVYD2JuL3tH
|
||||
252+kdNOERf1fLbLRigJAbd+hOhWYlNf963TFDgnNPliHNIW72SygVBnI2V3JwO1
|
||||
dp1hVKpK/zt8ziGdHW4gmOLTsH50YKdR4jNqUgQv4wASlKn9OpN6zHYc5G8h86fY
|
||||
BM+zxE5ikGI+I/vIqBuI0eaDU92AWN/YjFLpu8tMu9kLRSCf1vug6FIfDPWVo7iP
|
||||
ac/SI2v8jnnpaW7ph/Pz3WkzaG7ZZJsfFs+8dploWc6LOoDtbFBhMdGMxu024msC
|
||||
0PSjZb5ODXPIaO2NsA7fMiAtZcoK6anTUJh4zOP/stA9qsJGNxdrEmiPXSmBZY/N
|
||||
Y0wkZgZ6JTDhw7038bPvctkblJkCAwEAAaOCAYswggGHMB0GA1UdDgQWBBR3Pdk5
|
||||
r0K93FvKduru/c4+YSkwXzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qws
|
||||
hzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADCBmAYIKwYBBQUH
|
||||
AQEEgYswgYgwQAYIKwYBBQUHMAKGNGh0dHA6Ly9udWMtY2RwLnZvc2tob2QucnUv
|
||||
Y2RwL3Jvb3RjYV9zc2xfcnNhMjAyMi5jcnQwRAYIKwYBBQUHMAKGOGh0dHA6Ly9u
|
||||
dWMtY2RwLmRpZ2l0YWwuZ292LnJ1L2NkcC9yb290Y2Ffc3NsX3JzYTIwMjIuY3J0
|
||||
MIGFBgNVHR8EfjB8MDqgOKA2hjRodHRwOi8vbnVjLWNkcC52b3NraG9kLnJ1L2Nk
|
||||
cC9yb290Y2Ffc3NsX3JzYTIwMjIuY3JsMD6gPKA6hjhodHRwOi8vbnVjLWNkcC5k
|
||||
aWdpdGFsLmdvdi5ydS9jZHAvcm9vdGNhX3NzbF9yc2EyMDIyLmNybDANBgkqhkiG
|
||||
9w0BAQsFAAOCAgEAmsINXtQ7wwUWvIeOr80MdJS/5G4xhyZOVEmeUorThquT672y
|
||||
cCg3XCxc4fwbiZqSSbBqntQ7RtiTAKMYMvBageKoVHbzz+R4jX01tKcTx8cDePrz
|
||||
dJ73bLNUorE7RU9QsW4KyiUeRmjMDV23AUlEvuQFTwgkHXvbac1BBdPn9CrssQuF
|
||||
5EGohZKcQPFiAAc4SHbRNhlr7uAwgpc/erzI9EAcvA6BVAXcVKoeGpV01uexUgZ6
|
||||
St5RP9UmDWNA7T4yVXWJ233N0Q8bl+6AswINQ3PosPu6yQQHQjr65YS06epK+AeI
|
||||
6j+oGR4xI7EhTQhQvaobnGmX/8QQ7XDRYCP2HXYxiffnn/CfZ/BVyKLYeY1ZipjE
|
||||
nzqdQIC2+Q3WtY8jsVRQMP38WFRmtsIt5snehnPTs5bKGVIcYzj3o3Ex/K7agEz0
|
||||
zAJ0JR5ivXZOvNkT0g9x1v+S1IkU3e/nX1a+tpRquMtnHX0L2lXArNHUbaOO9EJt
|
||||
d57WaIpofV5cVhhwShOgAuBc9UMJF3/n4t4RKiPxtsK8P67gcmphMhslj7AMYrYM
|
||||
ej2NvQZY4m3ub3CPC/PrTjDONvb+8g5xrKtxBjYqC74HSB4dg9G3WimSDUuP2Su6
|
||||
G2y2TUeyJuCvCLz289VoO0vg7cNdMobE3KCqAiiNhN2VBFxHAUKmUoRcRdw=
|
||||
-----END CERTIFICATE-----
|
||||
Binary file not shown.
|
|
@ -0,0 +1,26 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<RootNamespace>Russain_SSL_Installer</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Loading…
Reference in New Issue