PDFsharp
PDFsharp copied to clipboard
Support of Digital Signatures (PKCS#7) + TSA timestamp
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.
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):
- Change image/pdf/certificate paths in Program.cs, change certificate password in Program.cs
- Run Program
Let me know any suggestions.
NB: this feature is available in PDFsharp-extended nuget package
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.
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.
Last thing to handle now is the signature's appearance that seems to not be displayed properly.
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
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?
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
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.
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.
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.
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!
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.
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?
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
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 ?
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
Hi! Is there any ETA on when this PR will be merged?
This PR will not be merged. There is no ETA for the signature feature yet.
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?
Boss does not like the changes to the String class and wants to come up with a "better" solution.
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.
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
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
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.
Hi,
Why there is not adding of ContactInfo filed for signature properties in GetSignatureDictionary
method? At the same time this option is in PdfSignatureOptions
.
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.
Julien, thank you for your work! Digital Signatures are the number one on our to-do list for PDFsharp 6.2 Preview 1.
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?
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.
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).
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.
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?