Repository navigation
TSA timestamping always fails with JSignPdf: --tsa-hash-algorithm is never passed, SHA-1 is used #8145
Description
Activity
There is an important detail to consider here.
In LibreSign 15 /
stable35and also inmainbranch, TSA settings were migrated to the Policies system. Because of this, this fix should not readtsa_hash_algorithmdirectly fromappConfig. It should be part of the TSA policy settings and exposed together with the other TSA options in the frontend, so it keeps working correctly in multi-tenant scenarios.I also checked JSignPdf 2.3.0, which is the version currently used by LibreSign. The report is correct:
-tsh/--tsa-hash-algorithmexists and the default is SHA-1 when it is not provided.JSignPdfHandleralready has logic to resolve the signature hash algorithm. Maybe this logic should be moved to a small dedicated class, with isolated unit tests for both signature and TSA hash behavior. I would not directly reuse the same result for TSA, because the current logic also considers PDF version compatibility and may select SHA-1 for old PDFs.The point about logging also makes sense:
checkTsaError()can throw before the original JSignPdf error is logged, so this should be reviewed too.- addedgood first issueGood for newcomersGood for newcomersbackendBackend workBackend workfrontendFrontend workFrontend workphpWork involving PHP codeWork involving PHP codejavascriptWork involving JavaScript codeWork involving JavaScript code
on Aug 31, 2026 Thanks for the quick confirmation and for checking JSignPdf 2.3.0 independently.
On the Policies system — agreed, and I withdraw the
appConfigsnippet from my report. It was
written against 13.3.0 (Nextcloud 33), where TSA options live inappConfig; reading
tsa_hash_algorithmfrom there would be the wrong shape going forward. Exposing it alongside the
other TSA options in the policy settings is clearly the right place.On not reusing the signature hash for the TSA — that is a subtlety I had not anticipated, and
it is worth recording in the issue: if the existing resolution can deliberately select SHA-1 for
older PDF versions, then deriving the TSA hash from it would silently reintroduce the very failure
reported here for any document that happens to trigger that path. That argues for a genuinely
separate TSA hash setting rather than a derived value, so a compatibility choice about the document
cannot leak into the timestamp request.Data point: the
PhpNativeworkaround, verified in productionFor anyone landing here before a fix, switching the signature engine works:
occ config:app:set libresign signature_engine --value=PhpNativeDefaultTimestampOptionsProviderdefaults tosha256, so Certum accepts the request. Verified on
a real signed contract by extracting both signatures from the PDF and inspecting them with
opensslalone (pdfsigis not available in the Nextcloud AIO image):/SubFilter/adbe.pkcs7.detached+/SubFilter/ETSI.RFC3161+/DocMDPall present;- timestamp token:
Status: Granted,Hash Algorithm: sha256,Policy OID: 1.2.616.1.113527.2.5.1.11, signed byCN = Certum Timestamp 2026, O = Asseco Data Systems S.A.; openssl smime -verifyagainst the internal root CA:Verification successful, and flipping a
single bit in the covered byte range turns it intodigest failure, so integrity detection
behaves as expected.
One caveat on that workaround, so nobody adopts it blind:
PhpNativeHandlerrenders the
visible signature stamp with/Type1 /HelveticaandescapePdfText()performs no UTF-8 to WinAnsi
conversion, so non-ASCII characters insignature_text_templateare mangled — a French template
withSigné/Émetteurcame out asSign^/^ metteur. Easy to work around with an ASCII-only
template, but it means the two engines are not interchangeable in both directions. Happy to open
that as a separate issue if it is not already tracked.One question about the migration
Does the move to Policies plan to carry over the existing
tsa_url/tsa_auth_type/
tsa_policy_oidappConfigvalues, or should instances configured on 13.x/14.x expect to
reconfigure the TSA after upgrading? Asking because a silently droppedtsa_urlwould produce
signatures without a timestamp, which is not visible unless the resulting PDF is inspected.Thanks for checking this in detail.
About the migration: yes, the existing TSA configuration is migrated automatically. The migration reads the legacy
tsa_url,tsa_policy_oid,tsa_auth_type,tsa_usernameandtsa_passwordvalues and moves them to the new TSA policy configuration. We also have unit tests covering this migration.So existing instances should not need to configure the TSA again after the upgrade.
One important note about LibreSign 15 / Nextcloud 35: some API contracts have changed. If you use the LibreSign API in an external integration, please validate your integration before upgrading.
About the non-ASCII problem with
PhpNativeHandler: this is outside the scope of this TSA issue. Please open a separate issue for it so we can investigate the rendering flow independently.Attention!!!
To clarify for anyone who wishes to work on this issue, here is what needs to be done:
7 remaining items
- added 5 commits that reference this issue
on Sep 6, 2026 - moved this from 0. Backlog to 4. to release in LibreSign Roadmap
on Sep 6, 2026 - added 2 commits that reference this issue
on Sep 6, 2026 - added 6 commits that reference this issue
on Sep 12, 2026
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Projects
- StatusShow more project fieldsNo status
Describe the bug
When a TSA is configured, signing with the JSignPdf engine always fails against any TSA that
rejects SHA-1 — which is every serious authority today.
JSignPdfHandler::getTsaParameters()passes only--tsa-server-url,--tsa-policy-oidand theauthentication options. It never passes
--tsa-hash-algorithm, so JSignPdf falls back to itsdefault of SHA-1 for the timestamp query. Certum and DigiCert answer
HTTP 400.Note that the admin setting "Signature hash algorithm" (
-ha) does not help: it applies tothe document signature, not to the TSA query. Both parameters are independent.
Two things make this hard to diagnose:
checkTsaError()triggers on any error containingTSAClientBouncyCastle,UnknownHostExceptionorInvalid TSA, and reports"Timestamp Authority (TSA) service error. Check TSA endpoint and DNS/network/firewall
connectivity from this server." Connectivity is fine — the request is simply rejected.
checkTsaError()throws before the$this->logger->error(...)line insignWrapper(), so nothing about the actual cause reachesnextcloud.log. I only found it by running JSignPdf by hand.To reproduce
http://time.certum.pl.JSignPdf).Connectivity is not the issue — a plain RFC 3161 exchange from inside the Nextcloud container
succeeds:
Running JSignPdf directly shows the real cause:
Adding
-tsh SHA-256makes it succeed:Measured against three TSAs, same document, same key:
-tsh)http://time.certum.plhttp://timestamp.digicert.comhttps://freetsa.org/tsrFreeTSA is the only one still accepting SHA-1, so it is currently the only TSA that works with the
JSignPdf engine. Falling back to it means SHA-1 timestamps from a service with no availability
commitment, which undermines the evidentiary value timestamping is meant to add.
Expected behavior
The timestamp query should use a modern digest (SHA-256 by default), and ideally follow the
configured signature hash algorithm.
Proposed fix
Add the option in
getTsaParameters(), defaulting to SHA-256 — the wrapper passes the optionstring through unchanged, so no
jsignpdf-phpchange is required:Two secondary improvements would have saved a lot of diagnosis time:
checkTsaError()throws;HTTP 400, avoid pointing at DNS/firewall — the server was reached.Relation to #8068
This is independent of the JSignPdf 3.x migration and does not need to wait for it: the option
already exists in JSignPdf 2.3.0 (
-tsh, --tsa-hash-algorithm), and the fix lives entirely inJSignPdfHandler, not in the wrapper.It is however directly relevant to the checklist in #8068 — "confirm that the current JSignPdf
options still work correctly, including [...] hash algorithm, TSA". This is a concrete case where
a TSA option is currently missing rather than merely changed, and it would be worth covering in the
tests added there.
Workaround
Switch the signature engine to
PhpNative, whose timestamp provider defaults tosha256(
DefaultTimestampOptionsProvider). Verified working against Certum:Environment
nextcloud-aio-nextcloud)