Assinando Digitalmente Emails (S/MIME) com PHP.

Recentemente resolvi aprimorar alguns scripts do meu servidor efetuando a assinatura digital dos emails enviados, acrescentando a garantia de integridade dos emails. A idéia é boa e a implementação simples, depois de se investir muitas horas de pesquisa na documentação escassa. Esse post busca resumir todo o trabalho, facilitando futuras implementações.

São necessários a chave privada (com ou sem senha), o certificado público e o email completo (pronto para ser enviado) salvos em arquivos. É desejável o pacote de certificados da entidade intermediária e root salvo em arquivo também. É imprescindível saber o endereço do destinatário do email!! 🙂

O email salvo deverá conter apenas as headers: MIME-Version e Content-Type, no caso de emails de múltiplas partes. As headers Message-ID, From, To, Subject, X-Originating-IP, X-Priority, Importance, X-Mailer, entre outras serão envelopadas no email assinado.

Com tudo à mão, esse é o código php que assinará digitalmente nosso email!

$certificado = "/arquivo/do/certificado.pem";
$chave_privada = "/arquivo/da/chave.key";
$senha_chave_privada = "123mudar"; //se não tiver senha, deixe vazio -> "".
$pacote_certificados = "/arquivo/da/entidade/certificadora.pem";
$email_salvo = "/arquivo/de/email.txt"; // essa é a fonte do email.
$email_assinado = "/arquivo/de/email.eml"; //esse será o email assinado.
$destinatario = "voce@seuemail.com.br";

// os dados da header podem ser extraídos do arquivo de email salvo
$headers = array();
$headers[] = "From: Extraido do arquivo de emails <email@fulano.com.br>";
$headers[] = "To: Para Voce <voce@seuemail.com.br>";
$headers[] = "Subject: Assunto desse email assinado digitalmente";

// início do código
$cert = file_get_contents($certificado);
$pkey = file_get_contents($chave_privada);
if (openssl_pkcs7_sign($email_salvo, $email_assinado, $cert, array($pkey, $senha_chave_privada), $headers, PKCS7_DETACHED, $pacote_certificados)) {
     // depois de assinar o email, é preciso corrigir as CRLF de diferentes sistemas operacionais.
     // evitando assim o problema de que o email que acaba de ser assinado foi violado.
     $file_array = file($email_assinado);
     $file = join ("", $file_array);
     $message = preg_replace("/\r\n|\r|\n/", "\r\n", $file);
     $fp = fopen($email_assinado, "wb");
     flock($fp, 2);
     puts($fp, $message);
     flock($fp, 3);
     fclose($fp);
     // corrigido o arquivo, usamos o sendmail para enviar o email.
     exec("cat " . $email_salvo " | /usr/sbin/sendmail -f email@fulano.com.br -- " . $destinatario);
}

Esse código pode ser integrado a sites, a linhas de comando, e está disponível no próprio site php.net na parte da função openssl_pkcs7_sign nas entrelinhas dos comentários.

Muitas vezes temos o certificado de assinatura no formato .pfx e precisamos convertê-lo para .pem, podemos utilizar o openssl para fazer essa conversão com os seguintes comandos (auto-explicativos):

openssl pkcs12 -in chave.pfx -nokeys -out certificado.pem
openssl pkcs12 -in chave.pfx -nocerts -des3 -out chave_privada.key

Para gerar uma chave_privada que não requer senha, basta trocar o parâmetro -des3 por -nodes.

Escreva seu comentário: