feat(xml): implementar assinatura digital no padrão XAdES-BES (ETSI TS 101 903 / ICP-Brasil)

This commit is contained in:
Claudecio Martins
2026-10-11 11:28:35 -03:00
parent 0cd4025f3c
commit 3fab68c118
9 changed files with 626 additions and 93 deletions
@@ -386,7 +386,7 @@ public final class BatchSigningDialog {
Path dest = targetDir.resolve(outPdfName);
pdfService.signPdfFile(item.getFile(), dest.toFile(), options);
} else if (lowerName.endsWith(".xml")) {
// Assinatura XMLDSig de documento XML
// Assinatura XAdES-BES de documento XML (ETSI TS 101 903 / ICP-Brasil)
br.com.cybercore.signer.core.xml.XmlSigningService xmlService =
new br.com.cybercore.signer.core.xml.XmlSigningService(certManager);
br.com.cybercore.signer.core.xml.XmlSignatureOptions options =
@@ -5,6 +5,7 @@ import br.com.cybercore.signer.core.domain.ManagedCertificate;
import br.com.cybercore.signer.core.provider.CertificateManager;
import br.com.cybercore.signer.core.security.PinSessionManager;
import br.com.cybercore.signer.core.xml.XmlSignatureOptions;
import br.com.cybercore.signer.core.xml.XmlSignatureProfile;
import br.com.cybercore.signer.core.xml.XmlSigningService;
import br.com.cybercore.signer.ui.components.SvgIcons;
import br.com.cybercore.signer.ui.theme.ThemeManager;
@@ -25,6 +26,7 @@ import javafx.scene.control.PasswordField;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Separator;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.ColumnConstraints;
@@ -68,7 +70,9 @@ public final class XmlSignDialog {
private PasswordField pinField;
private Label lblPinNotice;
// Controles de Modo de Assinatura
// Controles de Padrão e Modo de Assinatura
private RadioButton rbXadesBes;
private RadioButton rbXmlDsig;
private RadioButton rbWholeDocument;
private RadioButton rbSpecificElement;
private TextField txtElementTag;
@@ -94,10 +98,10 @@ public final class XmlSignDialog {
if (owner != null) {
this.stage.initOwner(owner);
}
this.stage.setTitle("CyberCore Signer — Assinatura Digital de XML (XMLDSig)");
this.stage.setTitle("CyberCore Signer — Assinatura Digital de XML (XAdES / ICP-Brasil)");
this.stage.setResizable(true);
this.stage.setMinWidth(720);
this.stage.setMinHeight(580);
this.stage.setMinHeight(600);
initLayout();
}
@@ -111,9 +115,9 @@ public final class XmlSignDialog {
HBox header = new HBox(14);
header.setAlignment(Pos.CENTER_LEFT);
VBox titleBox = new VBox(4);
Label lblTitle = new Label("ASSINATURA DE DOCUMENTO XML (XMLDSIG)");
Label lblTitle = new Label("ASSINATURA DE DOCUMENTO XML (XADES / ICP-BRASIL)");
lblTitle.getStyleClass().add("dialog-header");
Label lblSub = new Label("Assine digitalmente documentos XML no padrão W3C XMLDSig e ICP-Brasil (NF-e, CT-e, MDF-e, eSocial, etc.).");
Label lblSub = new Label("Assine digitalmente documentos XML no padrão avançado XAdES (ETSI TS 101 903 / ICP-Brasil DOC-ICP-15.03) ou XMLDSig.");
lblSub.getStyleClass().add("dialog-subtitle");
titleBox.getChildren().addAll(lblTitle, lblSub);
header.getChildren().addAll(SvgIcons.xmlIcon(), titleBox);
@@ -198,7 +202,21 @@ public final class XmlSignDialog {
// 3. Seção: Modo de Assinatura XMLDSig
VBox xmlModeSection = new VBox(12);
xmlModeSection.getStyleClass().add("dialog-box");
Label lblModeTitle = new Label("MODO DE ASSINATURA XMLDSIG");
Label lblProfileTitle = new Label("PADRÃO DE ASSINATURA XML");
lblProfileTitle.getStyleClass().add("dialog-field-title");
ToggleGroup profileGroup = new ToggleGroup();
rbXadesBes = new RadioButton("XAdES-BES (ETSI TS 101 903 / ICP-Brasil DOC-ICP-15.03 - Padrão Avançado)");
rbXadesBes.setToggleGroup(profileGroup);
rbXadesBes.setSelected(true);
rbXmlDsig = new RadioButton("XMLDSig Clássico W3C (Enveloped legado / NF-e SEFAZ)");
rbXmlDsig.setToggleGroup(profileGroup);
VBox profileBox = new VBox(8);
profileBox.getChildren().addAll(rbXadesBes, rbXmlDsig);
Label lblModeTitle = new Label("ESCOPO DE ASSINATURA");
lblModeTitle.getStyleClass().add("dialog-field-title");
ToggleGroup modeGroup = new ToggleGroup();
@@ -247,7 +265,16 @@ public final class XmlSignDialog {
chkValidateAfter = new CheckBox("Validar conformidade e integridade criptográfica após gravação");
chkValidateAfter.setSelected(true);
xmlModeSection.getChildren().addAll(lblModeTitle, rbWholeDocument, rbSpecificElement, elementConfigBox, chkValidateAfter);
xmlModeSection.getChildren().addAll(
lblProfileTitle,
profileBox,
new Separator(),
lblModeTitle,
rbWholeDocument,
rbSpecificElement,
elementConfigBox,
chkValidateAfter
);
formContent.getChildren().addAll(fileSection, certSection, xmlModeSection);
scrollPane.setContent(formContent);
@@ -389,6 +416,10 @@ public final class XmlSignDialog {
pin = pinField.getText().toCharArray();
}
XmlSignatureProfile profile = rbXadesBes.isSelected()
? XmlSignatureProfile.XADES_BES
: XmlSignatureProfile.XMLDSIG;
XmlSignatureOptions options;
if (rbSpecificElement.isSelected()) {
String tag = txtElementTag.getText().trim();
@@ -396,14 +427,14 @@ public final class XmlSignDialog {
if (tag.isBlank()) {
tag = "infNFe";
}
options = XmlSignatureOptions.forElement(cert.id(), pin, tag, id.isBlank() ? null : id);
options = XmlSignatureOptions.forElement(cert.id(), pin, tag, id.isBlank() ? null : id, profile);
} else {
options = XmlSignatureOptions.wholeDocument(cert.id(), pin);
options = XmlSignatureOptions.wholeDocument(cert.id(), pin, profile);
}
setFormDisabled(true);
progressIndicator.setVisible(true);
lblStatus.setText("Assinando documento XML (XMLDSig)...");
lblStatus.setText("Assinando documento XML (" + profile.getDisplayName() + ")...");
lblStatus.setStyle("-fx-text-fill: -fx-accent;");
boolean validate = chkValidateAfter.isSelected();