PDFsharp icon indicating copy to clipboard operation
PDFsharp copied to clipboard

Support of Digital Signatures (PKCS#7) + TSA timestamp

Open julienrffr opened this issue 1 year ago • 40 comments

Adding:

  • ability to sign PDF documents, using PKCS#7 format.
  • ability to timestamp the signature with a TSA (via the DefaultSigner).

I tried to do minimal changes in existing classes (only PdfDocument class has been modified to add BeforeSave/AfterSave events, and PdfString in order to track position in stream). PdfString could have not been modified if it was not sealed.

signature-screenshot

This is a port from https://github.com/empira/PDFsharp-1.5/pull/11 but with way smaller footprint.

Usage example (files in demo-signature.zip):

  1. Change image/pdf/certificate paths in Program.cs, change certificate password in Program.cs
  2. Run Program

demo-signature.zip

Let me know any suggestions.

NB: this feature is available in PDFsharp-extended nuget package

julienrffr avatar Oct 20 '23 14:10 julienrffr

Signature computation is working well, Signature seems to be correctly added to the Sig dictionary (hex string of signature decodes properly in https://lapo.it/asn1js/), BUT when opening the document in Adobe Reader, I still get this error message when clicking on signature:

` Error during signature verification.

Signature contains incorrect, unrecognized, corrupted or suspicious data. Support Information: SigDict /Contents illegal data `

I don't get to know what is causing the issue.

Btw I tried to sign with an auto-signed certificate.

julienrffr avatar Oct 24 '23 13:10 julienrffr

Signature feature is now working

Signature computation is working well, Signature seems to be correctly added to the Sig dictionary (hex string of signature decodes properly in https://lapo.it/asn1js/), BUT when opening the document in Adobe Reader, I still get this error message when clicking on signature:

` Error during signature verification.

Signature contains incorrect, unrecognized, corrupted or suspicious data. Support Information: SigDict /Contents illegal data `

I don't get to know what is causing the issue.

Btw I tried to sign with an auto-signed certificate.

With commit 80cdf02d7e97dc6d1ccae3a25f949d4278188391 I fixed the issue: the signature field was not properly added to the AcroForm array.

Now that it is in the AcroForm, Adobe properly detects it and display a more explicit message about my signature issue. By using a PDF/signature checker like https://tte.kominfo.go.id/verifyPDF, I was able to see that my signature was problematic because issuer is not recognized (I tested with self-signed certificate and another certificate not in recognized authorities). But it should work (confirming soon) with a recognized authority's certificate.

There may be a bug on AcroForm.Fields

@ThomasHoevel @PDFsharp-Team I had to make PdfAcroField.PdfAcroFieldCollection's constructor internal instead of private in order to instanciate it. Indeed when accessing document.Catalog.AcroForm.Fields, _fields is null and it tries to create an instance via VCF.CreateIndirect parameter, but Reflection then fails to find a constructor. Could you help on this particular commit 80cdf02d7e97dc6d1ccae3a25f949d4278188391 please? This may be a bug? Or there may be a better way to instanciate it.

julienrffr avatar Oct 24 '23 15:10 julienrffr

Last thing to handle now is the signature's appearance that seems to not be displayed properly.

julienrffr avatar Oct 24 '23 15:10 julienrffr

Signature feature is now working

Signature computation is working well, Signature seems to be correctly added to the Sig dictionary (hex string of signature decodes properly in https://lapo.it/asn1js/), BUT when opening the document in Adobe Reader, I still get this error message when clicking on signature: Error during signature verification. Signature contains incorrect, unrecognized, corrupted or suspicious data. Support Information: SigDict /Contents illegal data I don't get to know what is causing the issue. Btw I tried to sign with an auto-signed certificate.

With commit 80cdf02 I fixed the issue: the signature field was not properly added to the AcroForm array.

Now that it is in the AcroForm, Adobe properly detects it and display a more explicit message about my signature issue. By using a PDF/signature checker like https://tte.kominfo.go.id/verifyPDF, I was able to see that my signature was problematic because issuer is not recognized (I tested with self-signed certificate and another certificate not in recognized authorities). But it should work (confirming soon) with a recognized authority's certificate.

There may be a bug on AcroForm.Fields

@ThomasHoevel @PDFsharp-Team I had to make PdfAcroField.PdfAcroFieldCollection's constructor internal instead of private in order to instanciate it. Indeed when accessing document.Catalog.AcroForm.Fields, _fields is null and it tries to create an instance via VCF.CreateIndirect parameter, but Reflection then fails to find a constructor. Could you help on this particular commit 80cdf02 please? This may be a bug? Or there may be a better way to instanciate it.

Hi, I tried with a trusted certificate but get the same error

ibrahimAliTecman avatar Oct 26 '23 10:10 ibrahimAliTecman

Hi, I tried with a trusted certificate but get the same error

Does the certificate has a root certificate (+ intermediate certificate) + end user certificate? Is the root authority part of Adobe's AATL list?

julienrffr avatar Oct 26 '23 12:10 julienrffr

Hi, I tried with a trusted certificate but get the same error

Does the certificate has a root certificate (+ intermediate certificate) + end user certificate? Is the root authority part of Adobe's AATL list?

Yes the root is part of the Adobe AATL, the certificate was created using sectigo

ibrahimAliTecman avatar Oct 26 '23 13:10 ibrahimAliTecman

Hi, I tried with a trusted certificate but get the same error

Did you try in DEBUG or RELEASE build? I just pushed a fix for Release mode.

julienrffr avatar Oct 30 '23 09:10 julienrffr

Hi, I tried with a trusted certificate but get the same error

Did you try in DEBUG or RELEASE build? I just pushed a fix for Release mode.

Was using DEBUG, below is the adobe errors.

Screenshot 2023-10-30 112925

ibrahimAliTecman avatar Oct 30 '23 11:10 ibrahimAliTecman

Was using DEBUG, below is the adobe errors.

Hi @ibrahimAliTecman, I fixed the issue on the byte range computation that was caused by extra spaces added in DEBUG mode due to Verbose writer layout.

Please test again and let me know, signature should be fine now.

Only thing remaining to do is the signature appearance to take care of. I'll do that in the next days.

julienrffr avatar Oct 31 '23 11:10 julienrffr

Was using DEBUG, below is the adobe errors.

Hi @ibrahimAliTecman, I fixed the issue on the byte range computation that was caused by extra spaces added in DEBUG mode due to Verbose writer layout.

Please test again and let me know, signature should be fine now.

Only thing remaining to do is the signature appearance to take care of. I'll do that in the next days.

Hi, Awesome! it is working now. Verifies signature correctly. Thank you!

ibrahimAliTecman avatar Oct 31 '23 12:10 ibrahimAliTecman

I've just pushed 496c553 that allows to define a custom appearance for the signature field.

Since everything is working fine now, I'm setting the pr as ready for review. I would love to get remarks or help from @PDFsharp-Team / @ThomasHoevel.

I'm totally willingful to adjust code so it could fit better in PDFsharp lib, if any chances that something would be merged some day. In the meantime, I consider publishing a nuget package with all our additional features, based on branch https://github.com/KDS/PDFsharp/tree/pdfsharp-extended

Note: known limitation is that currently it can only sign unsigned documents.

julienrffr avatar Oct 31 '23 15:10 julienrffr

I've just pushed 496c553 that allows to define a custom appearance for the signature field.

Since everything is working fine now, I'm setting the pr as ready for review. I would love to get remarks or help from @PDFsharp-Team / @ThomasHoevel.

I'm totally willingful to adjust code so it could fit better in PDFsharp lib, if any chances that something would be merged some day. In the meantime, I consider publishing a nuget package with all our additional features, based on branch https://github.com/KDS/PDFsharp/tree/pdfsharp-extended

Note: known limitation is that currently it can only sign unsigned documents.

The signature is appearing blank still with the latest commit, is there anything that needs to be changed in the calling method?

ibrahimAliTecman avatar Oct 31 '23 15:10 ibrahimAliTecman

The signature is appearing blank still with the latest commit, is there anything that needs to be changed in the calling method?

Please provide your code and the generated pdf.

Here is a working Program for me, with bith default appearance and custom appearance: signature.txt

julienrffr avatar Oct 31 '23 16:10 julienrffr

Hey, would it be hard to implement :

a) multiple signatures on a file (with only one open/save) b) signing an already signed file ?

I've tried do make it work for several hours without success. Even by changing the name of the signature so they are all unique, it does not seem to do anything. An incremental save system seems the way to go.

Do you have anything almost working so I can try to help ?

Hraezvelg avatar Nov 03 '23 16:11 Hraezvelg

Hey, would it be hard to implement :

a) multiple signatures on a file (with only one open/save) b) signing an already signed file ?

I've tried do make it work for several hours without success. Even by changing the name of the signature so they are all unique, it does not seem to do anything. An incremental save system seems the way to go.

Do you have anything almost working so I can try to help ?

Hi,

a) applying multiple signatures on a file in a single open/save seems at least complicated, not sure if it's feasable. b) signing an already signed file is something we want too, but right now we do not have time to implement it. I have no draft for this sorry, feel free to open a pull request to https://github.com/KDS/PDFsharp/tree/signature-feature

NB: signature feature is now available in PDFsharp-extended nuget package

julienrffr avatar Nov 03 '23 16:11 julienrffr

Hi! Is there any ETA on when this PR will be merged?

kotlerman avatar Dec 18 '23 12:12 kotlerman

This PR will not be merged. There is no ETA for the signature feature yet.

TH-Soft avatar Dec 18 '23 13:12 TH-Soft

This PR will not be merged. There is no ETA for the signature feature yet.

Any reasons why? Or some feedback at least? Do you plan to implement your own signature feature internally?

julienrffr avatar Dec 18 '23 14:12 julienrffr

Boss does not like the changes to the String class and wants to come up with a "better" solution.

ThomasHoevel avatar Dec 18 '23 14:12 ThomasHoevel

If you don't have any ETA to adjust this MR with the PdfString class, can you please advise about those reservations ? Maybe we can help ?

We have used this feature for a while, as our customers heavily need it, and would love it to become mainstream. At the same time there is a lot of eagerness from other users in this MR.

If there's anything we can adjust to meet your requirements, let us know.

Duncan-Idaho avatar Dec 18 '23 15:12 Duncan-Idaho

ok, I tried this PR, and it worked for me, but I have a little different situation. what if want to make the box ready to sign and just build a PDF ready to sign, like the attached example? Any idea? Output.pdf

aamir-munir avatar Dec 22 '23 04:12 aamir-munir

ok, I tried this PR, and it worked for me, but I have a little different situation. what if want to make the box ready to sign and just build a PDF ready to sign, like the attached example? Any idea? Output.pdf

You'd have to do something similar to what is done in method AddSignatureComponents (in PdfSignatureHandler.cs), minus the placeholders for actual signature tokens. This means creating the annotation/field and referencing it in the AcroForm dictionary.

@aamir-munir

julienrffr avatar Jan 02 '24 08:01 julienrffr

Added the ability (for the DefaultSigner only for the moment) to attach a timestamp from a TSA to the signature. This is one step closer to PADES requirements.

julienrffr avatar Feb 23 '24 16:02 julienrffr

Hi, Why there is not adding of ContactInfo filed for signature properties in GetSignatureDictionary method? At the same time this option is in PdfSignatureOptions.

ivvitikhonov avatar Mar 26 '24 14:03 ivvitikhonov

Hey, would it be hard to implement : a) multiple signatures on a file (with only one open/save) b) signing an already signed file ? I've tried do make it work for several hours without success. Even by changing the name of the signature so they are all unique, it does not seem to do anything. An incremental save system seems the way to go. Do you have anything almost working so I can try to help ?

Hi,

a) applying multiple signatures on a file in a single open/save seems at least complicated, not sure if it's feasable. b) signing an already signed file is something we want too, but right now we do not have time to implement it. I have no draft for this sorry, feel free to open a pull request to https://github.com/KDS/PDFsharp/tree/signature-feature

NB: signature feature is now available in PDFsharp-extended nuget package

Update on the 'signing an already signed document' question: To accomplish this, we would first need to be able to work with 'incremental updates'. Right now this is not supported by pdfSharp as far as I know. This is a requirement for applying a second signature on a file without invalidating the first signature.

julienrffr avatar Apr 30 '24 15:04 julienrffr

Julien, thank you for your work! Digital Signatures are the number one on our to-do list for PDFsharp 6.2 Preview 1.

StLange avatar Jun 07 '24 15:06 StLange

Maybe I'm doing something wrong, but I got the PR using gh pr checkout 48 and cannot get it to compile. Is the PR compatible with .NET 4.7.2, .NET 6, and .NETstandard 2.0?

Does anybody have some useful tips for me?

image

3>D:\THHO\Repos\PDFsharp_PR\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\DefaultSigner.cs(7,18,7,22): error CS0234: The type or namespace name 'Http' does not exist in the namespace 'System.Net' (are you missing an assembly reference?) 3>D:\THHO\Repos\PDFsharp_PR\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\DefaultSigner.cs(9,36,9,40): error CS0234: The type or namespace name 'Pkcs' does not exist in the namespace 'System.Security.Cryptography' (are you missing an assembly reference?) 3>D:\THHO\Repos\PDFsharp_PR\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\DefaultSigner.cs(56,53,56,62): error CS0246: The type or namespace name 'SignedCms' could not be found (are you missing a using directive or an assembly reference?) 3>Done building project "PdfSharp-wpf.csproj" -- FAILED. 2>D:\THHO\Repos\PDFsharp_PR\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\DefaultSigner.cs(7,18,7,22): error CS0234: The type or namespace name 'Http' does not exist in the namespace 'System.Net' (are you missing an assembly reference?) 2>D:\THHO\Repos\PDFsharp_PR\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\DefaultSigner.cs(9,36,9,40): error CS0234: The type or namespace name 'Pkcs' does not exist in the namespace 'System.Security.Cryptography' (are you missing an assembly reference?) 2>D:\THHO\Repos\PDFsharp_PR\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\DefaultSigner.cs(56,53,56,62): error CS0246: The type or namespace name 'SignedCms' could not be found (are you missing a using directive or an assembly reference?) 2>Done building project "PdfSharp-gdi.csproj" -- FAILED. 2>PdfSharp-gdi -> D:\THHO\Repos\PDFsharp_PR\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp-gdi\bin\Debug\net6.0-windows\PdfSharp-gdi.dll 2>Done building project "PdfSharp-gdi.csproj". 5>------ Build started: Project: MigraDoc.RtfRendering-gdi, Configuration: Debug Any CPU ------ 6>------ Build started: Project: PdfSharp.Charting-gdi, Configuration: Debug Any CPU ------ 7>------ Build started: Project: PdfSharp.Quality-gdi, Configuration: Debug Any CPU ------ 4>D:\THHO\Repos\PDFsharp_PR\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\DefaultSigner.cs(69,27,69,50): error CS0103: The name 'Rfc3161TimestampRequest' does not exist in the current context 4>D:\THHO\Repos\PDFsharp_PR\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\DefaultSigner.cs(76,31,76,52): error CS0246: The type or namespace name 'ReadOnlyMemoryContent' could not be found (are you missing a using directive or an assembly reference?) 4>Done building project "PdfSharp.csproj" -- FAILED.

ThomasHoevel avatar Jun 10 '24 15:06 ThomasHoevel

Maybe I'm doing something wrong, but I got the PR using gh pr checkout 48 and cannot get it to compile. Is the PR compatible with .NET 4.7.2, .NET 6, and .NETstandard 2.0?

Does anybody have some useful tips for me?

Hi @ThomasHoevel , Glad to hear that signatures are in the pipe!

Indeed, I've just synchronized this pull request with your new release 6.1.0 (which now includes netstandard support) and I started to work on the compatibility because the last feature I introduced recently (signature timestamp) is not compatible yet.

To compile, for now you could just comment the method AddTimestampFromTSAAsync and its call in \src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\DefaultSigner.cs (and removing the not-compatible usings of course).

I'm working on this compatibility for this recent feature but it's not my top priority right now. I'll try to push something that compiles out-of-the-box ASAP.

In the meantime feel free to contact me for any support (here, via mail or Teams or Zoom).

julienrffr avatar Jun 11 '24 07:06 julienrffr

Thanks for the feedback. I already figured out that TSA is the problem and used "#if NET6_0_OR_GREATER" to exclude that.

Now I get a warning I cannot yet resolve: Found conflicts between different versions of "System.Memory" that could not be resolved. There was a conflict between "System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" and "System.Memory, Version=4.0.1.2

It's a company policy that projects compile without warnings.

Sooner or later I will also have to address the following compile-time warnings: 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.AcroForms\PdfSignatureField.cs(18,18,18,35): warning CS8618: Non-nullable property 'CustomAppearanceHandler' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.AcroForms\PdfSignatureField.cs(22,18,22,35): warning CS8618: Non-nullable property 'CustomAppearanceHandler' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\RangedStream.cs(79,20,79,114): warning CS8603: Possible null reference return. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\RangedStream.cs(106,35,106,47): warning CS8602: Dereference of a possibly null reference. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\PdfSignatureOptions.cs(11,45,11,62): warning CS8618: Non-nullable property 'AppearanceHandler' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\PdfSignatureOptions.cs(12,23,12,34): warning CS8618: Non-nullable property 'ContactInfo' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\PdfSignatureOptions.cs(13,23,13,31): warning CS8618: Non-nullable property 'Location' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\PdfSignatureOptions.cs(14,23,14,29): warning CS8618: Non-nullable property 'Reason' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\PdfSignatureHandler.cs(52,41,52,63): warning CS8622: Nullability of reference types in type of parameter 'sender' of 'void PdfSignatureHandler.AddSignatureComponents(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes). 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\PdfSignatureHandler.cs(53,40,53,64): warning CS8622: Nullability of reference types in type of parameter 'sender' of 'void PdfSignatureHandler.ComputeSignatureAndRange(object sender, PdfDocumentEventArgs e)' doesn't match the target delegate 'EventHandler<PdfDocumentEventArgs>' (possibly because of nullability attributes). 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\PdfSignatureHandler.cs(62,16,62,35): warning CS8618: Non-nullable field 'signatureFieldContentsPdfString' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\PdfSignatureHandler.cs(62,16,62,35): warning CS8618: Non-nullable field 'signatureFieldByteRangePdfArray' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\PdfSignatureHandler.cs(62,16,62,35): warning CS8618: Non-nullable property 'Document' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\PdfSignatureHandler.cs(88,39,88,93): warning CS8602: Dereference of a possibly null reference. 3>D:\THHO\Repos\PDFsharp\src\foundation\src\PDFsharp\src\PdfSharp\Pdf.Signatures\DefaultSigner.cs(97,17,97,57): warning CS8602: Dereference of a possibly null reference.

ThomasHoevel avatar Jun 11 '24 07:06 ThomasHoevel

Thanks for the feedback. I already figured out that TSA is the problem and used "#if NET6_0_OR_GREATER" to exclude that.

Now I get a warning I cannot yet resolve: Found conflicts between different versions of "System.Memory" that could not be resolved. There was a conflict between "System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" and "System.Memory, Version=4.0.1.2

It's a company policy that projects compile without warnings.

I'll try to take a look on that also when I have some time.

By the way, do you have an idea of how you'll introduce signatures feature? Basing on this PR, or re-writing entirely from scratch?

julienrffr avatar Jun 11 '24 08:06 julienrffr