A questão PKCS#12 vs PEM aparece sempre que uma chave e um certificado circulam entre servidores Unix e plataformas com repositórios de chaves próprios. Arquivos PEM são blocos separados e legíveis que softwares baseados em OpenSSL carregam diretamente; um arquivo PKCS#12 (.p12 ou .pfx) é um único contêiner criptografado que os sistemas operacionais e o Java importam em uma só etapa. As chaves e os certificados são os mesmos, a embalagem é diferente, e são os algoritmos da embalagem que decidem se uma importação funciona.
Visão geral
| PEM | PKCS#12 (PFX) | |
|---|---|---|
| Especificação | RFC 7468 (codificação em texto do DER) | RFC 7292 (PKCS #12 v1.1), RFC 9579 (PBMAC1) |
| Formato | Texto, um ou mais blocos BEGIN … END | Contêiner DER binário de “safe bags” |
| Conteúdo | O que você colocar no arquivo: chave, certificados, CRLs | Chaves privadas, certificados, CRLs, segredos, com nomes amigáveis e IDs de chave |
| Proteção | Opcional, por chave (PKCS#8 criptografado) | Criptografia dos bags baseada em senha, mais um MAC de integridade |
| Proteção padrão no OpenSSL 3.6 | PBES2 com AES-256-CBC para chaves criptografadas | PBES2, PBKDF2-HMAC-SHA256, AES-256-CBC, 2.048 iterações; MAC HMAC-SHA256 |
-legacy do OpenSSL | Não se aplica | RC2-40 para certificados, 3DES para chaves, MAC HMAC-SHA1 |
| Extensões típicas | .pem, .crt, .key | .p12, .pfx |
| Usado por | OpenSSL, servidores web, proxies, Go, Python, Node.js | Repositório de certificados do Windows, Keychain do macOS, KeyStore do Java, navegadores, signtool |
O que há dentro de um .pfx
Um arquivo PKCS#12 é uma estrutura PFX: um cofre autenticado (authenticated safe) formado por blocos de conteúdo, cada um com seus bags. As chaves privadas ficam em entradas pkcs8ShroudedKeyBag (PKCS#8 criptografado), os certificados em entradas certBag (muitas vezes também em um bloco criptografado), e os bags carregam atributos como friendlyName e localKeyId, que vinculam uma chave ao seu certificado. Um MAC derivado da senha protege a integridade do arquivo inteiro; a RFC 9579 (2024) acrescenta o PBMAC1, que deriva a chave do MAC com PBKDF2 em vez da KDF legada do PKCS#12.
O PEM não tem contêiner: um servidor é configurado com um arquivo de chave e um arquivo de certificado (ou de cadeia), ou com um único arquivo com vários blocos. A proteção é a que o arquivo de chave tiver, normalmente nenhuma nos servidores, porque o processo precisa ler a chave sem que uma pessoa digite uma senha.
Conversão
O script abaixo, executado com o OpenSSL 3.6.5, empacota uma chave e um certificado descartáveis em um arquivo PKCS#12 e depois os desempacota.
#!/bin/sh
dir=$(mktemp -d)
cd "$dir" || exit 1
openssl req -new -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -keyout key.pem \
-subj "/CN=example.com" -days 30 -out cert.pem 2>/dev/null
openssl pkcs12 -export -inkey key.pem -in cert.pem -name demo -passout pass:demo -out bundle.p12
openssl pkcs12 -in bundle.p12 -passin pass:demo -info -noout 2>&1 | grep -E "MAC:|Shrouded|PKCS7 Encrypted"
openssl pkcs12 -in bundle.p12 -passin pass:demo -nodes -out extracted.pem 2>/dev/null
grep -E "^-----BEGIN" extracted.pem
cd / && rm -rf "$dir"
MAC: sha256, Iteration 2048
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
-----BEGIN CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
Para incluir os intermediários, passe-os com -certfile chain.pem; para extrair apenas os certificados, use -nokeys, e apenas a chave, -nocerts. A flag -nodes (chamada -noenc nas versões mais novas) grava a chave sem criptografia, então trate a saída com o devido cuidado.
Compatibilidade: algoritmos modernos versus legados
O OpenSSL 3.0 trocou os padrões de exportação: de RC2-40 e 3DES com MAC HMAC-SHA1 para AES-256 com MAC HMAC-SHA256. Muitos consumidores só se adaptaram depois:
- macOS: o framework Security da Apple importa arquivos PKCS#12 modernos a partir do macOS 15; o macOS 14 e anteriores falham com “MAC verification failed during PKCS12 import (wrong password?)”, mesmo com a senha certa.
- Java: o JDK 9 tornou o PKCS#12 o tipo de keystore padrão; o JDK 16 (e as atualizações de 2021 do 8 e do 11) mudou o padrão do próprio Java para PBES2 com AES-256 e MAC HMAC-SHA256, e as versões atuais leem arquivos do OpenSSL 3.
- Windows: as versões atuais importam arquivos PFX com AES-256; versões muito antigas do Windows Server podem precisar da forma legada.
- OpenSSL 3 lendo arquivos antigos: arquivos cujos certificados estão criptografados com RC2-40 (o padrão do OpenSSL 1.1, e o que as exportações com
-legacyusam quando o RC2 está compilado) só carregam com-legacy, que carrega o provedor legacy. Sem ele, o OpenSSL 3.6.5 imprimeerror:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupportedparaRC2-40-CBC, e o Node.js 26 rejeita o arquivo comUnsupported PKCS12 PFX data. Arquivos que usam apenas 3DES e MAC HMAC-SHA1 carregam sem o provedor legacy.
Quando um arquivo moderno falha em um destino reconhecidamente antigo, exporte com openssl pkcs12 -export -legacy … ou escolha os algoritmos explicitamente (-keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg SHA1), e trate esse arquivo como fracamente protegido durante o transporte.
Aqui as mensagens de erro enganam: “MAC verification failed” ou “wrong password” geralmente significam que o leitor não oferece suporte ao algoritmo de MAC ou de criptografia, e não que a senha esteja errada. Inspecione o arquivo com openssl pkcs12 -info -noout (acrescente -legacy para arquivos antigos) e veja quais algoritmos foram usados antes de sair tentando senhas.
PKCS#12 como keystore
O Java trata um arquivo PKCS#12 como um keystore: keytool -list -keystore bundle.p12 -storetype PKCS12 mostra as entradas dele, e o friendlyName vira o alias. Um keystore pode conter várias entradas de chave e certificados confiáveis, o que um arquivo PEM não consegue expressar sem convenções de nomes. Os truststores são um caso especial: um truststore PKCS#12 que contém apenas certificados precisa de atributos específicos do Java para marcá-los como confiáveis, por isso keytool -importcert é a forma confiável de montar um. A maioria das outras plataformas ignora esses atributos do Java e importa chaves e certificados pela correspondência do localKeyId.
Quando escolher cada um
- TLS em servidores Linux, proxies reversos, ingress do Kubernetes: chave PEM e certificado com a cadeia completa.
- Importação de certificados no IIS do Windows, no Azure App Service e no Key Vault: PKCS#12.
- TLS em Java e Spring Boot: um keystore PKCS#12 (ou PEM, que as versões recentes do Spring Boot aceitam diretamente).
- Certificados de cliente para navegadores e dispositivos móveis: PKCS#12 com uma senha forte, e o arquivo entregue separadamente da senha.
- Assinatura de código com signtool ou jarsigner: PKCS#12, se é que a chave está em um arquivo; desde 2023, chaves de assinatura de código publicamente confiáveis precisam ficar em hardware.
- Inspeção e depuração: PEM, ou o inspetor de PFX, que lista os bags e os algoritmos localmente, sem revelar a chave.
Notas de migração
- Inclua a cadeia completa ao montar um PKCS#12 para servidores ou clientes, na ordem com o certificado folha primeiro; intermediários ausentes são a falha mais comum depois da importação.
- Confira se a chave corresponde ao certificado antes de empacotar: o verificador de correspondência de chave e certificado compara as chaves públicas dos dois.
- Escolha os algoritmos pensando no consumidor mais antigo e depois atualize: exporte de novo com os padrões modernos quando as plataformas antigas saírem de cena.
- Use senhas fortes e aleatórias em arquivos PKCS#12 que circulam, já que o arquivo pode ser atacado offline; as 2.048 iterações de PBKDF2 que o OpenSSL usa por padrão são poucas pelos critérios de armazenamento de senhas.
- Não deixe chaves extraídas largadas por aí: depois de converter um
.pfxem PEM para um servidor, restrinja as permissões do arquivo e apague as cópias temporárias.