From 14d6e949124972b5c8638abd424cafc4eb6106e7 Mon Sep 17 00:00:00 2001 From: Francesc Vila Date: Tue, 13 Feb 2024 13:06:20 +0100 Subject: [PATCH 1/3] improve signature handling with timestamp support --- .../PdfSharp/Pdf.Signatures/BouncySigner.cs | 5 ++++ .../PdfSharp/Pdf.Signatures/DefaultSigner.cs | 26 +++++++++++++++++++ .../src/PdfSharp/Pdf.Signatures/ISigner.cs | 1 + .../Pdf.Signatures/PdfSignatureHandler.cs | 20 ++++++++++++-- 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/BouncySigner.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/BouncySigner.cs index 1aa06532..abfa8c46 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/BouncySigner.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/BouncySigner.cs @@ -83,5 +83,10 @@ private string GetProperDigestAlgorithm(int pdfVersion) _ => throw new NotImplementedException(), }; } + + public Byte[] GetSignedCms(Stream stream, Int32 pdfVersion, Byte[] timestampToken) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs index cfcb7084..04ccb060 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs @@ -4,6 +4,7 @@ #if WPF using System.IO; #endif +using System.Security.Cryptography; using System.Security.Cryptography.Pkcs; using System.Security.Cryptography.X509Certificates; @@ -18,6 +19,31 @@ public DefaultSigner(X509Certificate2 Certificate) this.Certificate = Certificate; } + public byte[] GetSignedCms(Stream stream, int pdfVersion, byte[] timestampToken) + { + var range = new byte[stream.Length]; + + stream.Position = 0; + stream.Read(range, 0, range.Length); + + var contentInfo = new ContentInfo(range); + SignedCms signedCms = new SignedCms(contentInfo, true); + CmsSigner signer = new CmsSigner(Certificate); + + signer.UnsignedAttributes.Add(new Pkcs9SigningTime()); + + if (timestampToken != null) + { + AsnEncodedData timestampTokenAsnEncodedData = new AsnEncodedData(new Oid("1.2.840.113549.1.9.16.2.14"), timestampToken); + signer.UnsignedAttributes.Add(new CryptographicAttributeObject(new Oid("1.2.840.113549.1.9.16.2.14"), new AsnEncodedDataCollection(timestampTokenAsnEncodedData))); + } + + signedCms.ComputeSignature(signer, false); + var bytes = signedCms.Encode(); + + return bytes; + } + public byte[] GetSignedCms(Stream stream, int pdfVersion) { var range = new byte[stream.Length]; diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/ISigner.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/ISigner.cs index 8f5722c2..9141eb47 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/ISigner.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/ISigner.cs @@ -10,6 +10,7 @@ namespace PdfSharp.Pdf.Signatures public interface ISigner { byte[] GetSignedCms(Stream stream, int pdfVersion); + byte[] GetSignedCms(Stream stream, int pdfVersion, byte[] timestampToken); string GetName(); } diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/PdfSignatureHandler.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/PdfSignatureHandler.cs index 59a202a5..c868fc17 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/PdfSignatureHandler.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/PdfSignatureHandler.cs @@ -21,6 +21,7 @@ public class PdfSignatureHandler { private PdfString signatureFieldContentsPdfString; private PdfArray signatureFieldByteRangePdfArray; + private byte[] timestampToken; /// /// Cache signature length (bytes) for each PDF version since digest length depends on digest algorithm that depends on PDF version. @@ -52,7 +53,7 @@ public void AttachToDocument(PdfDocument documentToSign) // estimate signature length by computing signature for a fake byte[] if (!knownSignatureLengthInBytesByPdfVersion.ContainsKey(documentToSign.Version)) - knownSignatureLengthInBytesByPdfVersion[documentToSign.Version] = signer.GetSignedCms(new MemoryStream(new byte[] { 0 }), documentToSign.Version).Length; + knownSignatureLengthInBytesByPdfVersion[documentToSign.Version] = signer.GetSignedCms(new MemoryStream(new byte[] { 0 }), documentToSign.Version, timestampToken).Length; } public PdfSignatureHandler(ISigner signer, PdfSignatureOptions options) @@ -67,6 +68,21 @@ public PdfSignatureHandler(ISigner signer, PdfSignatureOptions options) this.Options = options; } + public PdfSignatureHandler(ISigner signer, PdfSignatureOptions options, byte[] timestampToken) + { + if (signer is null) + throw new ArgumentNullException(nameof(signer)); + if (options is null) + throw new ArgumentNullException(nameof(options)); + + if (options.PageIndex < 0) + throw new ArgumentOutOfRangeException($"Signature page index cannot be negative."); + + this.signer = signer; + this.Options = options; + this.timestampToken = timestampToken; + } + private void ComputeSignatureAndRange(object sender, PdfDocumentEventArgs e) { var writer = e.Writer; @@ -84,7 +100,7 @@ private void ComputeSignatureAndRange(object sender, PdfDocumentEventArgs e) // computing and writing document's digest - var signature = signer.GetSignedCms(rangedStreamToSign, Document.Version); + var signature = signer.GetSignedCms(rangedStreamToSign, Document.Version, timestampToken); if (signature.Length != knownSignatureLengthInBytesByPdfVersion[Document.Version]) throw new Exception("The digest length is different that the approximation made."); From bbcf5ac40089a3c4a42975ba8902d5f312a7c778 Mon Sep 17 00:00:00 2001 From: Francesc Vila Date: Wed, 14 Feb 2024 10:40:17 +0100 Subject: [PATCH 2/3] fix: cherry-pick conflict --- .../PdfSharp/Pdf.Signatures/BouncySigner.cs | 9 +++----- .../PdfSharp/Pdf.Signatures/DefaultSigner.cs | 21 +------------------ .../src/PdfSharp/Pdf.Signatures/ISigner.cs | 3 +-- .../Pdf.Signatures/PdfSignatureHandler.cs | 16 ++------------ 4 files changed, 7 insertions(+), 42 deletions(-) diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/BouncySigner.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/BouncySigner.cs index abfa8c46..ee1cd90c 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/BouncySigner.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/BouncySigner.cs @@ -28,8 +28,10 @@ public BouncySigner(Tuple certific this.CertificateChain = certificateData.Item2; } - public byte[] GetSignedCms(Stream rangedStream, int pdfVersion) + public byte[] GetSignedCms(Stream rangedStream, int pdfVersion, byte[]? timestampToken = null) { + // TODO: Implement timestampToken usage, right now is being ignored + rangedStream.Position = 0; CmsSignedDataGenerator signedDataGenerator = new CmsSignedDataGenerator(); @@ -83,10 +85,5 @@ private string GetProperDigestAlgorithm(int pdfVersion) _ => throw new NotImplementedException(), }; } - - public Byte[] GetSignedCms(Stream stream, Int32 pdfVersion, Byte[] timestampToken) - { - throw new NotImplementedException(); - } } } \ No newline at end of file diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs index 04ccb060..d2702e2e 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs @@ -19,7 +19,7 @@ public DefaultSigner(X509Certificate2 Certificate) this.Certificate = Certificate; } - public byte[] GetSignedCms(Stream stream, int pdfVersion, byte[] timestampToken) + public byte[] GetSignedCms(Stream stream, int pdfVersion, byte[]? timestampToken = null) { var range = new byte[stream.Length]; @@ -38,25 +38,6 @@ public byte[] GetSignedCms(Stream stream, int pdfVersion, byte[] timestampToken) signer.UnsignedAttributes.Add(new CryptographicAttributeObject(new Oid("1.2.840.113549.1.9.16.2.14"), new AsnEncodedDataCollection(timestampTokenAsnEncodedData))); } - signedCms.ComputeSignature(signer, false); - var bytes = signedCms.Encode(); - - return bytes; - } - - public byte[] GetSignedCms(Stream stream, int pdfVersion) - { - var range = new byte[stream.Length]; - - stream.Position = 0; - stream.Read(range, 0, range.Length); - - var contentInfo = new ContentInfo(range); - - SignedCms signedCms = new SignedCms(contentInfo, true); - CmsSigner signer = new CmsSigner(Certificate); - signer.UnsignedAttributes.Add(new Pkcs9SigningTime()); - signedCms.ComputeSignature(signer, true); var bytes = signedCms.Encode(); diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/ISigner.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/ISigner.cs index 9141eb47..00c66d5a 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/ISigner.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/ISigner.cs @@ -9,8 +9,7 @@ namespace PdfSharp.Pdf.Signatures { public interface ISigner { - byte[] GetSignedCms(Stream stream, int pdfVersion); - byte[] GetSignedCms(Stream stream, int pdfVersion, byte[] timestampToken); + byte[] GetSignedCms(Stream stream, int pdfVersion, byte[]? timestampToken = null); string GetName(); } diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/PdfSignatureHandler.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/PdfSignatureHandler.cs index c868fc17..e13079c2 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/PdfSignatureHandler.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/PdfSignatureHandler.cs @@ -21,7 +21,7 @@ public class PdfSignatureHandler { private PdfString signatureFieldContentsPdfString; private PdfArray signatureFieldByteRangePdfArray; - private byte[] timestampToken; + private byte[]? timestampToken; /// /// Cache signature length (bytes) for each PDF version since digest length depends on digest algorithm that depends on PDF version. @@ -56,19 +56,7 @@ public void AttachToDocument(PdfDocument documentToSign) knownSignatureLengthInBytesByPdfVersion[documentToSign.Version] = signer.GetSignedCms(new MemoryStream(new byte[] { 0 }), documentToSign.Version, timestampToken).Length; } - public PdfSignatureHandler(ISigner signer, PdfSignatureOptions options) - { - ArgumentNullException.ThrowIfNull(signer); - ArgumentNullException.ThrowIfNull(options); - - if (options.PageIndex < 0) - throw new ArgumentOutOfRangeException($"Signature page index cannot be negative."); - - this.signer = signer; - this.Options = options; - } - - public PdfSignatureHandler(ISigner signer, PdfSignatureOptions options, byte[] timestampToken) + public PdfSignatureHandler(ISigner signer, PdfSignatureOptions options, byte[]? timestampToken = null) { if (signer is null) throw new ArgumentNullException(nameof(signer)); From ab35a860fa1fd0be193e1825505ef0206d57ac71 Mon Sep 17 00:00:00 2001 From: Francesc Vila Date: Wed, 14 Feb 2024 12:49:22 +0100 Subject: [PATCH 3/3] fix: add timestamp to signed attribues --- .../src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs index d2702e2e..11ad750e 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DefaultSigner.cs @@ -35,7 +35,7 @@ public byte[] GetSignedCms(Stream stream, int pdfVersion, byte[]? timestampToken if (timestampToken != null) { AsnEncodedData timestampTokenAsnEncodedData = new AsnEncodedData(new Oid("1.2.840.113549.1.9.16.2.14"), timestampToken); - signer.UnsignedAttributes.Add(new CryptographicAttributeObject(new Oid("1.2.840.113549.1.9.16.2.14"), new AsnEncodedDataCollection(timestampTokenAsnEncodedData))); + signer.SignedAttributes.Add(new CryptographicAttributeObject(new Oid("1.2.840.113549.1.9.16.2.14"), new AsnEncodedDataCollection(timestampTokenAsnEncodedData))); } signedCms.ComputeSignature(signer, true);