Skip to content

toresoft/sign-verify

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

257 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

sign-verify

CI (GitHub) pipeline (GitLab) coverage (GitLab) docker license

๐Ÿ‡ฌ๐Ÿ‡ง English ยท ๐Ÿ‡ฎ๐Ÿ‡น Leggi in italiano

REST service for eIDAS electronic-signature verification (PAdES, CAdES, XAdES, JAdES, ASiC), built on Spring Boot 4.1 and the EU DSS 6.4 library, with EU Trusted List (LOTL/TSL) management.


1. Application overview

sign-verify checks digitally signed documents against the eIDAS standards and tells you whether the signature holds up. It returns an outcome (indication/subIndication) plus the DSS validation reports (Simple, Detailed, Diagnostic, ETSI).

Key features

  • Signature verification, both synchronous and asynchronous (job + HMAC-signed HTTP callback).
  • Verification profiles (presets BASIC / STANDARD / STRICT / AGID / AGID_TS / CUSTOM) with per-request policy overrides.
  • Extraction of the original document from a signed container.
  • TSL management: download and mirror of the EU List of Trusted Lists (LOTL), scheduled refresh, inspection of trusted certificates.
  • Authentication via API key (X-API-Key) and/or OAuth2 JWT, with STANDARD and PRIVILEGED roles.
  • Audit log and observability (health/readiness, Prometheus metrics, JSON logs), with automatic job retention and cleanup.

Supported signature formats: PAdES (PDF), CAdES (CMS), XAdES (XML), JAdES (JSON), ASiC-S/ASiC-E.

Architecture

Hexagonal (ports & adapters) architecture, enforced by ArchUnit.

sign-verify component map

Layer Package Responsibility
API api/ Thin REST controllers + RFC 9457 error handler (problem+json)
Application application/ Use-case logic (verification, profiles, async jobs, TSL, audit)
Domain domain/ Model, enums, ports (interfaces), exceptions
Adapter adapter/ Port implementations (DSS, crypto, callback, storage)
Persistence persistence/ Spring Data JPA repositories

2. Configuration

Configuration follows the Spring Boot model: application.yaml (base, production-oriented) plus profile-specific files. Every key can be overridden by environment variables.

Spring profiles

Profile Use Characteristics
(none) Production OAuth enabled, data under /var/lib/sign-verify, TSL active
dev Local host development In-memory H2, OAuth off, dev master key, data under ./target
docker Containerised development OAuth off, TSL skipped, data under /var/lib/sign-verify, datasource via env

Activation: -Dspring.profiles.active=dev (host) or SPRING_PROFILES_ACTIVE=docker (container).

Main environment variables

Variable Description Default
SPRING_DATASOURCE_URL JDBC database URL in-memory H2
SPRING_DATASOURCE_USERNAME / _PASSWORD DB credentials sa / (empty)
APP_SECRET_MASTER_KEY Secret-encryption key, base64 of 32 bytes (256-bit) (required)
APP_SECURITY_OAUTH_ENABLED Enable the OAuth2 JWT resource server true
APP_SECURITY_OAUTH_ISSUER_URI OIDC issuer (required when OAuth is enabled) (empty)
APP_SECURITY_OAUTH_ROLE_CLAIM JWT claim holding the roles roles
APP_SECURITY_OAUTH_PRIVILEGED_VALUES Claim values that grant the PRIVILEGED role admin,privileged
APP_SECURITY_BOOTSTRAP_KEY_FILE Path where the bootstrap key is written at first start /var/lib/sign-verify/bootstrap-api-key.txt
APP_STORAGE_JOBS_DIR Async jobs directory /var/lib/sign-verify/jobs
APP_STORAGE_TYPE Storage backend for job documents (filesystem or s3) filesystem
APP_STORAGE_S3_ENDPOINT S3 endpoint (empty = native AWS) (empty)
APP_STORAGE_S3_REGION S3 region eu-south-1
APP_STORAGE_S3_BUCKET S3 bucket name sign-verify-jobs
APP_STORAGE_S3_PATH_STYLE Path-style access (true for MinIO/Ceph) false
APP_DSS_CACHE_DIR DSS cache (TSL, CRL/OCSP) /var/lib/sign-verify/dss-cache
APP_OJ_KEYSTORE_PASSWORD EU Official Journal keystore password (LOTL verification) (empty)
SERVER_PORT HTTP port 8080
JAVA_TOOL_OPTIONS Extra JVM flags container ergonomics

