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