Add original revision extraction for signed PDFs

This commit is contained in:
2026-10-14 13:49:46 +02:00
parent b8a96330c0
commit 955ac57a1a
2 changed files with 45 additions and 0 deletions

View File

@@ -35,4 +35,13 @@ public interface IPdfDigitalSignatureService
/// <param name="cancellationToken">Cancellation token for cooperative cancellation.</param> /// <param name="cancellationToken">Cancellation token for cooperative cancellation.</param>
/// <returns>A structured result containing signature presence and per-signature details.</returns> /// <returns>A structured result containing signature presence and per-signature details.</returns>
Task<PdfSignatureCheckResultDto> CheckPdfSignatureAsync(byte[] pdfBytes, CancellationToken cancellationToken = default); Task<PdfSignatureCheckResultDto> CheckPdfSignatureAsync(byte[] pdfBytes, CancellationToken cancellationToken = default);
/// <summary>
/// Extracts the original signed revision from a signed PDF.
/// For multi-revision files this returns the first signed revision bytes.
/// </summary>
/// <param name="pdfBytes">Signed PDF bytes.</param>
/// <param name="cancellationToken">Cancellation token for cooperative cancellation.</param>
/// <returns>PDF bytes of the original signed revision.</returns>
Task<byte[]> ExtractOriginalPdfAsync(byte[] pdfBytes, CancellationToken cancellationToken = default);
} }

View File

@@ -162,6 +162,29 @@ public sealed class PdfDigitalSignatureService(IOptions<Pkcs11Options> options)
Signatures: items)); Signatures: items));
} }
public Task<byte[]> 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) private static PdfSignatureCertificateInfoDto BuildCertificateInfo(X509Certificate2? certificate, DateTimeOffset now)
{ {
if (certificate is null) if (certificate is null)
@@ -398,4 +421,17 @@ public sealed class PdfDigitalSignatureService(IOptions<Pkcs11Options> options)
return previousEnd == fileLength; 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;
}
} }