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
@@ -116,7 +116,13 @@ public record CcspRequest(
String targetElementTag,
@JsonProperty("target_element_id")
String targetElementId
String targetElementId,
@JsonProperty("document_size")
Long documentSize,
@JsonProperty("origin")
String origin
) implements Serializable {
public static final String PROTOCOL_VERSION = "1";
@@ -130,7 +136,8 @@ public record CcspRequest(
certificateHint, expiresAt, timestamp, nonce, signatureType,
null, null, null, null,
null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null);
null, null, null, null, null, null, null, null, null, null,
null, null);
}
public boolean isVersionSupported() {
@@ -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();
}
}