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.
Versão: 4.1.1 (DATA_VERSAO=14/06/2026)
Afeta:
CancelamentoUtil.criaProcEventoCancelamento,CartaCorrecaoUtil.criaProcEventoCCe,CancelamentoSubstituicaoUtil.criaProcEventoCancelamentoe qualquer round-trip JAXB de evento assinado.Não afeta: o
nfeProcda NF-e (o pacoteschemasestá correto).Resumo
O
procEventoNFeproduzido pelos utilitárioscriaProcEvento*sai com a assinatura vazia: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: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_eventosperderam o
namespaceno@XmlType. Comparando os mesmos tipos nos dois pacotes:schemasschemas_eventosSignatureType@XmlType(namespace="http://www.w3.org/2000/09/xmldsig#")SignedInfoTypeSignatureValueTypeKeyInfoTypeX509DataTypeReferenceTypeTransformTypeTransformsTypeVerificando no jar publicado:
Sem o
namespaceno@XmlType, o tipo cai no default dopackage-infodeschemas_eventos(http://www.portalfiscal.inf.br/nfe,elementFormDefault=QUALIFIED).Como consequência os elementos-filhos
SignedInfo,SignatureValueeKeyInfopassam aser 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
SignatureTypeinstanciado porém vazio.O campo
signatureemTEventoCancelamento/TEventoCartaCorrecao/ etc. está anotadocorretamente (
@XmlElement(name="Signature", namespace="http://www.w3.org/2000/09/xmldsig#"));o problema está no tipo, não no campo.
Por que o
replacesNfenão consertaXmlNfeUtil.replacesNfetem o reparo: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
O mesmo teste com
TNfeProc(pacoteschemas) preserva tudo — round-trip de umnfeProcreal de 8380 bytes devolve os mesmos 8380 bytes, com
SignedInfo,SignatureValue,DigestValueeX509Certificateintactos. É a diferença do@XmlType.Correção sugerida
Acrescentar
namespace = "http://www.w3.org/2000/09/xmldsig#"ao@XmlTypedos 8 tiposlistados acima em
schemas_eventos, alinhando com o que já existe emschemas. Se asclasses 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 unmarshalnão lança erro nenhum.
Sugestão adicional de API: expor o XML assinado
Hoje
Nfe.cancelarNfe,Nfe.cce,Nfe.atorInteressadoNFeetc. assinam o lote internamentee devolvem só o objeto de retorno — o XML assinado é descartado. Para montar o
procEventoNFe, oscriaProcEvento*precisam assinar tudo de novo. Nos nossos logsaparecem 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 peloprazo 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
procEventoNFerecortando o<evento>...</evento>do loteassinado como texto e concatenando com o
<retEvento>serializado à parte, sem passar aassinatura de volta pelo JAXB. O
<infEvento>mantém o mesmo namespace default herdado nosdois documentos, então a canonicalização inclusiva não muda e o digest continua válido.