Upload de certificado digital (.pfx/.p12) em integrações
Como anexar/trocar certificado digital A1 (.pfx/.p12) ao criar OU editar integração SOAP/REST. Inclui alerta automático 30/14/7/1d antes do vencimento e badge na UI.
Onde aparece
No formulário de criar ou editar integração manual, ao escolher protocolo SOAP, REST/HTTP, OData, Webhook/HTTP ou AS2:
- Campo Certificado .pfx/.p12 (mTLS) — upload do arquivo
- Campo Senha do certificado — passphrase do .pfx
A1 (arquivo .pfx) é suportado. Quando você seleciona "SEFAZ" no campo Plataforma, o protocolo muda automaticamente pra SOAP (padrão dos webservices NF-e/NFC-e/CT-e) e um aviso lembra de anexar o certificado. A3 (token físico/cartão) não está suportado — token requer driver/middleware no servidor, fora de escopo no SaaS.
O que acontece no servidor
- Frontend lê o arquivo, converte pra base64 e envia junto com a senha
- Backend chama
parsePkcs12(base64, senha)(libsrc/lib/integrations/certificate.ts, lê o PKCS#12 comasn1js+node:crypto) - Se senha errada ou arquivo inválido, retorna HTTP 400 "Certificado inválido". Só é aceito o arquivo que a conexão segura (mTLS) consegue usar: .pfx exportado com cifra antiga (RC2/RC4/DES — padrão do OpenSSL 1.x, Java e Windows antigos) é recusado com o aviso para exportar de novo em AES-256:
openssl pkcs12 -export -in cert.pem -inkey chave.pem -keypbe AES-256-CBC -certpbe AES-256-CBC -macalg sha256 -out novo.pfx (de um .pfx antigo, primeiro openssl pkcs12 -in antigo.pfx -nodes -legacy -out tudo.pem)- Windows: certmgr → Exportar → "Sim, exportar a chave privada" → criptografia AES256-SHA256 - Java 11.0.12+ ou 8u301+:
keytool -importkeystore -srckeystore antigo.pfx -srcstoretype PKCS12 -destkeystore novo.pfx -deststoretype PKCS12
- Se válido, extrai metadata: subject, issuer, serial, validade, fingerprint SHA-256
- Criptografa o blob (.pfx em base64) e a senha separadamente com AES-256-GCM (
src/lib/crypto.ts, chave emCREDENTIALS_ENCRYPTION_KEY) - Armazena tudo em
connections.certificates(jsonb)
A senha NUNCA fica em texto puro — só decifra no momento de abrir conexão TLS.
Onde o certificado é usado (mTLS de saída)
Com certificado anexado, toda chamada à conexão apresenta o certificado no handshake TLS: envio e "Testar conexão" de REST, OData (leitura, escrita e
$metadata), SOAP, Webhook/HTTP e AS2, e a consulta de status da entrega assíncrona. No Testar conexão antes de salvar vale o certificado recém-anexado; na edição sem anexo novo, o já salvo.- O certificado só vai ao servidor da conexão: um redirecionamento ou uma página seguinte em outro endereço sai sem ele.
- Certificado vencido (ou ainda não válido): nada é enviado — a mensagem diz a data e pede o renovado.
- O parceiro recusou o handshake: a mensagem diz se ele exige certificado (nenhum foi apresentado) ou recusou o apresentado (confira emissor e validade com o parceiro).
- Só em HTTPS: URL
http://com certificado é recusada. - A conexão mostra mTLS ativo e o card da integração o selo mTLS (vermelho quando vencido).
Editar / trocar / remover certificado
No card da integração, clique em Editar. A seção de protocolo mostra:
- O subject e a data de validade do cert atual (badge amarelo se < 30d, vermelho se < 7d ou expirado)
- Botão Remover certificado (apaga o cert sem deletar a integração)
- Campo de upload pra subir um novo .pfx (sobrescreve o anterior na próxima save)
Alerta de vencimento (cron diário)
O cron
/api/cron/cert-expiry-alerts roda diariamente às 07:00 UTC e cria 1 alerta por bucket atravessado:
- 30 dias antes — primeiro warning amarelo
- 14 dias — warning amarelo
- 7 dias — alerta vermelho
- 1 dia — alerta crítico vermelho
- 0 dias — cert expirado, urgent
Dedup via UNIQUE (connection_id, days_bucket) em
cert_expiry_alerts. Quando o cert é renovado (não_after muda), um trigger no DB apaga os alerts antigos automaticamente — buckets resetam.Canais (na ordem):
- Slack se
SLACK_ALERTS_WEBHOOK_URLsetado — bloco com subject + bucket + link pra integração - UI badge na integration-card mostrando "Cert vence em Xd" (sempre visível)
Limitações conhecidas
- Apenas A1 (arquivo). A3 (token físico) requer Lefia Agent on-prem ou Cloud HSM — fora desta versão.
- AS4 e EBICS continuam com PEM texto separado (não unificado nesta versão).
- Validação de cadeia/CRL/OCSP não é feita. Se o emissor revogou o cert, só notará na primeira chamada TLS (erro 4xx do servidor da contraparte).
- O cron de alerta roda 1x/dia. Se o cert vence em < 24h depois da última execução, o alerta sai no próximo ciclo.
Schema SQL
connections.certificates(jsonb): metadata + ciphertexts do client certcert_expiry_alerts(table): dedup de buckets, RLS super_admin select-only- Trigger
connections_cert_expiry_alerts_reset: limpa dedup quando cert renovado
Veja também
pipeline-mensagens-fluxos— fluxos completos de mensagem incluindo o cenário SEFAZ + certpii-redaction-llm— privacidade de dados em payloads enviados pra IAai-toggle-hierarchy— toggle de IA por scope
Histórico de versões
- 1.1.0
O certificado digital do cliente (.pfx/.p12) anexado na conexão agora é apresentado de verdade na conexão segura (mTLS) em toda chamada ao parceiro: envio e teste de REST, OData (leitura, escrita e $metadata), SOAP, Webhook/HTTP e AS2, a consulta de status da entrega e o "Testar conexão" — inclusive antes de salvar, com o certificado recém-anexado. Antes ele era aceito, validado e vigiado, mas não ia no handshake. Certificado vencido é recusado antes de enviar, com aviso claro; o certificado só vai ao servidor da conexão (nunca a um redirecionamento para outro endereço). O campo do certificado passa a existir também em OData, Webhook/HTTP e AS2, e a conexão mostra "mTLS ativo". Conexões sem certificado continuam exatamente como antes.
06/10/2026 · updated
- 1.0.1
Leitura do certificado digital (.pfx/.p12) refeita sem o node-forge (aviso de segurança sem correção). Passa a aceitar exatamente o que a conexão segura (mTLS) consegue usar: arquivo exportado com cifra antiga (RC2/RC4/DES — padrão de OpenSSL 1.x, Java e Windows antigos) é recusado na hora do upload, com o passo a passo para exportar de novo em AES-256, em vez de ser aceito e falhar só na conexão. Os demais arquivos abrem como antes; assunto e emissor com acento passam a sair certos e certificado de curva elíptica passa a ser lido. Erros do certificado aparecem traduzidos (PT/EN/ES).
03/10/2026 · updated
- 1.0.0
Upload de certificado digital A1 (.pfx/.p12) em integrações SOAP/REST. Lib parsePkcs12 com node-forge extrai metadata (subject/issuer/serial/validade/fingerprint). API create-integration valida senha e encripta blob+senha com AES-256-GCM, gravando em connections.certificates jsonb. UI file input + password. Documentado em /security e help (slug=certificado-digital-mtls).
06/05/2026 · new