Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: kejhal 17. 10. 2015, 17:07:58

Název: Digitální podpis PDF v PHP
Přispěvatel: kejhal 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 :)
Název: Re:PHP - Digitální podpis pdf souboru
Přispěvatel: Fantomas 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 ...?
Název: Re:PHP - Digitální podpis pdf souboru
Přispěvatel: TTTTTTT 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.
Název: Re:PHP - Digitální podpis pdf souboru
Přispěvatel: Jenda 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?
Název: Re:PHP - Digitální podpis pdf souboru
Přispěvatel: Filip Jirsák 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.
Název: Re:PHP - Digitální podpis pdf souboru
Přispěvatel: kejhal 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..
Název: Re:PHP - Digitální podpis pdf souboru
Přispěvatel: Filip Jirsák 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í?
Název: Re:PHP - Digitální podpis pdf souboru
Přispěvatel: kejhal 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.
Název: Re:PHP - Digitální podpis pdf souboru
Přispěvatel: Filip Jirsák 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?
Název: Re:PHP - Digitální podpis pdf souboru
Přispěvatel: Adam 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...
Název: Re:Digitální podpis PDF v PHP
Přispěvatel: kejhal 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á
Název: Re:Digitální podpis PDF v PHP
Přispěvatel: TTTTTTT 19. 10. 2015, 14:10:02
Název: Re:Digitální podpis PDF v PHP
Přispěvatel: Filip Jirsák 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.
Název: Re:Digitální podpis PDF v PHP
Přispěvatel: kejhal 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..  >:(
Název: Re:Digitální podpis PDF v PHP
Přispěvatel: kejhal 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..
Název: Re:Digitální podpis PDF v PHP
Přispěvatel: TTTTTTT 19. 10. 2015, 23:59:40
K podpisu je potřeba certifikát a k němu příslušející privátní klíč. Dle pojmenování z php scriptu tedy cat signcert.pem private.pem > sign.pem. Certifikační autorita je potřeba jen při ověření podpisu.

Citace
Když použiji čistě privátní zašifrovaný ...
..., tak to řve, že mu chybí certifikát

Citace
Když zkusím ale privátní s certifikátem certifikační autority ...
..., tak to řve, že privátní klíč neodpovídá veřejnému klíči v certifikátu.

Příkaz pro kontrolu, že jsou certifikáty správně:

Kód: [Vybrat]
echo -n "hello world" | \
  openssl cms  -sign -signed sign.pem -nodetach | \
  openssl cms  -verify -CAfile ca.pem

-nodetach říká, že má certifikát být v součástí podepsané zprávy, pamatuju-li si dobře. Zdá se, že certifikáty máte správně, pokud to openssl vezme.

Stránka na serveru se možná spustit nedá, ale mohlo by pomoct udělal si php skript, který bude volat jen tuhle jednu funkci a odladit do mimo server. Třeba nakonec zjistíte, že skript je dobře, jen ten server nemá přístup k certifikátům...
Název: Re:Digitální podpis PDF v PHP
Přispěvatel: kejhal 20. 10. 2015, 10:10:03
Když zkusím zmiňovaný postup, příkaz, tak jsem dostal pouze tuto odpověď.

Kód: [Vybrat]
Error reading S/MIME message
140735151387472:error:0D0D40D1:asn1 encoding routines:SMIME_read_ASN1:no content type:asn_mime.c:440:

Nakonec se to budu nejspíš stejně snažit vyřešit podpisem přes bashový skript, který phpko zavolá. Přijde mi to jako rychlejší cesta na vyřešení, snad si s tímto už poradím a najdu, co zmiňovaný chyba znamená.
Název: Re:Digitální podpis PDF v PHP
Přispěvatel: Petr 20. 10. 2015, 10:22:58
Podobne jsem v tom plaval take. Doporucuju podivat se do TCPDF, ta vyuziva stejnou fukci a me funguje. Pozdeji jsem podobny model implementoval do jineho pho , kde podpis vytvarime mimo tcpdf, dal jsme to dokonce prenesli i do C kodu a posledni aplikace s podpisem byla opacna - ta podpis z pdf ziskava, kontroluje - to posledni uz ale bylo o napsani vlastni php extension.

Pkcs7 je v pohode, takto to do php opravdu jde, myslim ze v hex konvezi, zarovnava se to nulami, no detail je vide zkoumanim kodu v tcpdf
Název: Re:Digitální podpis PDF v PHP
Přispěvatel: kejhal 20. 10. 2015, 11:30:41
@Petr, aspoň, že vím, že to teda někomu funguje. Právě, že já jsem se to pokoušel to vykuchat z toho tcpdfka.
Když už jsi s tím měl takovouto zkušenost, můžu tě poprosit, co je obsahem těch tvých certifikátů? Respektive co by správně měl obsahovat ten první, "signing_cert" a co všechno "private_key"? Protože i když jsem to zkusil udělat přes tcpdf, tak s tím nehnu a věřím, že chyba musí být v tom, co obsahují ty soubory, jen prostě nevím co ty soubory správně musí obsahovat.
Název: Re:Digitální podpis PDF v PHP
Přispěvatel: Petr 21. 10. 2015, 22:34:01
@Petr, aspoň, že vím, že to teda někomu funguje. Právě, že já jsem se to pokoušel to vykuchat z toho tcpdfka.
Když už jsi s tím měl takovouto zkušenost, můžu tě poprosit, co je obsahem těch tvých certifikátů? Respektive co by správně měl obsahovat ten první, "signing_cert" a co všechno "private_key"? Protože i když jsem to zkusil udělat přes tcpdf, tak s tím nehnu a věřím, že chyba musí být v tom, co obsahují ty soubory, jen prostě nevím co ty soubory správně musí obsahovat.

Ozvi se mi na petr@xgw.cz a pokusim se pomoci :-)