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.