Skip to main content
Version: v0.13

eIDAS Signature Client

The eIDAS signature client provides a command-based API for signing, validating, and timestamping documents. The client integrates with the IDK's key management system for automatic key resolution.

Commands​

CommandDescription
SignDocumentCommandSign a document in one step
CreateDigestCommandCreate digest for remote signing (step 1)
CompleteSignatureCommandComplete remote signature (step 2)
ValidateSignatureCommandValidate existing signatures
TimestampCommandRequest RFC 3161 timestamps

SignDocumentCommand​

Signs a document using a key from the configured KMS provider.

Arguments​

data class SignDocumentArgs(
val input: SignInput,
val keyInfo: KeyInfoType<*>,
val signatureLevel: SignatureLevel,
val signaturePackaging: SignaturePackaging = SignaturePackaging.ENVELOPED,
val parameters: SignatureParameters? = null,
val timestampParameters: TimestampParameters? = null
)

SignInput​

Factory methods for creating input documents:

// PDF document
val pdfInput = SignInput.pdf(pdfBytes)

// XML document
val xmlInput = SignInput.xml(xmlBytes)

// JSON document
val jsonInput = SignInput.json(jsonBytes)

// Binary data
val binaryInput = SignInput.binary(dataBytes)

// Pre-computed digest (for special cases)
val digestInput = SignInput.digest(hashBytes)

// Custom MIME type
val customInput = SignInput(
data = fileBytes,
name = "document.docx",
mimeType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
)

SignOutput​

data class SignOutput(
val signedData: ByteArray, // Signed document bytes
val signatureLevel: SignatureLevel, // Applied signature level
val signingTime: Instant, // When signature was created
val signatureId: String?, // Signature identifier
val certificateInfo: CertificateInfo?,
val timestampInfo: TimestampInfo?,
val name: String?,
val mimeType: String?
)

data class CertificateInfo(
val subjectDN: String, // "CN=John Doe, O=Company"
val issuerDN: String, // "CN=CA, O=Authority"
val serialNumber: String?,
val notBefore: Instant?,
val notAfter: Instant?,
val fingerprint: String? // SHA-256 fingerprint
)

data class TimestampInfo(
val timestampTime: Instant,
val timestampAuthority: String?,
val policyOid: String?,
val serialNumber: String?
)

Signature Parameters​

CAdES Parameters (Binary Data)​

data class CAdESParameters(
val signatureLevel: SignatureLevel = SignatureLevel.CAdES_BASELINE_B,
val signaturePackaging: SignaturePackaging = SignaturePackaging.ENVELOPING,
val digestAlgorithm: DigestAlg = DigestAlg.SHA256,
val signWithExpiredCertificate: Boolean = false,
val generateTBSWithoutCertificate: Boolean = false
)

// Usage
val params = CAdESParameters(
signatureLevel = SignatureLevel.CAdES_BASELINE_LT,
digestAlgorithm = DigestAlg.SHA384
)

PAdES Parameters (PDF)​

data class PAdESParameters(
val signatureLevel: SignatureLevel = SignatureLevel.PAdES_BASELINE_B,
val signaturePackaging: SignaturePackaging = SignaturePackaging.ENVELOPED,
val digestAlgorithm: DigestAlg = DigestAlg.SHA256,
val visualSignatureParameters: VisualSignatureParameters? = null,
val signatureFieldId: String? = null, // Use existing signature field
val reason: String? = null, // Signing reason
val location: String? = null, // Signing location
val contactInfo: String? = null, // Contact information
val permission: PdfPermission = PdfPermission.NO_CHANGES_PERMITTED
)

enum class PdfPermission {
NO_CHANGES_PERMITTED, // Lock document after signing
MINIMAL_CHANGES_PERMITTED, // Allow form filling
CHANGES_PERMITTED // Allow annotations
}

Visual Signature (PDF)​

data class VisualSignatureParameters(
val fieldParameters: SignatureFieldParameters? = null,
val imageParameters: SignatureImageParameters? = null,
val textParameters: SignatureTextParameters? = null,
val rotation: VisualSignatureRotation = VisualSignatureRotation.NONE,
val zoom: Int = 100
)

data class SignatureFieldParameters(
val page: Int = 1, // Page number (1-based)
val originX: Float = 0f, // X position from left
val originY: Float = 0f, // Y position from bottom
val width: Float = 200f, // Field width
val height: Float = 50f, // Field height
val fieldId: String? = null // Optional field identifier
)

data class SignatureImageParameters(
val image: ByteArray, // PNG, JPEG image bytes
val dpi: Int = 96,
val alignmentHorizontal: VisualSignatureAlignmentHorizontal = LEFT,
val alignmentVertical: VisualSignatureAlignmentVertical = TOP
)

data class SignatureTextParameters(
val text: String? = null,
val font: String = "Helvetica",
val size: Float = 10f,
val textColor: String = "#000000",
val backgroundColor: String? = null,
val signerNamePosition: SignerTextPosition = SignerTextPosition.TOP,
val textWrapping: TextWrapping = TextWrapping.FILL_BOX
)

JAdES Parameters (JSON)​

data class JAdESParameters(
val signatureLevel: SignatureLevel = SignatureLevel.JAdES_BASELINE_B,
val signaturePackaging: SignaturePackaging = SignaturePackaging.ENVELOPING,
val digestAlgorithm: DigestAlg = DigestAlg.SHA256,
val jwsSerializationType: JwsSerializationType = JwsSerializationType.COMPACT_SERIALIZATION,
val sigDPolicy: String? = null,
val base64UrlEncodedPayload: Boolean = true
)

enum class JwsSerializationType {
COMPACT_SERIALIZATION, // Three-part dot-separated
JSON_SERIALIZATION, // Full JSON structure
FLATTENED_JSON_SERIALIZATION
}

XAdES Parameters (XML)​

data class XAdESParameters(
val signatureLevel: SignatureLevel = SignatureLevel.XAdES_BASELINE_B,
val signaturePackaging: SignaturePackaging = SignaturePackaging.ENVELOPED,
val digestAlgorithm: DigestAlg = DigestAlg.SHA256,
val xPathLocationString: String? = null,
val signedInfoCanonicalizationMethod: CanonicalizationMethod = CanonicalizationMethod.EXCLUSIVE,
val signedPropertiesCanonicalizationMethod: CanonicalizationMethod = CanonicalizationMethod.EXCLUSIVE
)

enum class CanonicalizationMethod {
INCLUSIVE, // http://www.w3.org/TR/2001/REC-xml-c14n-20010315
INCLUSIVE_WITH_COMMENTS,
EXCLUSIVE, // http://www.w3.org/2001/10/xml-exc-c14n#
EXCLUSIVE_WITH_COMMENTS
}

Timestamp Parameters​

data class TimestampParameters(
val tsaUrl: String, // TSA endpoint
val tsaPolicyOid: String? = null, // Timestamp policy
val digestAlgorithm: DigestAlg = DigestAlg.SHA256,
val includeNonce: Boolean = true,
val includeCertificates: Boolean = true,
val username: String? = null, // TSA authentication
val password: String? = null
) {
companion object {
fun simple(url: String) = TimestampParameters(tsaUrl = url)

fun withAuth(url: String, username: String, password: String) =
TimestampParameters(
tsaUrl = url,
username = username,
password = password
)
}
}

Validation​

ValidateSignatureArgs​

data class ValidateSignatureArgs(
val signedDocument: ByteArray,
val originalDocument: ByteArray? = null, // For detached signatures
val signatureForm: SignatureForm? = null, // Auto-detect if null
val validationTime: Instant? = null, // Validate at specific time
val checkCertificateRevocation: Boolean = true
)

ValidationResult​

data class ValidationResult(
val isValid: Boolean,
val signatures: List<SignatureValidation>,
val documentName: String? = null,
val signatureForm: SignatureForm? = null
)

data class SignatureValidation(
val signatureId: String,
val isValid: Boolean,
val indication: ValidationIndication,
val subIndication: ValidationSubIndication? = null,
val signatureLevel: SignatureLevel? = null,
val signingTime: Instant? = null,
val certificateInfo: CertificateInfo? = null,
val timestampInfo: TimestampInfo? = null,
val errors: List<String> = emptyList(),
val warnings: List<String> = emptyList()
)

enum class ValidationIndication {
TOTAL_PASSED, // Signature valid
TOTAL_FAILED, // Signature invalid
INDETERMINATE // Cannot determine validity
}

Complete Examples​

Sign with Visual Signature​

val visualParams = VisualSignatureParameters(
fieldParameters = SignatureFieldParameters(
page = 1,
originX = 400f,
originY = 100f,
width = 150f,
height = 60f
),
imageParameters = SignatureImageParameters(
image = logoBytes,
dpi = 150
),
textParameters = SignatureTextParameters(
text = "Digitally signed by\n${signerName}\n${LocalDate.now()}",
font = "Courier",
size = 8f
)
)

val result = signDocumentCommand.execute(
SignDocumentArgs(
input = SignInput.pdf(pdfBytes),
keyInfo = keyInfo,
signatureLevel = SignatureLevel.PAdES_BASELINE_LT,
parameters = PAdESParameters(
visualSignatureParameters = visualParams,
reason = "Document approval",
location = "Amsterdam"
),
timestampParameters = TimestampParameters.simple(tsaUrl)
),
sessionContext
)

Sign JSON with JAdES​

val jsonData = """{"contract": "Agreement #123", "amount": 50000}"""

val result = signDocumentCommand.execute(
SignDocumentArgs(
input = SignInput.json(jsonData.encodeToByteArray()),
keyInfo = keyInfo,
signatureLevel = SignatureLevel.JAdES_BASELINE_T,
parameters = JAdESParameters(
jwsSerializationType = JwsSerializationType.JSON_SERIALIZATION,
base64UrlEncodedPayload = true
),
timestampParameters = TimestampParameters.simple(tsaUrl)
),
sessionContext
)

Validate Multiple Signatures​

val result = validateSignatureCommand.execute(
ValidateSignatureArgs(
signedDocument = signedPdfBytes,
checkCertificateRevocation = true
),
sessionContext
)

result.fold(
success = { validation ->
println("Document has ${validation.signatures.size} signature(s)")
println("Overall valid: ${validation.isValid}")

validation.signatures.forEachIndexed { index, sig ->
println("\nSignature ${index + 1}:")
println(" ID: ${sig.signatureId}")
println(" Valid: ${sig.isValid}")
println(" Indication: ${sig.indication}")
sig.subIndication?.let { println(" Sub-indication: $it") }
println(" Signer: ${sig.certificateInfo?.subjectDN}")
println(" Signed at: ${sig.signingTime}")
sig.timestampInfo?.let {
println(" Timestamped at: ${it.timestampTime}")
println(" TSA: ${it.timestampAuthority}")
}
if (sig.errors.isNotEmpty()) {
println(" Errors: ${sig.errors.joinToString()}")
}
if (sig.warnings.isNotEmpty()) {
println(" Warnings: ${sig.warnings.joinToString()}")
}
}
},
failure = { error ->
println("Validation failed: ${error.message}")
}
)

Signature Providers​

The EDK offers two SignatureProvider implementations that share the same interface:

DSS Provider (Local)​

For in-process signing using the EU DSS library (JVM only):

dependencies {
// Core signing client (multiplatform)
implementation("com.sphereon.edk:lib-eidas-signature-client-impl:0.13.0")

// JVM provider for full eIDAS compliance
implementation("com.sphereon.edk:lib-eidas-signature-client-dss:0.13.0")
}

The DSS provider supports all signature formats (CAdES, PAdES, JAdES, XAdES), all baseline levels (B, T, LT, LTA), timestamping, and validation with revocation checking.

REST Client Provider (Remote)​

For delegating signing to a remote eIDAS server:

dependencies {
// Core signing client
implementation("com.sphereon.edk:lib-eidas-signature-client-impl:0.13.0")

// REST client provider
implementation("com.sphereon.edk:lib-eidas-signature-rest-client:0.13.0")
}

The REST client implements the same SignatureProvider interface, allowing seamless switching between local and remote signing:

// Configuration for REST client
@Inject
@SingleIn(AppScope::class)
@ContributesBinding(AppScope::class)
class MyRestSignatureConfig : RestSignatureClientConfig {
override val baseUrl: String = "https://signature-server.example.com/api/v1"
override val defaultConfigId: String = "pdf-lt-config"
}

Once configured, use the same commands as with local signing:

// The RestSignatureProvider is automatically used when DSS is not on classpath
val result = signDocumentCommand.execute(
SignDocumentArgs(
input = SignInput.pdf(pdfBytes),
keyInfo = keyInfo,
signatureLevel = SignatureLevel.PAdES_BASELINE_LT
),
sessionContext
)

Hybrid Signing​

For maximum flexibility, use both providers:

  1. Local digest creation - Document never leaves your infrastructure
  2. Remote signing - Only the hash is sent to the server
  3. Local completion - Combine signature with document locally
// Step 1: Create digest locally (document stays on-premise)
val dssProvider: DssSignatureProvider = session.component.dssSignatureProvider
val digest = dssProvider.createDigest(input, keyInfo, params, sessionContext)

// Step 2: Sign hash remotely (only hash leaves your network)
val restProvider: RestSignatureProvider = session.component.restSignatureProvider
val signatureValue = restProvider.signDigest(digest.value.digestToSign)

// Step 3: Complete locally (final document created on-premise)
val signed = dssProvider.completeSignature(
signatureValue,
digest.value.signatureDocumentBytes,
keyInfo,
params,
sessionContext
)