With APP_STORAGE_TYPE=s3, credentials come from the AWS SDK default chain (AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, IAM roles/IRSA, profiles): no application property holds secrets. The bucket must exist: at startup the adapter performs a fail-fast headBucket.

Generate the master key

openssl rand -base64 32

Without a valid APP_SECRET_MASTER_KEY (32 bytes), and without APP_SECURITY_OAUTH_ISSUER_URI when OAuth is enabled, the application fails fast at startup. The database schema is managed by Flyway (db/migration), with Hibernate in ddl-auto: validate.

Authentication

  • API key: header X-API-Key: sv_<prefix>_<body> (bcrypt-hashed at rest). At first start, if no PRIVILEGED key exists, a bootstrap one is generated and written to the file pointed to by APP_SECURITY_BOOTSTRAP_KEY_FILE (mode 0600). Read it and then remove it.
  • OAuth2 JWT: enable with APP_SECURITY_OAUTH_ENABLED=true + an OIDC issuer.

3. Running

Prerequisites

  • JDK 21 (e.g. via SDKMAN) and Maven 3.9+
  • Docker / Docker Compose (for the containerised stacks)

a) Local host (in-memory H2)

mvn clean package
java -Dspring.profiles.active=dev -jar target/sign-verify-2.jar
# bootstrap key โ†’ ./target/bootstrap-api-key.txt

b) Development with Docker (app + PostgreSQL)

docker compose up --build

Starts the application (profile docker) and a PostgreSQL 16. Swagger UI at http://localhost:8080/swagger-ui/index.html.

c) Production (hardened image)

cp .env.example .env      # fill in DB, master key, OAuth issuer, keystore password
docker compose -f docker-compose.prod.yml up -d

The image is multi-stage with a JRE-only non-root runtime (uid 10001); the production compose applies read_only, cap_drop: ALL, no-new-privileges, a tmpfs /tmp, resource limits and a readiness healthcheck. It expects an external, managed PostgreSQL.


4. First try (quick start)

End-to-end example using the Docker development stack and the sample signed PDF bundled in the repository (src/test/resources/assets/pades/sample-pades-valid.pdf).

1. Start the stack

docker compose up --build -d

2. Fetch the bootstrap API key (role PRIVILEGED, generated at first start)

KEY=$(docker compose exec -T app cat /var/lib/sign-verify/bootstrap-api-key.txt)
echo "API key: $KEY"

3. Check the service is up

curl -s http://localhost:8080/actuator/health/readiness
# {"status":"UP"}

4. Verify a signature (synchronous)

Multipart request: a required file part plus optional dedicated parts (profile, the profile name, e.g. AGID; the deprecated profileId UUID; profileOverrides as a JSON object; reports as a CSV). Without them, the default profile and the simple + etsi reports are used. A legacy metadata JSON part is still accepted but deprecated and mutually exclusive with the dedicated parts (mixing the two yields 400).

curl -s -X POST http://localhost:8080/api/v1/verifications \
  -H "X-API-Key: $KEY" \
  -F "file=@src/test/resources/assets/pades/sample-pades-valid.pdf" | jq

Response (excerpt):

{
  "verifiedAt": "2026-06-08T10:15:30Z",
  "profileUsed": "STANDARD",
  "overridesApplied": false,
  "signatureFormat": "PAdES-BASELINE-B",
  "indication": "TOTAL_PASSED",
  "subIndication": null,
  "signatureCount": 1,
  "reports": { "simple": { }, "etsi": { } }
}

