From 955ac57a1a622d0664e17891d52e2a171c815098 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 14 Oct 2026 13:49:46 +0200 Subject: [PATCH] Add original revision extraction for signed PDFs --- .../Services/IPdfDigitalSignatureService.cs | 9 +++++ .../Services/PdfDigitalSignatureService.cs | 36 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/EnvelopeGenerator.Application/Common/Interfaces/Services/IPdfDigitalSignatureService.cs b/EnvelopeGenerator.Application/Common/Interfaces/Services/IPdfDigitalSignatureService.cs index 84fe4b80..4bfdd640 100644 --- a/EnvelopeGenerator.Application/Common/Interfaces/Services/IPdfDigitalSignatureService.cs +++ b/EnvelopeGenerator.Application/Common/Interfaces/Services/IPdfDigitalSignatureService.cs @@ -35,4 +35,13 @@ public interface IPdfDigitalSignatureService /// Cancellation token for cooperative cancellation. /// A structured result containing signature presence and per-signature details. Task CheckPdfSignatureAsync(byte[] pdfBytes, CancellationToken cancellationToken = default); + + /// + /// Extracts the original signed revision from a signed PDF. + /// For multi-revision files this returns the first signed revision bytes. + /// + /// Signed PDF bytes. + /// Cancellation token for cooperative cancellation. + /// PDF bytes of the original signed revision. + Task ExtractOriginalPdfAsync(byte[] pdfBytes, CancellationToken cancellationToken = default); } diff --git a/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs b/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs index 8295ace6..25a627ec 100644 --- a/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs +++ b/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs @@ -162,6 +162,29 @@ public sealed class PdfDigitalSignatureService(IOptions options) Signatures: items)); } + public Task ExtractOriginalPdfAsync(byte[] pdfBytes, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(pdfBytes); + cancellationToken.ThrowIfCancellationRequested(); + + var byteRanges = ParseByteRanges(pdfBytes); + if (byteRanges.Count == 0) + return Task.FromResult(pdfBytes); + + var revisionLength = byteRanges + .Select(GetSignedRevisionLength) + .Where(x => x > 0) + .DefaultIfEmpty(pdfBytes.LongLength) + .Min(); + + if (revisionLength > pdfBytes.LongLength) + throw new InvalidOperationException("Invalid ByteRange in signed PDF."); + + var originalPdf = new byte[revisionLength]; + Buffer.BlockCopy(pdfBytes, 0, originalPdf, 0, (int)revisionLength); + return Task.FromResult(originalPdf); + } + private static PdfSignatureCertificateInfoDto BuildCertificateInfo(X509Certificate2? certificate, DateTimeOffset now) { if (certificate is null) @@ -398,4 +421,17 @@ public sealed class PdfDigitalSignatureService(IOptions options) return previousEnd == fileLength; } + + private static long GetSignedRevisionLength(long[] byteRange) + { + if (byteRange.Length < 2) + return -1; + + var start = byteRange[^2]; + var length = byteRange[^1]; + if (start < 0 || length < 0) + return -1; + + return start + length; + } }