Add original revision extraction for signed PDFs
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user