The key field is indication: TOTAL_PASSED (valid), TOTAL_FAILED (invalid), INDETERMINATE (undeterminable, e.g. TSL not loaded under the docker profile).

5. (Optional) Pick a profile or specific reports

List the available profiles and re-run the verification with a chosen id:

curl -s http://localhost:8080/api/v1/profiles -H "X-API-Key: $KEY" | jq '.[].id,.[].name'

curl -s -X POST http://localhost:8080/api/v1/verifications \
  -H "X-API-Key: $KEY" \
  -F "file=@src/test/resources/assets/pades/sample-pades-valid.pdf" \
  -F 'profile=AGID' \
  -F 'reports=simple,detailed' | jq

Note on real certificate trust: under the docker profile the TSL is disabled (startup-mode: SKIP), so chain trust may come back as INDETERMINATE. For a full verification use the production configuration with the TSL active (APP_OJ_KEYSTORE_PASSWORD set).

Cleanup

docker compose down -v

5. Tests

mvn clean verify        # unit + integration tests (Testcontainers) + Spotless + JaCoCo
mvn spotless:apply      # auto-format (Google Java Format) before committing

6. CI/CD: publishing to Docker Hub

Two equivalent pipelines are provided:

  • GitLab CI: .gitlab-ci.yml
  • GitHub Actions: .github/workflows/ci.yml

Stages: validate โ†’ test โ†’ build โ†’ package โ†’ security. The package stage builds and publishes the image to Docker Hub as toresoft/sign-verify:

  • every default-branch pipeline โ†’ tags :<short-sha> and :latest
  • every git tag vX.Y.Z โ†’ tags :<short-sha> and :<tag>

Set the credentials as masked CI/CD variables on GitLab (Settings โ†’ CI/CD โ†’ Variables) or as repository secrets on GitHub (Settings โ†’ Secrets and variables โ†’ Actions):

Name Value
DOCKERHUB_USERNAME Docker Hub account/namespace
DOCKERHUB_TOKEN Docker Hub access token (Account โ†’ Security)

The security stage scans the image and filesystem with Trivy (HIGH/CRITICAL) and runs an OSV-Scanner dependency (SCA) scan against the OSV.dev database.

Build & push manually

docker build -t toresoft/sign-verify:dev .
echo "$DOCKERHUB_TOKEN" | docker login -u toresoft --password-stdin
docker push toresoft/sign-verify:dev

7. Detailed usage guide

In-depth usage documentation lives under docs/, in English (docs/en/) and Italian (docs/it/), with Mermaid diagrams. Indexes: docs/en/README.md ยท docs/it/README.md.

Topic ๐Ÿ‡ฌ๐Ÿ‡ง English ๐Ÿ‡ฎ๐Ÿ‡น Italiano
Build & configuration 01 01
Docker & configuration 02 02
Docker step-by-step operational guide 02b 02b
Authentication (API keys, OAuth) 03 03
Trusted Certificates (TSL) 04 04
Signature verification (sync/async) 05 05
File extraction 06 06
Logging & audit 07 07
Horizontal scaling 08 08

8. References

  • API: OpenAPI contract in src/main/resources/openapi/openapi.yaml. Swagger UI at /swagger-ui/index.html

9. License

sign-verify-2 is licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0) โ€” see LICENSE.

It depends on the EU DSS library (eu.europa.ec.joinup.sd-dss, LGPL-2.1) and bundles third-party test fixtures under separate licenses (LGPL-2.1, EUPL-1.1, GPL/Artistic). Full attribution: NOTICE.md.

About

REST service for eIDAS electronic-signature verification (PAdES, CAdES, XAdES, JAdES, ASiC) built on Spring Boot 3.4 and the EU DSS 6.4 library, with EU Trusted List (LOTL/TSL) management.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages