diff --git a/EnvelopeGenerator.Application/Common/Dto/PdfSignOptionsDto.cs b/EnvelopeGenerator.Application/Common/Dto/PdfSignOptionsDto.cs index 6ec8fa26..9b9ccf60 100644 --- a/EnvelopeGenerator.Application/Common/Dto/PdfSignOptionsDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/PdfSignOptionsDto.cs @@ -2,15 +2,53 @@ using EnvelopeGenerator.Application.Common.Enums; namespace EnvelopeGenerator.Application.Common.Dto; +/// +/// +/// public sealed record PdfSignOptionsDto { - public string? UUID { get; set; } - public string? Name { get; set; } - public string? Reason { get; set; } - public string? Location { get; set; } - public string? Email { get; set; } - public string? Phone { get; set; } - public string? ApplicationName { get; set; } - public PdfSignatureCertificationLevel? CertificationLevel { get; set; } - public PdfVisualSignatureOptionsDto? VisualSignature { get; set; } + /// + /// + /// + public string UUID { get; set; } = null!; + + /// + /// + /// + public string Name { get; set; } = null!; + + /// + /// + /// + public string Reason { get; set; } = null!; + + /// + /// + /// + public string Location { get; set; } = null!; + + /// + /// + /// + public string Email { get; set; } = null!; + + /// + /// + /// + public string Phone { get; set; } = null!; + + /// + /// + /// + public string ApplicationName { get; set; } = null!; + + /// + /// + /// + public PdfSignatureCertificationLevel CertificationLevel { get; set; } + + /// + /// + /// + public PdfVisualSignatureOptionsDto VisualSignature { get; set; } = null!; } diff --git a/EnvelopeGenerator.Application/Common/Dto/PdfVisualSignatureOptionsDto.cs b/EnvelopeGenerator.Application/Common/Dto/PdfVisualSignatureOptionsDto.cs index 68591c13..4716c1bb 100644 --- a/EnvelopeGenerator.Application/Common/Dto/PdfVisualSignatureOptionsDto.cs +++ b/EnvelopeGenerator.Application/Common/Dto/PdfVisualSignatureOptionsDto.cs @@ -1,5 +1,15 @@ namespace EnvelopeGenerator.Application.Common.Dto; +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// public sealed record PdfVisualSignatureOptionsDto( byte[] ImageBytes, int PageNumber, diff --git a/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs b/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs index c3b97ad4..8aa933b0 100644 --- a/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs +++ b/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs @@ -18,10 +18,8 @@ public sealed class PdfDigitalSignatureService(IOptions options) { private static readonly Regex SignatureReasonMetaRegex = new(@"\|\s*SFMETA:v1;doc=(?[0-9a-fA-F-]{36});sha256=(?[A-Fa-f0-9]{64})$", RegexOptions.CultureInvariant | RegexOptions.Compiled); - public async Task SignPdfAsync(Stream inputPdf, Stream outputPdf, PdfSignOptionsDto? optionsModel = null, CancellationToken cancellationToken = default) + public async Task SignPdfAsync(Stream inputPdf, Stream outputPdf, PdfSignOptionsDto request, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(inputPdf); - ArgumentNullException.ThrowIfNull(outputPdf); cancellationToken.ThrowIfCancellationRequested(); var (workingInput, ownsWorkingInput) = await EnsureSeekableInputAsync(inputPdf, cancellationToken); @@ -33,7 +31,7 @@ public sealed class PdfDigitalSignatureService(IOptions options) workingInput.Position = 0; var pkcs11Options = options.Value; - var request = optionsModel ?? new PdfSignOptionsDto(); + var uuid = ResolveUuid(request.UUID); var reasonWithMeta = BuildReasonWithMeta(request.Reason ?? pkcs11Options.DefaultReason, uuid, sourceHashHex); @@ -47,9 +45,9 @@ public sealed class PdfDigitalSignatureService(IOptions options) Name = $"Sig_{DateTimeOffset.UtcNow:yyyyMMddHHmmss}" }; - var signatureBounds = CreateSignatureBounds(request.VisualSignature); - if (signatureBounds is not null) - signatureFieldInfo.SignatureBounds = signatureBounds; + var left = request.VisualSignature!.Left; + var bottom = request.VisualSignature.Bottom; + signatureFieldInfo.SignatureBounds = new PdfRectangle(left, bottom, left + request.VisualSignature.Width, bottom + request.VisualSignature.Height); var builder = new PdfDeferredSignatureBuilder(signerInfo, signatureFieldInfo) { @@ -64,21 +62,18 @@ public sealed class PdfDigitalSignatureService(IOptions options) ?? PdfCertificationLevel.NoCertification }; - if(request.VisualSignature is PdfVisualSignatureOptionsDto visualSignature) + builder.SetSignatureAppearance(new PdfSignatureAppearance { - builder.SetSignatureAppearance(new PdfSignatureAppearance - { - AppearanceType = PdfSignatureAppearanceType.Name, - ShowLabels = visualSignature.ShowLabels, - ShowName = true, - ShowDate = false, - ShowReason = false, - ShowLocation = false, - ShowDistinguishedName = false - }); - builder.SetImageData(visualSignature.ImageBytes); - } - + AppearanceType = PdfSignatureAppearanceType.Name, + ShowLabels = request.VisualSignature.ShowLabels, + ShowName = true, + ShowDate = false, + ShowReason = false, + ShowLocation = false, + ShowDistinguishedName = false + }); + builder.SetImageData(request.VisualSignature.ImageBytes); + var deferredSigner = signer.SignDeferred(builder); var externalSigner = new Pkcs11ExternalSigner(pkcs11Options); deferredSigner.Sign(outputPdf, externalSigner.BuildSignature(deferredSigner.HashValue, digestCalculator.AlgorithmOid)); @@ -92,9 +87,6 @@ public sealed class PdfDigitalSignatureService(IOptions options) public async Task SignPdfAsync(Stream inputPdf, Stream outputPdf, IReadOnlyList optionsList, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(inputPdf); - ArgumentNullException.ThrowIfNull(outputPdf); - ArgumentNullException.ThrowIfNull(optionsList); cancellationToken.ThrowIfCancellationRequested(); if (optionsList.Count == 0) @@ -138,7 +130,6 @@ public sealed class PdfDigitalSignatureService(IOptions options) public async Task CheckPdfSignatureAsync(Stream inputPdf, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(inputPdf); cancellationToken.ThrowIfCancellationRequested(); var pdfBytes = await ReadAllBytesAsync(inputPdf, cancellationToken); @@ -246,8 +237,6 @@ public sealed class PdfDigitalSignatureService(IOptions options) public async Task ExtractOriginalPdfAsync(Stream inputPdf, Stream outputPdf, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(inputPdf); - ArgumentNullException.ThrowIfNull(outputPdf); cancellationToken.ThrowIfCancellationRequested(); var pdfBytes = await ReadAllBytesAsync(inputPdf, cancellationToken); @@ -338,18 +327,25 @@ public sealed class PdfDigitalSignatureService(IOptions options) KeyAlgorithm: certificate.PublicKey.Oid?.FriendlyName ?? certificate.PublicKey.Oid?.Value, SignatureAlgorithm: certificate.SignatureAlgorithm.FriendlyName ?? certificate.SignatureAlgorithm.Value, Pem: ToPem(certificate.RawData), - KeySizeBits: TryGetKeySize(certificate)); + KeySizeBits: TryGetKeySize(certificate, out var keySize) ? keySize : null); } - private static int? TryGetKeySize(X509Certificate2 certificate) + private static bool TryGetKeySize(X509Certificate2 certificate, out int keySize) { + keySize = 0; try { - return certificate.PublicKey.Key.KeySize; + using var rsa = certificate.GetRSAPublicKey(); + + if (rsa is null) + return false; + + keySize = rsa.KeySize; + return true; } - catch + catch (CryptographicException) { - return null; + return false; } } @@ -567,25 +563,6 @@ public sealed class PdfDigitalSignatureService(IOptions options) return pageNumber; } - private static PdfRectangle? CreateSignatureBounds(PdfVisualSignatureOptionsDto? visualOptions) - { - if (visualOptions is null) - return null; - - var left = visualOptions.Left; - var bottom = visualOptions.Bottom; - var width = visualOptions.Width; - var height = visualOptions.Height; - - if (left < 0 || bottom < 0) - throw new ArgumentOutOfRangeException(nameof(visualOptions), "VisualSignature.Left and VisualSignature.Bottom cannot be negative."); - - if (width <= 0 || height <= 0) - throw new ArgumentOutOfRangeException(nameof(visualOptions), "VisualSignature.Width and VisualSignature.Height must be greater than zero."); - - return new PdfRectangle(left, bottom, left + width, bottom + height); - } - private static string BuildReasonWithMeta(string? businessReason, string uuid, string sourceHashHex) { var trimmedReason = string.IsNullOrWhiteSpace(businessReason) ? null : businessReason.Trim();