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;
+ }
}