Refactor and rename to DocumentService
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.
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace DocumentService.Application.Common.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents PKCS#11 signing configuration for PDF signing workflows.
|
||||
/// This options model maps to the <c>Crypt:Pkcs11</c> configuration section.
|
||||
/// </summary>
|
||||
public sealed class Pkcs11Options
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration section path used for binding PKCS#11 options.
|
||||
/// </summary>
|
||||
public const string SectionName = "Crypt:Pkcs11";
|
||||
|
||||
/// <summary>
|
||||
/// Optional base directory that contains the <c>pkcs11</c> folder.
|
||||
/// If null/empty, runtime uses the application base directory.
|
||||
/// </summary>
|
||||
public string? AssetsRootPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional numeric slot identifier used to directly select the target token slot.
|
||||
/// </summary>
|
||||
public ulong? SlotId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional token label used as a fallback token selector when <see cref="SlotId"/> is not provided.
|
||||
/// </summary>
|
||||
public string? TokenLabel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional PKCS#11 proxy endpoint (for example <c>tcp://host:port</c>).
|
||||
/// </summary>
|
||||
public string? ProxySocket { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resolved absolute PKCS#11 proxy module path used at runtime.
|
||||
/// Computed from <see cref="AssetsRootPath"/> and runtime defaults.
|
||||
/// </summary>
|
||||
public string ResolvedLibraryPath { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Resolved absolute pkcs11-tool path used at runtime.
|
||||
/// Computed from <see cref="AssetsRootPath"/> and runtime defaults.
|
||||
/// Can be null when tool fallback is not available.
|
||||
/// </summary>
|
||||
public string? ResolvedToolPath { get; private set; }
|
||||
|
||||
public void SetResolvedPaths(string resolvedLibraryPath, string? resolvedToolPath)
|
||||
{
|
||||
ResolvedLibraryPath = resolvedLibraryPath;
|
||||
ResolvedToolPath = resolvedToolPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User PIN used to authenticate to the PKCS#11 token session.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
public required string UserPin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Label of the private key object used to create digital signatures.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
public required string PrivateKeyLabel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Label of the certificate object returned/embedded with the signature.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
public required string CertificateLabel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional global default signing reason used when request-level options do not specify a value.
|
||||
/// </summary>
|
||||
public string? DefaultReason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Application name embedded into PDF signature metadata.
|
||||
/// This value is controlled via app settings and is not client-overridable.
|
||||
/// </summary>
|
||||
public string? ApplicationName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls how fallback signing time is created when request-level options do not specify a value.
|
||||
/// <c>true</c> uses <see cref="DateTimeOffset.UtcNow"/>, <c>false</c> uses <see cref="DateTimeOffset.Now"/>.
|
||||
/// </summary>
|
||||
public bool UseUtcSigningTime { get; set; } = true;
|
||||
}
|
||||
52
DocumentService.Application/Common/DTOs/PdfSignOptionsDto.cs
Normal file
52
DocumentService.Application/Common/DTOs/PdfSignOptionsDto.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
namespace DocumentService.Application.Common.DTOs;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public sealed record PdfSignOptionsDto
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public long DocumentElementId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Reason { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Location { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? Phone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? ApplicationName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public PdfSignatureCertificationLevel CertificationLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public PdfSignatureAppearanceDto? Appearance { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
namespace DocumentService.Application.Common.DTOs;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum PdfSignatureCoordinateUnit
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Point = 0,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Inch = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum PdfSignatureCoordinateOrigin
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
BottomLeft = 0,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
TopLeft = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public sealed record PdfSignatureAppearanceDto
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int PageNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public double Left { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public double Bottom { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public double Width { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public double Height { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool ShowLabels { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool ShowName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool ShowDate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool ShowReason { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool ShowLocation { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool ShowDistinguishedName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string DateTimeFormat { get; init; } = "dd.MM.yyyy HH:mm:ss zzz";
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public PdfSignatureCoordinateUnit CoordinateUnit { get; init; } = PdfSignatureCoordinateUnit.Point;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public PdfSignatureCoordinateOrigin CoordinateOrigin { get; init; } = PdfSignatureCoordinateOrigin.BottomLeft;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public FontDto Font { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public sealed record FontDto
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string FamilyName { get; init; } = "Helvetica";
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public float Size { get; init; } = 10;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsBold { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsItalic { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsUnderline { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public RgbaColorDto Color { get; init; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public sealed record RgbaColorDto
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public double R { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public double G { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public double B { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace DocumentService.Application.Common.DTOs;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum PdfSignatureCertificationLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// All changes are allowed
|
||||
/// </summary>
|
||||
NoCertification = 0,
|
||||
|
||||
/// <summary>
|
||||
/// No changes are allowed
|
||||
/// </summary>
|
||||
NoChangesAllowed,
|
||||
|
||||
/// <summary>
|
||||
/// Allows users to fill in forms and sign the document.
|
||||
/// </summary>
|
||||
FillForms,
|
||||
|
||||
/// <summary>
|
||||
/// Allows users to fill in forms, create, modify and delete annotations, and sign the document.
|
||||
/// </summary>
|
||||
FillFormsAndAnnotate
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
namespace DocumentService.Application.Common.DTOs;
|
||||
|
||||
public sealed record PdfSignaturePrincipalDto(
|
||||
string? CommonName,
|
||||
string? Organization,
|
||||
string? Country,
|
||||
string? Full);
|
||||
|
||||
public sealed record PdfSignatureCertificateInfoDto(
|
||||
PdfSignaturePrincipalDto IssuedTo,
|
||||
PdfSignaturePrincipalDto IssuedBy,
|
||||
string? SerialNumber,
|
||||
DateTimeOffset? ValidFrom,
|
||||
DateTimeOffset? ValidTo,
|
||||
bool SelfSigned,
|
||||
bool Expired,
|
||||
bool NotYetValid,
|
||||
bool IsSigner,
|
||||
int? Version,
|
||||
string? KeyAlgorithm,
|
||||
string? SignatureAlgorithm,
|
||||
string? Pem,
|
||||
int? KeySizeBits);
|
||||
|
||||
public sealed record PdfSignatureCheckItemDto(
|
||||
bool CoversWholeDoc,
|
||||
string? Reason,
|
||||
string? BusinessReason,
|
||||
string? Location,
|
||||
string? DeclaredName,
|
||||
string? ContactInfo,
|
||||
string? Email,
|
||||
string? Phone,
|
||||
string? DigestAlgorithm,
|
||||
string? SignatureAlgorithm,
|
||||
string? SignerName,
|
||||
string? Issuer,
|
||||
string? SerialNumber,
|
||||
DateTimeOffset? ValidFrom,
|
||||
DateTimeOffset? ValidTo,
|
||||
string ExpiryCheckedAgainst,
|
||||
IReadOnlyList<PdfSignatureCertificateInfoDto> Certificates,
|
||||
bool Unchanged,
|
||||
string? FieldName,
|
||||
bool IsCryptographicallyValid,
|
||||
string? SigningTime,
|
||||
string? Filter,
|
||||
string? SubFilter,
|
||||
string? ApplicationName,
|
||||
long? DocumentElementId,
|
||||
string? OriginalDocumentSha256,
|
||||
string? MessageDigestHex,
|
||||
string? Error);
|
||||
|
||||
public sealed record PdfSignatureCheckResultDto(
|
||||
bool Signed,
|
||||
IReadOnlyList<PdfSignatureCheckItemDto> Signatures);
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace DocumentService.Application.Common.DTOs;
|
||||
|
||||
public sealed record Pkcs11SelfTestStatusDto(
|
||||
bool Completed,
|
||||
bool Success,
|
||||
string Mode,
|
||||
string Summary,
|
||||
DateTimeOffset? CheckedAtUtc,
|
||||
string? InteropError,
|
||||
string? ToolError);
|
||||
@@ -0,0 +1,29 @@
|
||||
using DocumentService.Application.Common.DTOs;
|
||||
|
||||
namespace DocumentService.Application.Common.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Defines PDF digital-signing operations for the application boundary.
|
||||
/// </summary>
|
||||
public interface IPdfDigitalSignatureService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a digital signature for the provided PDF bytes and returns
|
||||
/// a new signed PDF document.
|
||||
/// </summary>
|
||||
/// <param name="inputPdf">Source PDF content stream.</param>
|
||||
/// <param name="outputPdf">Destination stream for signed PDF output.</param>
|
||||
/// <param name="options">Optional signing options that map to PDF signature metadata fields.</param>
|
||||
/// <param name="image">Optional image stream for signature appearance.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for cooperative cancellation.</param>
|
||||
Task SignPdfAsync(Stream inputPdf, Stream outputPdf, PdfSignOptionsDto? options = null, Stream? image = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the provided PDF contains signature field(s) and whether
|
||||
/// those signatures are cryptographically valid.
|
||||
/// </summary>
|
||||
/// <param name="inputPdf">PDF content stream to inspect.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for cooperative cancellation.</param>
|
||||
/// <returns>A structured result containing signature presence and per-signature details.</returns>
|
||||
Task<PdfSignatureCheckResultDto> CheckPdfSignatureAsync(Stream inputPdf, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using DocumentService.Application.Common.DTOs;
|
||||
|
||||
namespace DocumentService.Application.Common.Interfaces;
|
||||
|
||||
public interface IPkcs11SelfTestStatusProvider
|
||||
{
|
||||
Pkcs11SelfTestStatusDto GetStatus();
|
||||
}
|
||||
22
DocumentService.Application/SignPdf/SignPdfCommand.cs
Normal file
22
DocumentService.Application/SignPdf/SignPdfCommand.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using DocumentService.Application.Common.DTOs;
|
||||
using DocumentService.Application.Common.Interfaces;
|
||||
using MediatR;
|
||||
|
||||
namespace DocumentService.Application.SignPdf;
|
||||
|
||||
public sealed record SignPdfCommand : IRequest<byte[]>
|
||||
{
|
||||
public required Stream InputPdf { get; init; }
|
||||
public required PdfSignOptionsDto Options { get; init; }
|
||||
public Stream? SignatureImage { get; init; }
|
||||
}
|
||||
|
||||
public sealed class SignPdfCommandHandler(IPdfDigitalSignatureService signer) : IRequestHandler<SignPdfCommand, byte[]>
|
||||
{
|
||||
public async Task<byte[]> Handle(SignPdfCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
using var output = new MemoryStream();
|
||||
await signer.SignPdfAsync(command.InputPdf, output, command.Options, command.SignatureImage, cancellationToken);
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user