using EnvelopeGenerator.Application.Common.Configurations; namespace EnvelopeGenerator.Infrastructure.Crypt.Services; internal static class Pkcs11InteropBootstrap { private static readonly object Sync = new(); private static bool Initialized; private const string ProxyRelativePath = "pkcs11\\windows\\proxy\\libpkcs11-proxy.dll"; private const string ToolRelativePath = "pkcs11\\windows\\opensc\\tools\\pkcs11-tool.exe"; public static void EnsureLoaded(Pkcs11Options options) { if (Initialized) return; lock (Sync) { if (Initialized) return; var libraryPath = ResolveLibraryPath(options); var toolPath = ResolveToolPath(options); options.SetResolvedPaths(libraryPath, toolPath); EnsureNativeDependencySearchPath(options.ResolvedLibraryPath); if (!string.IsNullOrWhiteSpace(options.ProxySocket)) { Environment.SetEnvironmentVariable("PKCS11_PROXY_SOCKET", options.ProxySocket); Environment.SetEnvironmentVariable("PKCS11_DAEMON_SOCKET", options.ProxySocket); } if (!string.IsNullOrWhiteSpace(options.ResolvedToolPath)) { var toolDir = Path.GetDirectoryName(options.ResolvedToolPath); if (!string.IsNullOrWhiteSpace(toolDir)) EnsurePathContains(toolDir); } WarmupWithPkcs11Tool(options); Initialized = true; } } private static string ResolveLibraryPath(Pkcs11Options options) { var assetsRoot = string.IsNullOrWhiteSpace(options.AssetsRootPath) ? AppContext.BaseDirectory : options.AssetsRootPath; var resolved = Path.Combine(assetsRoot, ProxyRelativePath); if (File.Exists(resolved)) return resolved; throw new FileNotFoundException($"PKCS#11 proxy module not found. Checked path: {resolved}", resolved); } private static string? ResolveToolPath(Pkcs11Options options) { var assetsRoot = string.IsNullOrWhiteSpace(options.AssetsRootPath) ? AppContext.BaseDirectory : options.AssetsRootPath; var resolved = Path.Combine(assetsRoot, ToolRelativePath); if (File.Exists(resolved)) return resolved; return null; } private static void WarmupWithPkcs11Tool(Pkcs11Options options) { if (string.IsNullOrWhiteSpace(options.ResolvedToolPath)) return; if (!File.Exists(options.ResolvedToolPath)) return; var process = new System.Diagnostics.Process { StartInfo = new System.Diagnostics.ProcessStartInfo { FileName = options.ResolvedToolPath, Arguments = $"--module \"{options.ResolvedLibraryPath}\" -L", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; process.Start(); process.WaitForExit(15000); } private static void EnsureNativeDependencySearchPath(string libraryPath) { var directory = Path.GetDirectoryName(libraryPath); if (string.IsNullOrWhiteSpace(directory)) return; EnsurePathContains(directory); } private static void EnsurePathContains(string directory) { var currentPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; var entries = currentPath.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (entries.Any(p => string.Equals(p, directory, StringComparison.OrdinalIgnoreCase))) return; var updatedPath = string.IsNullOrWhiteSpace(currentPath) ? directory : directory + ";" + currentPath; Environment.SetEnvironmentVariable("PATH", updatedPath); } }