Certificado digital¶
A emissão de documentos fiscais eletrônicos depende de um certificado digital da ICP-Brasil. Ele cumpre dois papéis distintos, que vale separar na cabeça:
- Assina o XML. O grupo
infNFeé assinado com a chave privada, e a assinatura viaja dentro do documento. - Identifica o cliente na conexão. Os serviços da SEFAZ exigem autenticação mútua TLS: o mesmo certificado prova, no handshake, quem está se conectando.
A1 e A3¶
| A1 | A3 | |
|---|---|---|
| Onde fica a chave | Em arquivo .pfx ou .p12 |
Em token USB, cartão inteligente ou HSM |
| Validade típica | 1 ano | 1 a 3 anos |
| Automação | Direta | Depende de driver PKCS#11 e presença física |
| Suportado pelo GoNFE | Sim, nativamente | Pela interface xmldsig.Assinante |
O pacote certificado
cobre A1 inteiramente, em Go puro e sem CGO. Certificados A3 podem ser usados
implementando xmldsig.Assinante — veja Assinatura digital.
Carregando¶
Há variantes para quem já tem os bytes em memória ou um io.Reader:
cert, err := certificado.Carregar(dados, senha) // []byte
cert, err := certificado.CarregarDe(reader, senha) // io.Reader
O carregamento aceita tanto os algoritmos antigos — 3DES com SHA-1, ainda comuns em certificados exportados por sistemas legados — quanto os modernos, AES-256 com PBES2, que as autoridades certificadoras emitem hoje.
Inspecionando¶
fmt.Println(cert.RazaoSocial()) // COMERCIO EXEMPLO LTDA
fmt.Println(cert.CNPJ()) // 12345678000195
fmt.Println(cert.Emissor()) // AC SOLUTI Multipla v5
fmt.Println(cert.ValidoAte()) // 2027-03-04 12:00:00 -0300
fmt.Println(cert.DiasParaVencer())
fmt.Println(cert.Descrever()) // resumo de uma linha para log
O CNPJ vem da extensão subjectAltName, onde a ICP-Brasil o registra sob o OID
2.16.76.1.3.3. Quando a extensão não está presente, a biblioteca recorre ao
sufixo do nome comum, que na ICP-Brasil tem o formato RAZAO SOCIAL:CNPJ. Em
certificados e-CPF, CPF() faz o equivalente com o OID 2.16.76.1.3.1.
Vigiando a validade¶
Certificado vencido derruba a emissão sem aviso prévio. Vale conferir na inicialização do sistema e alertar com antecedência:
if err := cert.ValidoEm(time.Now()); err != nil {
return fmt.Errorf("certificado inutilizável: %w", err)
}
if dias := cert.DiasParaVencer(); dias < 30 {
log.Printf("ATENÇÃO: o certificado vence em %d dias", dias)
}
Guardando a senha¶
A senha do certificado é a chave do cofre
Quem tem o .pfx e a senha consegue emitir documentos fiscais em nome da
empresa. Trate os dois com o mesmo cuidado que você trataria uma senha de
banco.
Recomendações práticas:
- Nunca versione o
.pfxnem a senha. O.gitignoredo projeto já bloqueia*.pfx,*.p12,*.peme*.key. - Leia a senha de variável de ambiente, de um cofre de segredos ou do gerenciador de segredos da sua nuvem — não de arquivo de configuração em texto claro.
- Em contêiner, monte o certificado como segredo, não como camada de imagem.
- Restrinja a permissão do arquivo:
chmod 600 certificado.pfx.
Cadeia de certificação¶
Alguns serviços da SEFAZ exigem a cadeia completa no handshake TLS. Quando o
arquivo PKCS#12 traz os intermediários, eles são carregados em cert.Cadeia e
cert.TLS() os inclui automaticamente. Se o seu .pfx só tem o certificado do
titular e o handshake falhar, exporte-o de novo incluindo a cadeia.
Na direção contrária, para validar o certificado do servidor da SEFAZ, o Go usa
o armazenamento de raízes do sistema operacional. Em imagens scratch, copie o
ca-certificates.crt para dentro da imagem.
Testes sem certificado real¶
O pacote interno certtest gera certificados sintéticos com a estrutura de um
A1 da ICP-Brasil — inclusive os OIDs do CNPJ no subjectAltName — e é o que a
própria suíte de testes usa. Ele não é exportado, mas o
código
serve de referência para montar o equivalente no seu projeto:
// resumo do que certtest faz
chave, _ := rsa.GenerateKey(rand.Reader, 2048)
modelo := x509.Certificate{ /* CN "EMPRESA:CNPJ", SAN com OID 2.16.76.1.3.3 */ }
der, _ := x509.CreateCertificate(rand.Reader, &modelo, caCert, &chave.PublicKey, caChave)
folha, _ := x509.ParseCertificate(der)
cert, _ := certificado.De(chave, folha, caCert)
certificado.De é público justamente para permitir esse caminho: qualquer
*rsa.PrivateKey com o *x509.Certificate correspondente vira um
*certificado.Certificado utilizável.