feat(ui): enhance signature confirmation dialog with wysiwys details and update ccsp spec

This commit is contained in:
Claudecio Martins
2026-10-20 14:56:08 -03:00
parent 1a727e6ff5
commit 66459bfae4
4 changed files with 82 additions and 11 deletions
@@ -472,9 +472,27 @@ public class CyberCoreDesktopApplication extends Application {
.map(AuthorizedApplication::name)
.orElse(request.applicationId());
Long docSize = request.documentSize();
if (docSize == null && request.pdf() != null) {
try {
docSize = (long) Base64.getDecoder().decode(request.pdf()).length;
} catch (Exception ignored) {}
} else if (docSize == null && request.xmlBase64() != null) {
try {
docSize = (long) Base64.getDecoder().decode(request.xmlBase64()).length;
} catch (Exception ignored) {}
}
String requestOrigin = request.origin();
if (requestOrigin == null || requestOrigin.isBlank()) {
requestOrigin = ApplicationRegistry.getInstance().findById(request.applicationId())
.map(a -> String.join(", ", a.allowedOrigins()))
.orElse("Requisição Web Local");
}
SignaturePromptDialog dialog = new SignaturePromptDialog(
stage, appName, "Requisição Web CCSP", certificate, context,
request.documentName(), request.documentType(), request.isAuthentication()
stage, appName, requestOrigin, certificate, context,
request.documentName(), request.documentType(), docSize, request.isAuthentication()
);
dialog.showAndAwait().whenComplete((approved, err) -> {
if (err != null) {
@@ -36,12 +36,18 @@ public class SignaturePromptDialog {
public SignaturePromptDialog(Stage owner, String applicationName, String origin,
ManagedCertificate certificate, SigningContext context) {
this(owner, applicationName, origin, certificate, context, null, null, false);
this(owner, applicationName, origin, certificate, context, null, null, null, false);
}
public SignaturePromptDialog(Stage owner, String applicationName, String origin,
ManagedCertificate certificate, SigningContext context,
String documentName, String documentType, boolean isAuthentication) {
this(owner, applicationName, origin, certificate, context, documentName, documentType, null, isAuthentication);
}
public SignaturePromptDialog(Stage owner, String applicationName, String origin,
ManagedCertificate certificate, SigningContext context,
String documentName, String documentType, Long documentSize, boolean isAuthentication) {
stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
if (owner != null) {
@@ -52,11 +58,11 @@ public class SignaturePromptDialog {
: "CyberCore Signer — Solicitação de Assinatura");
stage.setResizable(false);
initLayout(applicationName, origin, certificate, context, documentName, documentType, isAuthentication);
initLayout(applicationName, origin, certificate, context, documentName, documentType, documentSize, isAuthentication);
}
private void initLayout(String appName, String origin, ManagedCertificate cert, SigningContext context,
String documentName, String documentType, boolean isAuthentication) {
String documentName, String documentType, Long documentSize, boolean isAuthentication) {
VBox root = new VBox(16);
root.getStyleClass().add("dialog-container");
root.setPadding(new Insets(20, 24, 20, 24));
@@ -94,7 +100,8 @@ public class SignaturePromptDialog {
grid.setVgap(10);
int row = 0;
addField(grid, row++, "Sistema:", appName + " (" + (origin != null ? origin : "Local") + ")");
addField(grid, row++, "Aplicação Solicitante:", appName);
addField(grid, row++, "Origem Web:", (origin != null && !origin.isBlank() ? origin : "Local"));
if (isAuthentication) {
addField(grid, row++, "Operação:", "Autenticação de Usuário (Challenge-Response)");
} else {
@@ -102,7 +109,12 @@ public class SignaturePromptDialog {
String docLabel = (documentType != null && !documentType.isBlank())
? "[" + documentType.toUpperCase() + "] " + documentName
: documentName;
if (documentSize != null && documentSize > 0) {
docLabel += " (" + formatFileSize(documentSize) + ")";
}
addField(grid, row++, "Documento:", docLabel);
} else if (documentSize != null && documentSize > 0) {
addField(grid, row++, "Tamanho do Documento:", formatFileSize(documentSize));
}
addField(grid, row++, "Operação:", "Assinatura Digital (Digest)");
}
@@ -128,14 +140,16 @@ public class SignaturePromptDialog {
detailsBox.getChildren().add(grid);
// Caixa do Hash / Digest
// Caixa do Hash / Digest formatado em blocos legíveis (WYSIWYS)
VBox hashBox = new VBox(6);
Label hashLabel = new Label("Hash do Documento (SHA-256 Digest):");
Label hashLabel = new Label("Hash do Documento (SHA-256 Digest formatado):");
hashLabel.getStyleClass().add("dialog-field-title");
String hashHex = HexFormat.of().formatHex(context.digest());
Label hashValue = new Label(hashHex);
String formattedHash = formatHashBlocks(hashHex);
Label hashValue = new Label(formattedHash);
hashValue.getStyleClass().add("dialog-hash-value");
hashValue.setStyle("-fx-font-family: 'Liberation Mono', 'Courier New', monospace; -fx-font-size: 11px;");
hashValue.setWrapText(true);
hashBox.getChildren().addAll(hashLabel, hashValue);
@@ -301,4 +315,27 @@ public class SignaturePromptDialog {
stage.toFront();
return approvalFuture;
}
private static String formatFileSize(long bytes) {
if (bytes < 1024) {
return bytes + " B";
} else if (bytes < 1024 * 1024) {
return String.format(java.util.Locale.ROOT, "%.1f KB", bytes / 1024.0);
} else {
return String.format(java.util.Locale.ROOT, "%.1f MB", bytes / (1024.0 * 1024.0));
}
}
private static String formatHashBlocks(String hex) {
if (hex == null || hex.isBlank()) return "";
StringBuilder sb = new StringBuilder();
String upper = hex.toUpperCase(java.util.Locale.ROOT);
for (int i = 0; i < upper.length(); i++) {
if (i > 0 && i % 4 == 0) {
sb.append(' ');
}
sb.append(upper.charAt(i));
}
return sb.toString();
}
}