Skip to content

feat: transmitir eventos já assinados fora da biblioteca - #182

Merged
robertorp merged 1 commit into
masterfrom
ra/feat-sobre-carga-metodos-assinatura-opcional
Jul 30, 2026
Merged

feat: transmitir eventos já assinados fora da biblioteca#182
robertorp merged 1 commit into
masterfrom
ra/feat-sobre-carga-metodos-assinatura-opcional

Conversation

@robertorp

@robertorp robertorp commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

O problema

Em arquiteturas onde a emissão roda num servidor mas o certificado A3 é do
cliente e vive na máquina dele
, o servidor não tem — e não pode ter — a chave
privada usada para assinar. Ele fala com a SEFAZ com outro certificado, só para
o TLS. A assinatura precisa acontecer fora do servidor.

Para a emissão isso já funciona: NFeAutorizacao4 recebe List<NFe>
assinadas e apenas transmite. Para eventos e inutilização faltavam duas
peças:

1. 7 dos 11 serviços de evento não tinham como receber o objeto já assinado.
Só cancelamento, carta de correção, EPEC e perecimento tinham a sobrecarga que
recebe List<evento>. Os demais — incluindo a Manifestação do Destinatário —
existiam apenas na forma acoplada, que monta e assina com o certificado do
próprio processo. Quem assina fora do servidor simplesmente não conseguia usar
esses eventos.

2. Não havia como obter o @Id sem assinar.
O Id do elemento assinável (infEvento/@Id, infInut/@Id) só era calculado
dentro dos métodos que assinam. Como Assina() lança exceção se o Id vier
nulo, quem montasse o objeto por fora precisava reimplementar a regra de
formação na mão. Errar um dígito não dá erro em lugar nenhum: o digest deixa de
bater e a SEFAZ rejeita a assinatura.

O que foi feito

  • Sobrecarga RecepcaoEventoX(int idlote, List<evento> eventos, bool assinar = false)
    para os 7 serviços que faltavam. Com assinar: false (padrão) o evento é
    transmitido exatamente como veio; com true a biblioteca calcula o Id e
    assina com o certificado da instância.
  • Extevento.ObterId(tpEvento, chNFe, nSeqEvento) e
    ExtinutNFe.ObterId(cUF, ano, cnpj, modelo, serie, nNFIni, nNFFin)
    públicos, estáticos e de uso opcional.
  • Testes de vetor ouro dos dois formatos de Id.

Cancelamento por substituição não precisou de método novo: usa o mesmo
ServicoNFe.RecepcaoEventoCancelmento, então a sobrecarga de cancelamento que
já existia atende.

Fluxo completo que passa a ser possível:

// servidor: monta e resolve o Id, sem assinar
evento.infEvento.Id = Extevento.ObterId(tpEvento, chNFe, nSeqEvento);
var xml = evento.ObterXmlString();          // vai para a máquina cliente

// máquina cliente: assina com o A3
var naBorda = FuncoesXml.XmlStringParaClasse<evento>(xml);
naBorda.Assina(certificadoA3,
               cfg.Certificado.SignatureMethodSignedXml,
               cfg.Certificado.DigestMethodReference,
               cfg.RemoverAcentos);          // volta assinado

// servidor: só transmite
servicos.RecepcaoEventoManifestacaoDestinatario(idLote, new List<evento> { assinado });Atenção ao assinar na borda: removerAcentos tem default false emAssina(). Se o servidor usa RemoverAcentos = true, os três parâmetros de
▎ assinatura precisam ser os mesmos dos dois lados, senão o digest é calculado
▎ sobre um XML e transmitido outro.

Compatibilidade

Mudança puramente aditiva: 0 remoções. Nenhuma assinatura,
retorno ou corpo de método existente foi alterado.

@danilobreda
danilobreda self-requested a review July 29, 2026 20:02
@robertorp
robertorp merged commit 6102dd7 into master Jul 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants