DEV Community

Cover image for PHP: Importando e assinando digitalmente arquivos PDF
Cledilson Nascimento
Cledilson Nascimento

Posted on

PHP: Importando e assinando digitalmente arquivos PDF

Manipular arquivos PDF não é uma tarefa simples e muitas vezes precisamos apenas realizar uma pequena manipulação nestes arquivos, ou seja, será apenas uma parte muito pequena do nosso sistema, tornando inviável o desenvolvimento dessas funcionalidades "do zero".

Para PHP temos várias bibliotecas disponíveis e gratuitas, entre elas a TCPDF, que pra mim é uma das melhores, mas, falta nela a opção de importação. Com uma boa pesquisada na Internet encontrei o FPDI, que tem a função de importar PDFs em bibliotecas já existentes, como a já citada TCPDF. O funcionamento e uso são simples: o FPDI estende a classe TCPDF e acrescenta a funcionalidade de importação. Então, neste artigo vamos usar essas duas bibliotecas. Durante o desenvolvimento de um projeto(no qual trabalho enquanto escrevo este artigo) tive um problema durante a assinatura do arquivo usando as bibliotecas:

Warning: openssl_pkcs7_sign(): error getting private key in...

Depois de muito pesquisar, recorri à comunidade do PHPBA, onde o Marcio Albuquerque(@mlalbuquerque) me ajudou e o problema foi resolvido. Se você não participa de uma comunidade, aconselho que procure a comunidade da sua região, é muito bom. Voltando, a ideia é que os arquivos do certificado devem ser passados como stream, necessitando de file:// antes do endereço do arquivo para funcionar corretamente. Então, fique atento ao código para não deixar esse detalhe passar.

A instalação via composer, como sempre é bem simples: composer require setasign/fpdi ou para instalação manual, consulte a documentação oficial: https://github.com/Setasign/FPDI. Instale também o TCPDF: composer require tecnickcom/tcpdf. E veja como fica o código com uso simples:

//Endereço do arquivo do certificado
//Obs.: Tentei usar o certificado no formato PFX e não funcionou
//Para converter use o comando no Prompt do Windows ou Terminal do Linux:
//openssl pkcs12 -in certificado.pfx -out tcpdf.crt -nodes
$cert = 'C:\\tcpdf.crt';

//Informações da assinatura - Preencha com os seus dados
$info = array(
   'Name' => 'Nome',
   'Location' => 'Localidade',
   'Reason' => 'Descreva o motivo da assinatura',
   'ContactInfo' => 'Dados de contato',
);

$pdf = new Fpdi();
//Configura a assinatura. Para saber mais sobre os parâmetros
//consulte a documentação do TCPDF, exemplo 52.
//Não esqueça de mudar 'senha' para a senha do seu certificado
$pdf->setSignature('file://'.$cert, 'file://'.realpath($cert), 'senha','', 2, $info);

//Importa uma página
$pdf->AddPage();
$pdf->setSourceFile("C:\\documento.pdf");
$tplId = $pdf->importPage(1);
$pdf->useTemplate($tplId, 0, 0); //Importa nas medidas originais

//Manda o PDF pra download
$pdf->Output('teste.pdf', 'D');
Enter fullscreen mode Exit fullscreen mode

O código acima importa apenas a primeira página, caso necessite importar mais de uma página basta fazer um loop. Então é isso, agradeço por ter lido, caso tenha dúvida deixe seu comentário. Abraço!

Top comments (14)

Collapse
 
sidneijr1984 profile image
Sidnei Amadeu Junior

Bom dia... muito legal seu post.

Eu fiz deu tudo certo mas tenho algumas duvidas. No adobe ao abrir o arquivo ele da como assinatura invalida, estava lendo que isso ocorre porque o adobe nao reconhece a assinatura com certificado A1 a nao ser que voce importe o seu certificado pra dentro do adobe eh isso mesmo?

Outra duvida, como posso pegar por exemmplo um hash desse arqvuio assinado para posteriormente validar se foi feito alguma alteracao no mesmo? Pois tenho umn sistema medico e minha ideia nao seria salvar o documento assinado e sim o hash dele pra depois poder validar o mesmo .

