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.