diff --git a/Russain SSL Installer.slnx b/Russain SSL Installer.slnx
new file mode 100644
index 0000000..e6ab6b9
--- /dev/null
+++ b/Russain SSL Installer.slnx
@@ -0,0 +1,3 @@
+
+
+
diff --git a/Russain SSL Installer/CertificateChecker.cs b/Russain SSL Installer/CertificateChecker.cs
new file mode 100644
index 0000000..4ff87e3
--- /dev/null
+++ b/Russain SSL Installer/CertificateChecker.cs
@@ -0,0 +1,552 @@
+using System.Collections;
+using System.Reflection;
+using System.Resources;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+
+namespace CertificateChecker;
+
+///
+/// Основной класс для проверки сертификатов
+///
+public class CertificateChecker
+{
+ ///
+ /// Загружает сертификаты из Resources.resx
+ ///
+ public List LoadCertificatesFromResources()
+ {
+ var certificates = new List();
+ 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();
+ 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;
+ }
+
+ ///
+ /// Проверяет, установлен ли сертификат на ПК
+ ///
+ public async Task 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(),
+ 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;
+ }
+
+ ///
+ /// Проверка цепочки сертификатов
+ ///
+ private async Task 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;
+ }
+
+ ///
+ /// Проверяет сертификат по отпечатку
+ ///
+ 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;
+ }
+
+ ///
+ /// Устанавливает сертификат в указанное хранилище
+ ///
+ public async Task 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;
+ }
+ }
+
+ ///
+ /// Устанавливает сертификат с автоматическим выбором хранилища
+ ///
+ public async Task InstallCertificateWithAutoStoreAsync(X509Certificate2 certificate)
+ {
+ var result = new InstallResult
+ {
+ Certificate = certificate,
+ Success = false,
+ Messages = new List()
+ };
+
+ // Определяем куда устанавливать в зависимости от типа сертификата
+ 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;
+ }
+
+ ///
+ /// Удаляет сертификат из указанного хранилища
+ ///
+ public async Task 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;
+ }
+ }
+}
+
+///
+/// Результат проверки сертификата
+///
+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 FoundInStores { get; set; } = new();
+ public ChainValidationResult ChainValidationResult { get; set; } = new();
+}
+
+///
+/// Результат установки сертификата
+///
+public class InstallResult
+{
+ public X509Certificate2 Certificate { get; set; } = null!;
+ public bool Success { get; set; }
+ public string InstallType { get; set; } = string.Empty;
+ public List InstalledStores { get; set; } = new();
+ public List Messages { get; set; } = new();
+}
+
+///
+/// Результат проверки цепочки сертификатов
+///
+public class ChainValidationResult
+{
+ public bool IsValid { get; set; }
+ public string? ErrorMessage { get; set; }
+ public List Status { get; set; } = new();
+}
+
+///
+/// Информация о статусе цепочки
+///
+public class ChainStatusInfo
+{
+ public X509ChainStatusFlags Status { get; set; }
+ public string? StatusInformation { get; set; }
+}
\ No newline at end of file
diff --git a/Russain SSL Installer/Program.cs b/Russain SSL Installer/Program.cs
new file mode 100644
index 0000000..6e68209
--- /dev/null
+++ b/Russain SSL Installer/Program.cs
@@ -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;
+ }
+ }
+ }
+
+ ///
+ /// Проверка сертификатов
+ ///
+ 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();
+
+ 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();
+ }
+
+ ///
+ /// Установка сертификатов
+ ///
+ 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();
+ 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();
+ }
+
+ ///
+ /// Удаление сертификатов
+ ///
+ 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();
+ 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();
+ }
+
+ ///
+ /// Показать все установленные сертификаты
+ ///
+ static async Task ShowAllInstalledCertificatesAsync()
+ {
+ Console.Clear();
+ Console.WriteLine("╔══════════════════════════════════════════════════════════════════════════╗");
+ Console.WriteLine("║ ВСЕ УСТАНОВЛЕННЫЕ СЕРТИФИКАТЫ ║");
+ Console.WriteLine("╚══════════════════════════════════════════════════════════════════════════╝\n");
+
+ var checker = new CertificateChecker();
+ var allCerts = new List();
+
+ // Проверяем все хранилища
+ 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();
+ }
+
+ ///
+ /// Установка всех сертификатов
+ ///
+ static async Task InstallAllCertificatesAsync(CertificateChecker checker, List certificates, List 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(" Попробуйте запустить приложение от имени администратора.");
+ }
+ }
+
+ ///
+ /// Выборочная установка сертификатов
+ ///
+ static async Task InstallSelectedCertificatesAsync(CertificateChecker checker, List certificates, List 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 selectedIndexes = new List();
+
+ 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}");
+ }
+
+ ///
+ /// Удаление всех сертификатов
+ ///
+ static async Task DeleteAllCertificatesAsync(CertificateChecker checker, List certificates, List 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(" Попробуйте запустить приложение от имени администратора.");
+ }
+ }
+
+ ///
+ /// Выборочное удаление сертификатов
+ ///
+ static async Task DeleteSelectedCertificatesAsync(CertificateChecker checker, List certificates, List 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 selectedIndexes = new List();
+
+ 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}");
+ }
+
+ ///
+ /// Выводит таблицу всех проверенных сертификатов
+ ///
+ static void PrintCertificateTable(List results)
+ {
+ var colWidth = new Dictionary
+ {
+ ["#"] = 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);
+ }
+}
\ No newline at end of file
diff --git a/Russain SSL Installer/Properties/Resources.Designer.cs b/Russain SSL Installer/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..dbd3223
--- /dev/null
+++ b/Russain SSL Installer/Properties/Resources.Designer.cs
@@ -0,0 +1,113 @@
+//------------------------------------------------------------------------------
+//
+// Этот код создан программой.
+// Исполняемая версия:4.0.30319.42000
+//
+// Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае
+// повторной генерации кода.
+//
+//------------------------------------------------------------------------------
+
+namespace Russain_SSL_Installer.Properties {
+ using System;
+
+
+ ///
+ /// Класс ресурса со строгой типизацией для поиска локализованных строк и т.д.
+ ///
+ // Этот класс создан автоматически классом 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() {
+ }
+
+ ///
+ /// Возвращает кэшированный экземпляр ResourceManager, использованный этим классом.
+ ///
+ [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;
+ }
+ }
+
+ ///
+ /// Перезаписывает свойство CurrentUICulture текущего потока для всех
+ /// обращений к ресурсу с помощью этого класса ресурса со строгой типизацией.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Поиск локализованного ресурса типа System.Byte[].
+ ///
+ internal static byte[] russian_trusted_root_ca {
+ get {
+ object obj = ResourceManager.GetObject("russian_trusted_root_ca", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Поиск локализованного ресурса типа System.Byte[].
+ ///
+ internal static byte[] russian_trusted_root_ca_gost_2025 {
+ get {
+ object obj = ResourceManager.GetObject("russian_trusted_root_ca_gost_2025", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Поиск локализованного ресурса типа System.Byte[].
+ ///
+ internal static byte[] russian_trusted_sub_ca {
+ get {
+ object obj = ResourceManager.GetObject("russian_trusted_sub_ca", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Поиск локализованного ресурса типа System.Byte[].
+ ///
+ internal static byte[] russian_trusted_sub_ca_2024 {
+ get {
+ object obj = ResourceManager.GetObject("russian_trusted_sub_ca_2024", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Поиск локализованного ресурса типа System.Byte[].
+ ///
+ internal static byte[] russian_trusted_sub_ca_gost_2025 {
+ get {
+ object obj = ResourceManager.GetObject("russian_trusted_sub_ca_gost_2025", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+ }
+}
diff --git a/Russain SSL Installer/Properties/Resources.resx b/Russain SSL Installer/Properties/Resources.resx
new file mode 100644
index 0000000..eec8e9d
--- /dev/null
+++ b/Russain SSL Installer/Properties/Resources.resx
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Resources\russian_trusted_root_ca.cer;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ Root
+
+
+ ..\Resources\russian_trusted_root_ca_gost_2025.cer;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ Root
+
+
+ ..\Resources\russian_trusted_sub_ca.cer;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ Sub
+
+
+ ..\Resources\russian_trusted_sub_ca_2024.cer;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ Sub
+
+
+ ..\Resources\russian_trusted_sub_ca_gost_2025.cer;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ Sub
+
+
\ No newline at end of file
diff --git a/Russain SSL Installer/Resources/russian_trusted_root_ca.cer b/Russain SSL Installer/Resources/russian_trusted_root_ca.cer
new file mode 100644
index 0000000..4c143a2
--- /dev/null
+++ b/Russain SSL Installer/Resources/russian_trusted_root_ca.cer
@@ -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-----
\ No newline at end of file
diff --git a/Russain SSL Installer/Resources/russian_trusted_root_ca_gost_2025.cer b/Russain SSL Installer/Resources/russian_trusted_root_ca_gost_2025.cer
new file mode 100644
index 0000000..873460e
Binary files /dev/null and b/Russain SSL Installer/Resources/russian_trusted_root_ca_gost_2025.cer differ
diff --git a/Russain SSL Installer/Resources/russian_trusted_sub_ca.cer b/Russain SSL Installer/Resources/russian_trusted_sub_ca.cer
new file mode 100644
index 0000000..2e3f828
--- /dev/null
+++ b/Russain SSL Installer/Resources/russian_trusted_sub_ca.cer
@@ -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-----
\ No newline at end of file
diff --git a/Russain SSL Installer/Resources/russian_trusted_sub_ca_2024.cer b/Russain SSL Installer/Resources/russian_trusted_sub_ca_2024.cer
new file mode 100644
index 0000000..1fbc4a4
--- /dev/null
+++ b/Russain SSL Installer/Resources/russian_trusted_sub_ca_2024.cer
@@ -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-----
diff --git a/Russain SSL Installer/Resources/russian_trusted_sub_ca_gost_2025.cer b/Russain SSL Installer/Resources/russian_trusted_sub_ca_gost_2025.cer
new file mode 100644
index 0000000..9ed53a0
Binary files /dev/null and b/Russain SSL Installer/Resources/russian_trusted_sub_ca_gost_2025.cer differ
diff --git a/Russain SSL Installer/Russain SSL Installer.csproj b/Russain SSL Installer/Russain SSL Installer.csproj
new file mode 100644
index 0000000..8e82cd7
--- /dev/null
+++ b/Russain SSL Installer/Russain SSL Installer.csproj
@@ -0,0 +1,26 @@
+
+
+
+ Exe
+ net10.0
+ Russain_SSL_Installer
+ enable
+ enable
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+