Collapse
 
cledilsonweb profile image
Cledilson Nascimento

Então, realmente ele mostra um alerta quanto ao certificado, mas dá no A3 também se você não tiver marcado ele como confiável no Adobe, acho que o ICP-Brasil já deveria ter resolvido isso com a Adobe e outras empresas...
Quanto ao hash, você consegue fazer, porém é complicado, você teria que pegar apenas o conteúdo do documento ao invés de fazer o hash no arquivo todo, já que algum programa pode aplicar alguma tag, etc. Porém, uma hash é gerada quando alguém assina, e é com ela que os leitores de PDF sabem que o documento não foi alterado depois que ele foi assinado, a garantia que a assinatura digital dá a um PDF é justamente essa.

Abraço e obrigado pelo comentário!

Collapse
 
paulohandreani profile image
Paulo Andreani

Tentei fazer a conversão do formato do certificado com o cmd mas não deu certo.
Abri o CMD na pasta do certificado e executei o "openssl pkcs12 -in certificado.pfx -out tcpdf.crt -nodes"
Seria isso?

Collapse
 
cledilsonweb profile image
Cledilson Nascimento

Opa, conferiu se o openssl está instalado e o path configurado corretamente?

Collapse
 
danielpitta profile image
DanielPitta

Boa tarde,

Fiz tudo como vc falou, e inclui no inicio do arquivo
require_once('vendor/autoload.php');

Mas não esta funcionando.
No log do Servidor aparece esse erro:

PHP Fatal error: Uncaught Error: Class 'Fpdi' not found

Então inclui mais um require apontando direto para o arquivo Fpdi.php.
require_once('vendor/setasign/fpdi/src/Fpdi.php');

E passou a dar outro erro:

PHP Fatal error: Uncaught Error: Class 'FPDF' not found in

Alguém pode ajudar?

Collapse
 
cledilsonweb profile image
Cledilson Nascimento

Você instalou as bibliotecas via Composer? Se não usou, vai ter que incluir todas elas "na mão".

Collapse
 
mlalbuquerque profile image
Marcio Albuquerque

Nessas horas, uma assinatura de método vem a calhar... Ou uma doc melhor estruturada. Não tem nada na doc dizendo que a string deve vir formato de stream (file://)

Collapse
 
cledilsonweb profile image
Cledilson Nascimento

Exatamente, um simples detalhe que pode nos levar um bom tempo! Valeu Marcio!!

Collapse
 
wilderluz profile image
Wilder

Sabe me dizer como personalizar as assinaturas? Tenho um sistema que gera Laudos, e agora estou ajustando para já sair assinado do sistema com certificado A1. Preciso personalizar a assinatura, inserir um png com a assinatura do usuário, o nosso site e algumas mensagens. Sabe me dizer se tem como?

Collapse
 
willianfae profile image
willianfae

Uma pergunta, se eu envio um doc para um cliente, esse doc eu já consigo assinar digitalmente ele pelo servidor. Quando o cliente der um aceitar, é possivel fazer uma assinatura no PDF?

Collapse
 
cledilsonweb profile image
Cledilson Nascimento

Opa. Se esse "der um aceitar" for clicar num link enviado por email, e este link for um PHP para realizar essa assinatura, dá sim. Só seguir o artigo que provavelmente vai dar certo. Obrigado pelo comentário. Abraço!

Collapse
 
devguidolin profile image
Thiago Guidolin

Obrigado pelo teu artigo, fiz aqui e gerou o PDF, porém não está assinando.

Aqui é o site do governo que valida assinaturas digitais.
verificador.iti.gov.br/verifier-2....

Collapse
 
richenrique profile image
ricardo henrique

Poderia compartilhar quais arquivos você incluiu na página?

Collapse
 
cledilsonweb profile image
Cledilson Nascimento

Amigo, o código é basicamente esse que está no artigo. E os arquivos da biblioteca instalei via composer:
composer require setasign/fpdi tecnickcom/tcpdf
Mas se não usa Composer dá uma olhada nesse link:
github.com/Setasign/FPDI
Abraço!