Closed milouk closed 3 years ago
Quick Update:
I used this flag signer_key_usage={'digital_signature'},
and now it works. but Im having an issue with the timestamping. Do you have any idea of what could be wrong? I assume that without timestamping I cant have the document LTV enabled right?
File "/usr/local/lib/python3.7/site-packages/pyhanko/sign/signers.py", line 2230, in sign_pdf
timestamper.validation_paths(validation_context)
File "/usr/local/lib/python3.7/site-packages/pyhanko/sign/timestamps.py", line 150, in validation_paths
yield validator.validate_usage(set(), {"time_stamping"})
File "/usr/local/lib/python3.7/site-packages/certvalidator/__init__.py", line 193, in validate_usage
self._validate_path()
File "/usr/local/lib/python3.7/site-packages/certvalidator/__init__.py", line 108, in _validate_path
paths = self._context.certificate_registry.build_paths(self._certificate)
File "/usr/local/lib/python3.7/site-packages/certvalidator/registry.py", line 320, in build_paths
missing_issuer_name
certvalidator.errors.PathBuildingError: Unable to build a validation path for the certificate "Common Name: APED Qualified TSU A2 2020, Organization: HELLENIC PUBLIC ADMINISTRATION CERTIFICATION AUTHORITY, Organizational Unit: APED Time-Stamping Services, Country: GR" - no issuer matching "Common Name: APED Global Root CA, Organization: APED, Country: GR" was found
Hi milouk, thanks for checking in!
Your first issue is indeed due to the fact that pyHanko (by default) requires signer certificates to have the nonRepudiation key usage bit set. However, some CAs exclusively use the digitalSignature bit instead (whether that's an appropriate decision is a matter for debate). As a validator it is of course your prerogative to determine which key usages you consider appropriate, so you can override that default (and it seems that you've found out how that works, so that's OK).
As for your second issue: from the error message I'm seeing here, I guess that the time stamping authority that you're using is not supplying all certificates in its chain of trust. This isn't a hard requirement of the time stamping protocol, so that sometimes happens. What you can then do is try to track down the relevant certificate(s) yourself, and add them to the ValidationContext
you're using to handle signing/validating. I found a certificate with a subject line that matches the missing one here: http://pki.aped.gov.gr/ca/APEDRoot.crt. Chances are that adding that one to the ValidationContext
will do the trick.
Hope this helps, if anything's still unclear, feel free to ask. :)
EDIT: The cert I linked is self-signed, so you'll have to add it to the trusted certificates in the ValidationContext
for it to work. Whether you trust that certificate or not is something I can't decide for you, obviously.
Thanks for the very quick and detailed reply and for your research as well.
What you proposed indeed worked and I got the document signed but it still shows as "Signature is not LTV enabled" in Adobe. Am I missing something here?
Hard to tell just from that screenshot, but I'm 90% sure that that's because the TSA authorising the timestamp is untrusted in Acrobat. You can fix that on your own machine by importing the root certificate from earlier into Acrobat's trust store (it's somewhere deep in the settings menu), but that won't necessarily help if you want these signatures to be verifiable by others as well. Regardless, if marking the TSA as trusted makes the signature LTV enabled, then you'll know that that was the problem.
Is there a reason why you have to use this particular TSA? If the platform you're targeting requires this specific TSA I would simply mark the relevant certificate as trusted and move on, but otherwise you might be better off looking for a TSA that is more widely trusted (e.g. one authorised by a certificate authority on the AATL or EUTL).
So I added it to trusted certificates as you mentioned and now I got this
Is this considered an "LTV enabled" document although the "Rev 1" signature is not LTV enabled?
PS: We are using this TSA as its the Greek Governments official TSA. If we decide that the TSA is the issue here we will most probably contact them to also supply the rest certificates in the chain of trust
Interesting. Then it could be due to missing revocation information for one of the intermediate certificates, perhaps. Can you send me a signed dummy file at pyhanko.samples <at mark goes here>
mvalvekens.be?
Adobe's "LTV" criteria aren't officially documented, so this kind of thing is not always easy to debug.
EDIT: Or perhaps because the TSA's revocation info is itself missing, but the document timestamp is apparently LTV enabled, so that's less likely.
EDIT 2: You can also try debugging with pyHanko's pyhanko sign validate --ltv-profile pades-lta
command (supplied with the appropriate trust settings, follow the links here for more info). It should complain if it runs into certificates with incomplete revocation information. If that doesn't detect any problems, try adding --ltv-obsessive
.
Ah, wait, before you try all that: I noticed just now that you didn't add the allow_fetching=True
parameter to your validation context. That's another potential reason.
Hmm, perhaps I should have pyHanko log a warning if allow_fetching
is False
when embedding revocation info...
Ah, thought about it as I was reading through the ValidationContext
declaration but I thought it wouldn't make any difference. Im trying it now.
Edit: Unfortunately, it didn't make any difference, Im sending you the output in your email. Tomorrow morning I am going to try validating via CLI as you proposed.
Thanks again.
EDIT 2:
So I ran pyhanko sign validate --pretty-print --ltv-profile pades-lta --trust '/APEDRoot.cer' out.pdf
and got
2021-03-13 23:06:56,826 - pyhanko.sign.general - WARNING - The active key usage policy requires the key extensions non repudiation to be present.
2021-03-13 23:06:56,827 - pyhanko.sign.general - WARNING - Chain of trust validation for "deducted" failed.
==================
Field 1: Signature
==================
Signer info
-----------
Certificate subject: "deducted";
Organization Identifier: deducted;
Organization: deducted"
Certificate SHA1 fingerprint: deducted
Certificate SHA256 fingerprint: deducted
Trust anchor: "No path to trust anchor found."
The signer's certificate is untrusted.
Integrity
---------
The signature is cryptographically sound.
The signature does not cover the entire file.
All modifications relate to signature maintenance, and they appear to be compatible with the current document modification policy.
Signing time
------------
Signing time: 2021-03-13T22:49:38+00:00
The signing time is guaranteed by a time stamping authority.
TSA certificate subject: "Common Name: APED Qualified TSU A2 2020, Organization: HELLENIC PUBLIC ADMINISTRATION CERTIFICATION AUTHORITY, Organizational Unit: APED Time-Stamping Services, Country: GR"
TSA certificate SHA1 fingerprint: d82baeb2f7c86908f42cfbcee52c6e0db391ad09
TSA certificate SHA256 fingerprint: 99ad3e70552851b6cddfe164cfc5d57f92c18c02f1e27d68ffcb5d9912cd957c
TSA cert trust anchor: "Common Name: APED Global Root CA, Organization: APED, Country: GR"
The TSA certificate is trusted.
Seed value constraints
----------------------
There were no SV issues detected for this signature.
Bottom line
-----------
The signature is judged INVALID.
That said there is something probably wrong with the signing certificate eh?
Ooh, I get it now! I should have read your code more carefully.
You marked the signer's certificate as a trust root in the validation context. This is very likely not what you want: in almost all trust models based on X.509, trust roots are absolute, and in particular things like "revocation status" and so on don't apply to them, so pyHanko doesn't fetch revocation info for trust roots (because that wouldn't make any sense).
You'll want to instead mark the highest relevant certificate in the chain as a trust root here; any intermediates go under other_certs
. The signer's certificate doesn't need to be included in the validation context at all.
Fortunately, your cert seems to have been directly issued by a CA on the EUTL ("HARICA Qualified Legal Entities SubCA R3"), so marking that one as a trust root should work.
PS: after writing my previous comment I realised that the CLI won't help you in this case, since there's no way to get rid of the nonRepudiation key usage requirement from the CLI. That's on my roadmap of issues to fix, though.
Finally, this worked. Matthias thank you very much for your patience and for your help. I learned a lot. Cheers.
Hello Matthias,
First of all thanks (I am also at GRNET where we implement signing / validation services for the Greek public sector). Just a note for the non-repudiation bit. I think it is important to make it configurable, in order for the signatures to be compliant with the ETSI EN 319 412-2, section 4.3.2 (Key Usage) standard; there you can see the permitted combinations of Key Usage settings. The notes are particularly interesting.
In practice, non-repudiation is used with Qualified Electronic Signatures, not with Advanced Signatures.
Best Regards,
Panos.
Hi Panos,
Thanks for that! I was aware of the difference in semantics between digitalSignatures and nonRepudiation from X.509 / RFC 5280, but I didn't know of this specific call-out in the ESI standard.
Key usage constraints are already fully configurable in the API, but you've convinced me to make getting that into the CLI a priority (esp. for validation). It's not a lot of work---just a matter of exposing the right settings; I should be able to take care of that by the time 0.5.0 is released (which should be soon).
Thanks!
Dear Matthias
about signed pdf document validation I am stucking in "The signer's certificate is untrusted." error(same as above). I'm sure I'm setting the right one (root and int CA's). I am also using signer_key_usage={'digital_signature'} at signing stage.
$ python3 verify.py The active key usage policy requires at least one of the key usage extensions non repudiation to be present. Chain of trust validation for Common Name: xxxxx failed.
If there are any other possibilities, could you please advise?
root_cert = load_cert_from_pemder('/home/ubuntu/ws/hanko/class1-root-only.crt')
int_cert = load_cert_from_pemder('/home/ubuntu/ws/hanko/intca.crt')
tsa_root_cert = load_cert_from_pemder('/home/ubuntu/ws/hanko/tsa_cacert.pem')
vc = ValidationContext(
trust_roots=[root_cert],
other_certs=[int_cert]
)
ts_vc = ValidationContext(
trust_roots=[tsa_root_cert]
)
with open('signed-13.pdf', 'rb') as doc:
r = PdfFileReader(doc)
sig = r.embedded_signatures[0]
status = validate_pdf_signature(sig, vc, ts_vc)
print(status.pretty_print_details())
Hi @inui-sdt2 !
You need to tell the validator to accept the digital_signature
key usage as well. Right now, pyHanko takes a conservative default position by rejecting all signer certificates that don't have the nonRepudiation/contentCommitment key usage bit set.
You can change that behaviour using the key_usage_settings
argument to validate_pdf_signature
. See here:
In your case, you probably want to pass in KeyUsageConstraints(key_usage={'digital_signature'})
.
I should probably add a few words to the library usage guide to make this part of the API a bit easier to find.
PS: If you have any follow-up questions, could you please create a thread in the discussion space instead? That makes it easier to keep the issue tracker somewhat organised :).
I had the same problem (Signature is not LTV enabled and will expire after xxx) using Sectigo's QTSA server. Fortunately, the comments in this issue helped me to configure the trust root/other certs properly.
trust_root_paths = [
'ca/sectigo/Sectigo-Qualified-Time-Stamping-Root-R45.crt', # https://crt.sh/?id=3547980726
'ca/sectigo/Sectigo-Qualified-Time-Stamping-CA-R35.crt', # https://crt.sh/?id=3547998112
]
other_certs_paths = [
'ca/sectigo/Sectigo-Qualified-Time-Stamping-Signer-3.crt', # https://crt.sh/?id=9297382517
]
trust_roots = [ load_cert_from_pemder(each) for each in trust_root_paths ]
other_certs = [ load_cert_from_pemder(each) for each in other_certs_paths ]
My issue was using Sectigo-Qualified-Time-Stamping-Signer-3.crt
as trust_root, but it have to go in other_certs!
Hello Matthias,
First of all great work you have done with pyHanko.
Describe the bug We are planning on using pyHanko on a large scale for signed pdf docs for the Greek public sector. For that we need the signed documents to be LTV enabled. I have tried to make an initial implementation but when I use the param
embed_validation_info
I get anpyhanko.sign.general.SigningError: ("The signer's certificate could not be validated", InvalidCertificateError('The X.509 certificate provided is not valid for the purpose of non repudiation'))
error.To Reproduce The code I have written so far looks like this
Where the
certificate_filename
is a p12 signing certificate andpdf_buffer
is the document I want to sign in BytesIOExpected behavior In the end I expect a signed document that when opened with Adobe Acrobat Reader it is listed as "LTV enabled"
Screenshots
Environment (please complete the following information):
Additional context The same issue exists in both 0.4.0 as well as 0.5.0-dev1
I have contaced HARICA which is the Hellenic Academic & Research Institutions Certification Authority and they told me that non-repudiaton has nothing to do with LTV and it shouldn't be needed to LTV enable a document.