Добавьте файлы проекта.

This commit is contained in:
MaDestro 2026-08-11 22:19:10 +03:00
parent 9b62db45fa
commit 010f86fffc
11 changed files with 1755 additions and 0 deletions

View File

@ -0,0 +1,3 @@
<Solution>
<Project Path="Russain SSL Installer/Russain SSL Installer.csproj" />
</Solution>

View File

@ -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; }
}

View File

@ -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);
}
}

View File

@ -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));
}
}
}
}

View File

@ -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>

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -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>