Renamed namespaces and projects from DocumentOperator to DocumentService, updating all references accordingly. Refactored PdfSignatureAppearanceDto and related enums to use properties with init accessors for improved clarity. Removed and re-added IPkcs11SelfTestStatusProvider with updated namespace. Simplified dependency injection by removing DocumentOperatorApplication registration. Updated appsettings.json with a specific AssetsRootPath. Incremented version numbers in project files for a new release. Introduced PdfSignatureClient and IPdfSignatureClient for PDF digital-signing operations. Updated project files to use newer Microsoft.Extensions package versions. Removed all DocumentOperator references, completing the transition to DocumentService. Added new DTOs and enums for PDF signature options and appearance.
125 lines
4.3 KiB
C#
125 lines
4.3 KiB
C#
using DocumentService.Application.Common.Configuration;
|
|
using Net.Pkcs11Interop.Common;
|
|
using Net.Pkcs11Interop.HighLevelAPI;
|
|
|
|
namespace DocumentService.Infrastructure.Crypt.Services;
|
|
|
|
internal static class Pkcs11SessionRunner
|
|
{
|
|
public static T Run<T>(Pkcs11Options options, Func<ISession, T> action)
|
|
{
|
|
Pkcs11InteropBootstrap.EnsureLoaded(options);
|
|
|
|
var factories = new Pkcs11InteropFactories();
|
|
using var pkcs11 = LoadLibraryWithFallback(factories, options.ResolvedLibraryPath, options.ProxySocket);
|
|
var slot = ResolveSlot(pkcs11.GetSlotList(SlotsType.WithTokenPresent), options);
|
|
|
|
using var session = slot.OpenSession(SessionType.ReadOnly);
|
|
session.Login(CKU.CKU_USER, options.UserPin);
|
|
|
|
try
|
|
{
|
|
return action(session);
|
|
}
|
|
finally
|
|
{
|
|
session.Logout();
|
|
}
|
|
}
|
|
|
|
private static IPkcs11Library LoadLibraryWithFallback(Pkcs11InteropFactories factories, string libraryPath, string? proxySocket)
|
|
{
|
|
List<Exception> errors = [];
|
|
foreach (string candidate in BuildLibraryCandidates(libraryPath))
|
|
{
|
|
try
|
|
{
|
|
return factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, candidate, AppType.MultiThreaded);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errors.Add(new InvalidOperationException($"{candidate} | MultiThreaded+WithFunctionList", ex));
|
|
}
|
|
|
|
try
|
|
{
|
|
return factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, candidate, AppType.SingleThreaded);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errors.Add(new InvalidOperationException($"{candidate} | SingleThreaded+WithFunctionList", ex));
|
|
}
|
|
|
|
try
|
|
{
|
|
return factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, candidate, AppType.MultiThreaded, InitType.WithoutFunctionList);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errors.Add(new InvalidOperationException($"{candidate} | MultiThreaded+WithoutFunctionList", ex));
|
|
}
|
|
|
|
try
|
|
{
|
|
return factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, candidate, AppType.SingleThreaded, InitType.WithoutFunctionList);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errors.Add(new InvalidOperationException($"{candidate} | SingleThreaded+WithoutFunctionList", ex));
|
|
}
|
|
}
|
|
|
|
var socketInfo = string.IsNullOrWhiteSpace(proxySocket) ? "<empty>" : proxySocket;
|
|
throw new AggregateException($"Unable to initialize PKCS#11 library '{libraryPath}' with any supported initialization mode. PKCS11_PROXY_SOCKET={socketInfo}", errors);
|
|
}
|
|
|
|
private static IEnumerable<string> BuildLibraryCandidates(string configuredPath)
|
|
{
|
|
var unique = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
configuredPath
|
|
};
|
|
|
|
var directory = Path.GetDirectoryName(configuredPath);
|
|
if (!string.IsNullOrWhiteSpace(directory))
|
|
{
|
|
string[] alternates =
|
|
[
|
|
"client.dll",
|
|
"pkcs11-proxy.dll",
|
|
"libpkcs11-proxy.dll"
|
|
];
|
|
|
|
foreach (var fileName in alternates)
|
|
{
|
|
var candidate = Path.Combine(directory, fileName);
|
|
if (File.Exists(candidate))
|
|
unique.Add(candidate);
|
|
}
|
|
}
|
|
|
|
return unique;
|
|
}
|
|
|
|
private static ISlot ResolveSlot(IEnumerable<ISlot> slots, Pkcs11Options options)
|
|
{
|
|
if (options.SlotId.HasValue)
|
|
{
|
|
var bySlotId = slots.FirstOrDefault(s => s.SlotId == options.SlotId.Value);
|
|
if (bySlotId is not null)
|
|
return bySlotId;
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(options.TokenLabel))
|
|
{
|
|
var byTokenLabel = slots.FirstOrDefault(s =>
|
|
string.Equals(s.GetTokenInfo().Label?.Trim(), options.TokenLabel.Trim(), StringComparison.Ordinal));
|
|
|
|
if (byTokenLabel is not null)
|
|
return byTokenLabel;
|
|
}
|
|
|
|
throw new InvalidOperationException("Unable to resolve PKCS#11 slot. Check Crypt:Pkcs11:SlotId / TokenLabel.");
|
|
}
|
|
}
|