Skip to content

procEventoNFe gerado sem assinatura: tipos dsig de schemas_eventos perderam o namespace no @XmlType (4.1.1) #352

Description

@edivaldoms

Versão: 4.1.1 (DATA_VERSAO=14/06/2026)
Afeta: CancelamentoUtil.criaProcEventoCancelamento, CartaCorrecaoUtil.criaProcEventoCCe, CancelamentoSubstituicaoUtil.criaProcEventoCancelamento e qualquer round-trip JAXB de evento assinado.
Não afeta: o nfeProc da NF-e (o pacote schemas está correto).

Resumo

O procEventoNFe produzido pelos utilitários criaProcEvento* sai com a assinatura vazia:

<evento versao="1.00"><infEvento Id="ID110111...">...</infEvento><Signature/></evento>

Como o <Signature/> fica sem namespace próprio, ele herda o default do documento
(http://www.portalfiscal.inf.br/nfe) e o arquivo reprova na validação de schema:

The element 'evento' in namespace 'http://www.portalfiscal.inf.br/nfe' has invalid
child element 'Signature' in namespace 'http://www.portalfiscal.inf.br/nfe'.
List of possible elements expected: 'Signature' in namespace 'http://www.w3.org/2000/09/xmldsig#'.

O evento é transmitido e autorizado normalmente (cStat 135) — o XML enviado à SEFAZ está
correto e assinado. O problema é só no arquivo que precisamos deixar gravados por 5 anos, e sem assinatura, não tem validade jurídica.

Causa raiz

Na consolidação de pacotes da 4.1.1, os tipos XMLDSig duplicados em schemas_eventos
perderam o namespace no @XmlType. Comparando os mesmos tipos nos dois pacotes:

Tipo schemas schemas_eventos
SignatureType @XmlType(namespace="http://www.w3.org/2000/09/xmldsig#") (sem namespace)
SignedInfoType idem (sem namespace)
SignatureValueType idem (sem namespace)
KeyInfoType idem (sem namespace)
X509DataType idem (sem namespace)
ReferenceType idem (sem namespace)
TransformType idem (sem namespace)
TransformsType idem (sem namespace)

Verificando no jar publicado:

$ javap -v br/com/swconsultoria/nfe/schemas/SignatureType.class | grep -A3 'XmlType('
    javax.xml.bind.annotation.XmlType(
      name="SignatureType"
      namespace="http://www.w3.org/2000/09/xmldsig#"      <-- correto

$ javap -v br/com/swconsultoria/nfe/schemas_eventos/SignatureType.class | grep -A3 'XmlType('
    javax.xml.bind.annotation.XmlType(
      name="SignatureType"
      propOrder=["signedInfo","signatureValue","keyInfo"]  <-- faltou o namespace

Sem o namespace no @XmlType, o tipo cai no default do package-info de
schemas_eventos (http://www.portalfiscal.inf.br/nfe, elementFormDefault=QUALIFIED).
Como consequência os elementos-filhos SignedInfo, SignatureValue e KeyInfo passam a
ser esperados no namespace do portal fiscal. Os elementos reais vêm em
http://www.w3.org/2000/09/xmldsig#, não casam, e o unmarshal os descarta em silêncio —
sobra um SignatureType instanciado porém vazio.

O campo signature em TEventoCancelamento / TEventoCartaCorrecao / etc. está anotado
corretamente (@XmlElement(name="Signature", namespace="http://www.w3.org/2000/09/xmldsig#"));
o problema está no tipo, não no campo.

Por que o replacesNfe não conserta

XmlNfeUtil.replacesNfe tem o reparo:

.replace("<Signature>", "<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">")

Ele só casa com a tag de abertura. Como o objeto ficou vazio, o marshaller emite a forma
autofechada <Signature/>, que escapa do replace e permanece no namespace errado.

Como reproduzir

// 1. lote assinado (o mesmo que foi enviado e autorizado pela SEFAZ)
String assinado = Assinar.assinaNfe(config, xmlDoLote, AssinaturaEnum.EVENTO);
// contém: <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo>...

// 2. round-trip pelas classes de schemas_eventos
TEnvEventoCancelamento lote = XmlNfeUtil.xmlToObject(assinado, TEnvEventoCancelamento.class);
TEventoCancelamento evento = lote.getEvento().get(0);

System.out.println(evento.getSignature());                 // objeto != null
System.out.println(evento.getSignature().getSignedInfo()); // null  <-- perdido
System.out.println(evento.getSignature().getKeyInfo());    // null  <-- perdido

O mesmo teste com TNfeProc (pacote schemas) preserva tudo — round-trip de um nfeProc
real de 8380 bytes devolve os mesmos 8380 bytes, com SignedInfo, SignatureValue,
DigestValue e X509Certificate intactos. É a diferença do @XmlType.

Correção sugerida

Acrescentar namespace = "http://www.w3.org/2000/09/xmldsig#" ao @XmlType dos 8 tipos
listados acima em schemas_eventos, alinhando com o que já existe em schemas. Se as
classes forem geradas por XJC, provavelmente basta corrigir o binding/XSD de origem dos
eventos para que o namespace dsig seja emitido, como acontece no da NF-e.

Sugestão de teste de regressão: assinar um evento, fazer o round-trip e afirmar que
getSignature().getSignedInfo() != null — hoje isso passa despercebido porque o unmarshal
não lança erro nenhum.

Sugestão adicional de API: expor o XML assinado

Hoje Nfe.cancelarNfe, Nfe.cce, Nfe.atorInteressadoNFe etc. assinam o lote internamente
e devolvem só o objeto de retorno — o XML assinado é descartado. Para montar o
procEventoNFe, os criaProcEvento* precisam assinar tudo de novo. Nos nossos logs
aparecem duas cargas de certificado por cancelamento, uma para o envio e outra para gravar o
arquivo.

Seria melhor se o XML assinado do lote ficasse acessível ao chamador (num campo do retorno,
ou como retorno de um método dedicado). Além de evitar a segunda assinatura, garante que o
arquivo guardado seja exatamente o documento transmitido, e não uma reconstrução —
qualquer divergência de canonicalização entre as duas assinaturas passa a ser impossível por
construção.

Por que isso importa

O procEventoNFe é o documento de arquivo do evento: é ele que precisa ser guardado pelo
prazo exigido pela legislação fiscal — na nossa operação, 5 anos. Gravado sem assinatura ele
não serve para essa finalidade: não valida contra o XSD e não é verificável
criptograficamente anos depois, quando o certificado que assinou já expirou e o XML é a única
prova de que o evento existiu e partiu do emitente.

Agrava o fato de a falha ser silenciosa: o evento é autorizado, o retorno é 135, o arquivo é
gravado e nada no log indica problema. Só se descobre quando alguém valida os arquivos —
possivelmente anos depois, em fiscalização, quando não há mais como regerar a assinatura.

Contorno adotado

Enquanto isso, montamos o procEventoNFe recortando o <evento>...</evento> do lote
assinado como texto e concatenando com o <retEvento> serializado à parte, sem passar a
assinatura de volta pelo JAXB. O <infEvento> mantém o mesmo namespace default herdado nos
dois documentos, então a canonicalização inclusiva não muda e o digest continua válido.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions