Digitální podpis PDF v PHP

kejhal

Digitální podpis PDF v PHP
« kdy: 17. 10. 2015, 17:07:58 »
Zdravím,

už nějakou dobu se trápím s digitálním podpisem souboru, repsektive PDFka.. Máme certifikát zakoupený na ssls.cz, přesněji tento digitální podpis https://www.ssls.cz/id/professional.html.

Všechno se odehrává v průběhu nějakého php procesu. Používám na to funkci openssl_pkcs7_sign(), pro ukázku kód vypadá přesně takto:

Kód: [Vybrat]
openssl_pkcs7_sign($path,
    $this->debtor->getPath($this->uploadDir . '/signed.pdf'),
    'file://' . realpath('_cesta_/signcert.pem')),
    'file://' . realpath('_cesta_/private.pem')),
    []
);
Cesty jsou generované na 100% správně.

Soukromý klíč jsem stáhl v administraci ssls.cz ve formátu .pfx a veřejný .cer binární a .pem plain.
Soukromý klíč jsem převedl z pkcs12 (.pfx) do pkcs7 (.pem) pomocí příkazu
Kód: [Vybrat]
openssl pkcs12 -in filename.pfx -nocerts -nodes -out key.pem
V tuto chvíli, když vemu veřejný .pem plain, pojmenovaný jako signcert.pem a privátní .pem pojmenovaný private.pem, tak mi server vrátí "openssl_pkcs7_sign(): error getting private key".

Plno lidí má tenhle problém spojený se špatnou cestou, ta je opravená právě pomocí realpath(), ale i tak nejsem schopný soubor podepsat..

Kdyby se tady našel někdo, kdo už s tím zkušenost měl nebo by ho napadl jakýkoli způsob, budu velmi rád.

Díky :)
« Poslední změna: 18. 10. 2015, 23:39:16 od Petr Krčmář »


Fantomas

Re:PHP - Digitální podpis pdf souboru
« Odpověď #1 kdy: 17. 10. 2015, 20:12:36 »
openssl pkcs12 -in filename.pfx -nocerts -nodes -out key.pem
Kdyz pouzivas funkci openssl_pkcs7_sign, nema tam byt pri konverzi openssl pkcs7 ...?

TTTTTTT

Re:PHP - Digitální podpis pdf souboru
« Odpověď #2 kdy: 17. 10. 2015, 22:11:30 »
Nemůže to být o předponě file://? V příkladu na http://php.net/manual/en/function.openssl-pkcs7-sign.php není pro certifikát použitá. Zkusil bych privátní klič vygenerovat s heslem (-nodes) a pak použít volání z toho přikladu. Funguje to, pokud to podepíšeš pomocí openssl?
Kód: [Vybrat]
openssl cms -sign -signer sign.pemsign.pem obsahuje signcert.pem + private.pem.

Jenda

Re:PHP - Digitální podpis pdf souboru
« Odpověď #3 kdy: 17. 10. 2015, 22:22:23 »
Nemůže to být o předponě file://?
Nemůže, tak je přece 100% správně ;D

Je k té funkci nějaký funkční example s jejich/ukázkovými klíči? Vypadají ty klíče „podobně“? Co na to řekne openssl rsa -noout -text -in bla.key nebo openssl x509 -noout -text -in bla.key?

Re:PHP - Digitální podpis pdf souboru
« Odpověď #4 kdy: 17. 10. 2015, 23:04:34 »
Mně je tam tedy podezřelý ten převod privátního klíče do PKCS7. PKCS7 je formát uložená zpráv, tj. např. šifrovaný/podepsaný e-mail, případně se v něm ukládají sady certifikátů (buď certifikační cesta nebo CRL). Ale že by v PKCS7 byl uložen privátní klíč, to se mi moc nezdá.

Nicméně ten příkaz, který jste použil, podle mne jenom z PFX souboru extrahoval privátní klíč a uložil ho nešifrovaný do souboru key.pem. Pokud jste ten soubor pak přejmenoval na private.pem a máte ho uložený ve správném umístění, mělo by to být v pořádku.

To realpath() vám vrací správnou cestu?

Jinak dokumentace té funkce openssl_pkcs7_sign je opravdu fascinující. Nicméně ve všech příkladech tam v parametru pro privátní klíč předávají pole, ve kterém je na první pozici cesta k souboru s klíčem a na druhém heslo ke klíči. Zkuste si ten privátní klíč vyexportovat s nějakým heslem a použít tuhle variantu. Některé knihovny bez hesla neumějí pracovat.


kejhal

Re:PHP - Digitální podpis pdf souboru
« Odpověď #5 kdy: 18. 10. 2015, 16:09:08 »
Všechno vrací správné cesty, několikrát kontrolované, pro jistotu i teď znovu a cesty jsou na 110% správné.
Ta předpona file:// by tam být měla, podle všech článků na stackoverflow, kdy to lidem pomohlo vyřešit problém se stejným errorem.
Pravda, že to, co jsem posílal v předchozím postu, byl privátní klíč bez hesla. I když jsem zkusil podle @TTTTTTT privátní klíč vygenerovat s heslem, problém je pořád stejný.

Když jsem zkusil
Kód: [Vybrat]
openssl cms -sign -signer sign.pem, tak mi to bash vrátil bohužel jako neplatný command.

---

@fantomas - tak by ta konverze měla být správně, našel jsem to tak na stackoverflow, ale i na jiných zdrojích (https://www.sslshopper.com/ssl-converter.html).
Problém je, že ten .pfx nese úplně všechno, certifikační autoritu, veřejný i privátní klíč. Po jeho konverzi tak je potřeba ten soubor otevřít a jednotlivé klíče z toho vytáhnout každý zvlášť.. Ale problém je i tak pořád stejný, stále stejný error.

---

I když zkusím do funkce dát jako argument .pfx certifikát, nikam se nepohnu.
A je pravda, že pkcs7 je používaný i mimo jiné k podepisování mailů, ale přesně tato funkce je použita v knihovne TCPDF, která dělá vlastně totéž..

Kód: [Vybrat]
openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
Přesně tuhle konstrukci jsem zkoušel jak s pfx, tak s .pem, jak s heslem, tak i bez hesla..

Re:PHP - Digitální podpis pdf souboru
« Odpověď #6 kdy: 18. 10. 2015, 16:22:30 »
Ten soubor s privátním klíčem, který se snažíte použít, tedy vypadá následovně?

Kód: [Vybrat]
-----BEGIN PRIVATE KEY-----
…data zakódovaná v BASE64…
-----END PRIVATE KEY-----
Tj. nic dalšího v tom souboru není?

kejhal

Re:PHP - Digitální podpis pdf souboru
« Odpověď #7 kdy: 18. 10. 2015, 16:28:59 »
Vypadá spíše takto

Kód: [Vybrat]
Bag Attributes
    localKeyID: 6A B3...(pokračuje)
Key Attributes: <No Attributes>
-----BEGIN RSA PRIVATE KEY-----
zakódovaná data
-----END RSA PRIVATE KEY-----

Ať to exportuju jakkoli, vdždycky jsem došel do této podoby.

Re:PHP - Digitální podpis pdf souboru
« Odpověď #8 kdy: 18. 10. 2015, 16:43:08 »
To je divné, nedivil bych se, kdyby s tímhle openssl neumělo pracovat. Zkuste ten začátek smazat, aby tam zůstaly jen ty řádky s BEGIN a END a ta data mezi nimi. Také je mi divné to „RSA“, to je podle mne privátní klíč ve formátu PKCS1, který používaly staré verze OpenSSL, novější používají PKCS8, kde je typ klíče uložen uvnitř těch dat. Používáte nejnovější verzi OpenSSL?

Adam

Re:PHP - Digitální podpis pdf souboru
« Odpověď #9 kdy: 18. 10. 2015, 21:25:00 »
Vypadá spíše takto

Kód: [Vybrat]
Bag Attributes
    localKeyID: 6A B3...(pokračuje)
Key Attributes: <No Attributes>
-----BEGIN RSA PRIVATE KEY-----
zakódovaná data
-----END RSA PRIVATE KEY-----
Navrhuji smajznout vše před "-----BEGIN RSA PRIVATE KEY-----" a za "-----END RSA PRIVATE KEY-----". Některé softwary to nemají rády. (A obdobně u certifikátu.)
Mně to už dvakrát pomohlo. Čímž netvrdím, že je to samospasitelné, ten "doplňkový binec" by ničemu vadit neměl - teoreticky...

kejhal

Re:Digitální podpis PDF v PHP
« Odpověď #10 kdy: 19. 10. 2015, 13:12:19 »
Lokální verze OpenSSL je OpenSSL 0.9.8zg 14 July 2015.
Zkusil jsem celý export udělat na linuxovém stroji kde verze OpenSSL je OpenSSL 1.0.1k 8 Jan 2015.

Když jsem vyexportoval .pfx soubor, který nese jak certifikační autoritu, veřejný i soukromý klíč, tak už to správně udělalo u privátního klíče s heslem
-----BEGIN ENCRYPTED PRIVATE KEY-----
...
-----END ENCRYPTED PRIVATE KEY-----

bez hesla
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----

Vytáhl jsem tak jednotlivé klíče a udělal zvlášť public
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

a zvlášť privátní. Zkusil jsem to teď pro změnu jak s těmi Bag Attributes, tak i bez, ale jsem pořád tam kde jsem byl.. Pravdou je, že převod na lokálu, kde je jiná verze OpenSSL byla asi špatná

TTTTTTT

Re:Digitální podpis PDF v PHP
« Odpověď #11 kdy: 19. 10. 2015, 14:10:02 »
  • funguje zašifrování pomocí openssl cms? Je od verze 1.0.0, tedy ten novější stroj by to měl podporovat
  • změní se chybová hláška, pokud soubor vůbec není k dispozici?
  • čte skript soubor s privátním klíčem (prozradí strace)?

Re:Digitální podpis PDF v PHP
« Odpověď #12 kdy: 19. 10. 2015, 14:10:50 »
Pak už podle mne zbývá jenom podívat se na zdroják té funkce, jaké kroky postupně dělá, a zkusit zjistit, kde přesně to končí chybu – např. ty jednotlivé kroky provést zvlášť. Případně jestli tam je možné zapnout vyšší úroveň logování, abyste zjistil, co se provedlo a co ne. Protože ta funkce musí jenom při načítání privátního klíče najít soubor, otevřít a přečíst ho, dekódovat ten formát PKCS8 nebo PKCS1, čímž teprve získá data k rozšifrování a ta rozšifrovat zadaným heslem. Z té hlášky vůbec není jasné, ve které fázi k té chybě dojde a kde tedy hledat problém.

kejhal

Re:Digitální podpis PDF v PHP
« Odpověď #13 kdy: 19. 10. 2015, 21:11:37 »
Pokud soubor není vůbec k dispozici, tak to vrací pořád stejnou chybu..
Když jsem ale teď zkusil tedy přímo na serveru zmiňovaný openssl cms, tak.. I když se tomu snažím porozumět, už se v tom začínám ztrácet. Jaký z certifikátů bych měl pro tento příkaz správně použít?

Když použiji čistě privátní zašifrovaný, tak dostanu
Kód: [Vybrat]
139809107854992:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:703:Expecting: TRUSTED CERTIFICATETo stejné i s veřejným.

Když zkusím ale privátní s certifikátem certifikační autority, dostanu odpověď
Kód: [Vybrat]
139957258282640:error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch:x509_cmp.c:330:
139957258282640:error:2E066088:CMS routines:CMS_add1_signer:private key does not match certificate:cms_sd.c:311:

Bohužel tady už je to úplně mimo mé znalnosti co se certifikátů týká, tak zkouším všechny možnosti.

strace zkusím použít, ale nejspíš to nebude úplně nejjednodušší kvůli použitému frameworku, kdy se nedá určitá stránka jen tak z konzole spustit.

@Filip Jirsák, je to taky varianta, zkusím pozjišťovat něco i touto cestou.

V nejhorším případě mě napadá soubor podepsat bashovým skriptem, kdyby se nenašel jiný způsob..  >:(

kejhal

Re:Digitální podpis PDF v PHP
« Odpověď #14 kdy: 19. 10. 2015, 22:41:17 »
Omlouvám se za double post, ale když jsem vzal veřejný, privátní i certifikační autoritu v jednom, tak už to po openssl cms skočilo do nějakého editoru S/MIME signed message. Když jsem ale to stejné zkusil v phpku, žádná šance..