Skip to main content

Digital Signing and E-Sign

Module: suredms-desktop-client-signing
Source root: SC/suredms-desktop-client-signing/src/main/java/com/sureclinical/suredms/esign/

This module manages the complete digital signing and electronic signature stack for the desktop client. It provides a mode-routing proxy, a remote Nuxeo signing service, an offline stub, custom PDF signature appearance management, signing wizards, and signing-related report generators.

Purpose

suredms-desktop-client-signing lets the rest of the desktop application trigger signings without knowing whether a live Nuxeo server is available. ESignServiceProxy makes this decision at construction time by reading the active EndPointsType. All callers depend on the ESignService interface — they never reference a concrete signing class.

Package Structure

PackageContents
com.sureclinical.suredms.esignESignServiceProxy, ClientCustomSignatureAppearanceProvider
com.sureclinical.suredms.esign.implRemoteESignService, RemoteESignServiceOfflineStub
com.sureclinical.suredms.esign.reportReportSignatureValidation, ReportSigningHistoryAuditTrail
com.sureclinical.suredms.esign.uiDigitalIdVerificationDialog (referenced from the shell's security package)
com.sureclinical.suredms.esign.ui.settingsSigning settings UI components

The SignLocalFileWizardBuilder (3-step local file signing wizard) lives in the shell module at com.sureclinical.suredms.ui.wizard.sign.

ESignService (interface)

SC/suredms-common/...services/esign/ESignService.java

The contract all callers use. Key methods:

MethodDescription
isSigningAllowed()Whether the current user has the user:signer role
getCertificate(String username)Returns a CertificateDescription or null if none active
signFile(NamedFile, ESignSettings)Signs a local file and returns the signed NamedFile
signDocument(Document, ESignSettings)Signs a Nuxeo-tracked document
isPasswordRequired()Whether a PIN/password is required for signing
showCertificateSetupDialog()Opens the certificate enrollment flow
initSigningProcess(String documentId)Server-side signing initiation
requiresVerificationCodeAndPasswordForSigning(String documentId)Returns a Tuple<Boolean, Boolean> — (needsCode, needsPassword)

ESignServiceProxy

SC/suredms-desktop-client-signing/src/main/java/com/sureclinical/suredms/esign/ESignServiceProxy.java

Extends AbstractClientService. Implements ESignService. Constructed at login time.

public ESignServiceProxy() {
User user = DesktopClient.getInstance().getUser();
remoteEnabled = user.isRemoteSigner();
if (EndPoints.getCurrentEndPoints().getEndPointsType() == EndPointsType.EP_REMOTE) {
this.service = new RemoteESignService();
} else {
this.service = new RemoteESignServiceOfflineStub();
}
}

Key behavior:

  • remoteEnabled is set at construction and never changes during a session. It reflects user.isRemoteSigner() which checks the user:signer Nuxeo property.
  • isSigningAllowed() returns remoteEnabled. If false, the ribbon signing actions are disabled regardless of connection state.
  • All ESignService interface method calls are delegated directly to the inner service (either RemoteESignService or RemoteESignServiceOfflineStub).
  • The proxy cannot switch the inner service mid-session. Each login creates a new proxy.

RemoteESignService

SC/suredms-desktop-client-signing/src/main/java/com/sureclinical/suredms/esign/impl/RemoteESignService.java

Extends AbstractClientService. Implements ESignService and RemoteCertificateService.

Certificate Caching

Uses a Guava LoadingCache for certificate lookups:

CacheBuilder.newBuilder()
.expireAfterWrite(...)
.build(new CacheLoader<...>() { ... })

getCertificate(username):

  • Checks getCertificateStatus(username).
  • Returns new CertificateDescription("cert") if status is CertificateStatus.ACTIVE.
  • Returns null otherwise (no certificate configured or not active).

Signing Request Parameters

All signing Nuxeo operation requests use these parameter keys:

  • PASSWORD_REQUEST_PARAM = "password"
  • VERIFICATION_CODE_REQUEST_PARAM = "verificationCode"
  • USERNAME_REQUEST_PARAM = "username"

Priority

getServicePriority() returns PRIORITY_HIGH when EndPointsType.EP_REMOTE, PRIORITY_LOW otherwise. Used by ServiceProvider to order competing service implementations.

User Resolution

getCurrentUser() first tries EndPoints.getCurrentEndPoints().getAuthSvc().getUserInfo(); if that fails, falls back to DesktopClient.getInstance().getUser(). This handles the case where signing is triggered before the full session is established.

Exception Types

RemoteESignService throws specific signing exceptions from com.sureclinical.suredms.common.sign.exception:

  • ESignException — general signing failure
  • ESignPasswordException — incorrect or missing password/PIN
  • ESignProtectedDocumentException — document is locked against signing
  • CertificateStatus checks gate all signing attempts.

RemoteESignServiceOfflineStub

SC/suredms-desktop-client-signing/src/main/java/com/sureclinical/suredms/esign/impl/RemoteESignServiceOfflineStub.java

Offline fallback used when EndPointsType is not EP_REMOTE. Returns safe no-op or stub results for all ESignService methods. Signing operations that strictly require remote connectivity are either queued or return a meaningful error to the caller.

ClientCustomSignatureAppearanceProvider

SC/suredms-desktop-client-signing/src/main/java/com/sureclinical/suredms/esign/ClientCustomSignatureAppearanceProvider.java

Extends AbstractClientService. Implements CustomSignatureAppearanceProvider.

Manages the library of named custom signature appearances (image-based signature blocks stored server-side). All operations are no-ops when endPointsType != EP_REMOTE.

MethodNuxeo OperationDescription
addAppearance(appearance)SureOperations.OP_APPEARANCE_CREATEUploads an appearance image blob with a name
deleteAppearance(username, name)SureOperations.OP_APPEARANCE_DELETERemoves an appearance by name
getAppearances(username)SureOperations.OP_APPEARANCE_QUERYReturns all appearances for the user
getAppearance(username, style)(iterates getAppearances)Returns a single appearance by style name

Appearances are sent to the server as ByteArrayInputStream blobs via client.asInput(...).

getAppearances() checks NuxeoClientPool.isInitialized() before attempting a query; during account verification the user is not yet logged in, so it returns an empty list safely.

Signing Wizards

The local-file signing wizard lives in the shell module:

SignLocalFileWizardBuilder

SC/suredms-desktop-client/src/main/java/com/sureclinical/suredms/ui/wizard/sign/SignLocalFileWizardBuilder.java

Extends WizardBuilder. Dialog size: 1024 × 768, resizable. Three steps:

StepClassDescription
1SelectFileWizardStepUser picks a local PDF file to sign
2SignLocalFileWizardStepSigns the file using ESignService; shows progress
3DownloadSignedFileWizardStepUser saves the signed PDF to disk

performWizardAction() is a no-op — the signing itself happens inside Step 2.

SignLocalFileWizardContext carries the selected file and the signed result between steps.

Signing Reports

ClassPurpose
ReportSignatureValidationGenerates a report validating all signatures on a document
ReportSigningHistoryAuditTrailGenerates a chronological signing-history report for an archive or document

Both integrate with ReportService from the shell module and are accessible from the quality module's report actions panel.

Full Signing Flow

Document signing (remote)

Caller (ribbon action or quality view)
└── ESignServiceProxy.signDocument(document, settings)
└── RemoteESignService.signDocument(...)
├── initSigningProcess(documentId) [Nuxeo]
├── requiresVerificationCodeAndPassword [Nuxeo → Tuple<Boolean,Boolean>]
├── (if required) DigitalIdVerificationDialog → user enters PIN/code
└── sign Nuxeo operation with {username, password, verificationCode}
└── update Document entity signing fields

Signature appearance management

User opens Signing Settings dialog
└── ClientCustomSignatureAppearanceProvider.getAppearances(username)
└── SureOperations.OP_APPEARANCE_QUERY [Nuxeo]
└── User uploads new image
└── ClientCustomSignatureAppearanceProvider.addAppearance(...)
└── SureOperations.OP_APPEARANCE_CREATE [Nuxeo blob upload]

Local file signing

Ribbon: Sign Local File
└── SignLocalFileWizardBuilder (3 steps)
├── Step 1: SelectFileWizardStep → pick PDF
├── Step 2: SignLocalFileWizardStep → ESignService.signFile(file, settings)
└── Step 3: DownloadSignedFileWizardStep → save to disk

Certificate Handling

  • CertificateStatus.ACTIVE is required before any signing request proceeds.
  • Certificate status is fetched via Nuxeo and cached in RemoteESignService's Guava LoadingCache.
  • showCertificateSetupDialog() opens the enrollment flow (e.g., DigitalIdSignupDialog in the shell's security package).
  • Certificate chain validation and trust verification happen server-side.
  • DigitalIdVerificationDialog prompts only when requiresVerificationCodeAndPasswordForSigning() returns true for the relevant flag.

ESignSettings — Signing Parameters Object

ESignSettings is a cloneable parameter object that carries all configuration for a single signing operation. It is passed from the wizard or ribbon action to ESignServiceProxy.signDocument() / signFile().

Located at: suredms-desktop-client-connector/src/main/java/com/sureclinical/suredms/services/esign/ESignSettings.java

Fields

FieldTypeDescription
verificationCodeStringMFA or OTP verification code (if required)
certificationLevelintCertificationLevel value: NOT_CERTIFIED or CERTIFIED_NO_CHANGES_ALLOWED
ownerPasswordStringPDF owner password (for certification)
signatureLocationRectanglePixel rectangle for the visual signature placement
imageFileFileImage to use as the signature appearance
pageIntegerPage number (1-based) where the signature is placed
reasonStringSigning reason text embedded in the signature
passwordStringUser's signing password / PIN
locationStringPhysical location string embedded in the signature
appearanceIdStringNamed custom appearance ID from ClientCustomSignatureAppearanceProvider
fixedAppearancebooleanIf true, the user may not change the appearance
allowSelectLocationbooleanIf false, the location field is read-only in UI (default true)
allowSelectAppearancebooleanIf false, the appearance picker is hidden (default true)
allowSelectReasonbooleanIf false, the reason drop-down is hidden (default true)
fixedSignatureLocationbooleanIf true, the signature rectangle is pre-set and cannot be moved
certifyNotAllowedbooleanPrevents certifying this document via UI toggle
workflowTaskIdStringAssociated workflow task ID for task-gated signing scenarios

Key helpers

  • setCertifyRequired(boolean) — sets certificationLevel to CERTIFIED_NO_CHANGES_ALLOWED or NOT_CERTIFIED.
  • isInitial() — returns true if appearanceId equals SignatureAppearanceProvider.INITIALS_APPEARANCE_ID.
  • clone()ESignSettings is Cloneable; wizards clone the settings before mutating them per-step.

Dependencies

ModuleRole
suredms-desktop-clientShell hosting, ribbon, SignLocalFileWizardBuilder, DigitalIdVerificationDialog
suredms-desktop-client-dataDocument, User (including isRemoteSigner())
suredms-desktop-client-connectorEndPoints, NuxeoClientPool, NuxeoClientImpl, SureOperationRequest

Constraints and Notes

  • ESignServiceProxy.remoteEnabled is evaluated once at construction. Revoking the user:signer role mid-session has no effect until the next login.
  • ClientCustomSignatureAppearanceProvider captures endPointsType at construction; if the endpoint changes mid-session the appearance provider will use the old type.
  • RemoteESignServiceOfflineStub is a true stub — it does not queue signing operations for later delivery.
  • Signing credentials (password, verification code) are passed as request parameters and are not retained in memory after the signing call returns.
  • NuxeoClientPool.isInitialized() must be checked before calling any NuxeoClientPool method during the early login phase.