DynaForm API is a secure, zero-knowledge dynamic form service that enables workflow engines to collect sensitive user input through encrypted form submissions. Built with Spring Boot 4.0 and Java 25, it provides end-to-end encryption using both classical (RSA-OAEP) and post-quantum (ML-KEM) cryptography.
- Zero-Knowledge Architecture: Service never sees plaintext data after submission
- Envelope Encryption: AES-256-GCM for data encryption + public key encapsulation
- Post-Quantum Ready: Support for ML-KEM (Kyber) alongside RSA-OAEP
- Dynamic JSON Schemas: Define form structure using JSON Schema v7+
- Cryptographically Signed Tokens: Ed25519-signed form access tokens
- TTL-Based Lifecycle: Automatic expiration and cleanup of sensitive data
- RFC 7807 Error Handling: Standardized ProblemDetail error responses
- Java 25
- Spring Boot 4.0.0
- PostgreSQL (production) / H2 (testing)
- Liquibase for database migrations
- Jackson 3 for JSON processing
- Bouncy Castle for cryptographic operations
- networknt/json-schema-validator for JSON Schema validation
- Java 25
- Maven 3.9+
- PostgreSQL 16+ (for production)
git clone https://github.com/callibrity/dynaform-api.git
cd dynaform-api
mvn clean installmvn spring-boot:runThe application will start on port 8080 by default.
mvn clean testAll 32 tests should pass, covering:
- 14 ExchangeService tests
- 17 Ed25519TokenService tests
- 1 Spring Boot context load test
POST /exchanges
Content-Type: application/json
{
"exchangeId": "optional-custom-id",
"schema": { JSON Schema object },
"instructions": "Instructions for form completion",
"publicKey": "base64-encoded-public-key",
"encryptionScheme": "ML_KEM_768",
"ttl": "PT24H",
"accessMode": "PUBLIC_LINK"
}Response:
{
"exchangeId": "generated-uuid",
"formAccessToken": "signed-access-token",
"formUrl": "/forms/generated-uuid?token=signed-access-token"
}GET /exchanges/{exchangeId}/resultResponse:
{
"exchangeId": "...",
"encryptionEnvelope": "{JSON envelope with ciphertext}",
"submittedAt": "2025-12-07T10:30:00Z",
"expiresAt": "2025-12-08T10:30:00Z"
}GET /forms/{exchangeId}?token={formAccessToken}Response:
{
"exchangeId": "...",
"schema": { JSON Schema object },
"instructions": "Form instructions"
}POST /forms/{exchangeId}/submit?token={formAccessToken}
Content-Type: application/json
{
"formData": "{ user input matching schema }"
}Response:
{
"exchangeId": "...",
"submittedAt": "2025-12-07T10:30:00Z"
}DynaForm supports multiple encryption schemes for future-proof security:
| Scheme | Key Size | Quantum Safe | Status |
|---|---|---|---|
RSA_OAEP_4096 |
4096-bit | ❌ No | ✅ Fully Implemented |
ML_KEM_768 |
1184 bytes | ✅ Yes | |
ML_KEM_1024 |
1568 bytes | ✅ Yes |
All schemes use AES-256-GCM for symmetric data encryption.
- Generate random AES-256 symmetric key
- Encrypt form data with AES-GCM (produces ciphertext + auth tag)
- Encrypt/encapsulate symmetric key with recipient's public key
- Return envelope containing:
ciphertext: Encrypted form dataencryptedKey/encapsulatedKey: Wrapped symmetric keyiv: Initialization vector for GCMtag: Authentication tag for GCM
The service never has access to the recipient's private key and cannot decrypt submissions.
# Database Configuration
spring.datasource.url=jdbc:postgresql://localhost:5432/dynaform
spring.datasource.username=dynaform
spring.datasource.password=your-password
# Token Service
dynaform.token.signing-key=base64-encoded-ed25519-private-key
dynaform.token.ttl-default=PT24H
dynaform.token.ttl-min=PT1H
dynaform.token.ttl-max=P7D
# Encryption
dynaform.encryption.default-scheme=ML_KEM_768# Generate private key
openssl genpkey -algorithm Ed25519 -out private.pem
# Extract base64-encoded key
openssl pkey -in private.pem -outform DER | base64- Service never stores or logs plaintext form data
- All submissions encrypted before persistence
- Encryption keys managed by workflow engine
- Ed25519 cryptographic signatures (fast, secure, battle-tested)
- Unique token IDs prevent replay attacks
- TTL-based automatic expiration
- 404 responses on invalid tokens (prevents enumeration)
- AWAITING_INPUT: Exchange created, waiting for submission
- COMPLETED: Form submitted and encrypted
- EXPIRED: TTL exceeded, data eligible for cleanup
┌─────────────────────────────────┐
│ REST Controllers │
│ (ExchangeController, │
│ FormController) │
└────────────┬────────────────────┘
│
┌────────────▼────────────────────┐
│ Service Layer │
│ (ExchangeService, │
│ SubmissionService, │
│ RetrievalService) │
└────────────┬────────────────────┘
│
┌────────────▼────────────────────┐
│ Encryption Infrastructure │
│ (Enum-as-Factory Pattern) │
└────────────┬────────────────────┘
│
┌────────────▼────────────────────┐
│ JPA Repositories │
│ (PostgreSQL / H2) │
└─────────────────────────────────┘
- Enum-as-Factory:
EncryptionSchemeenum instances contain encryptor implementations - Strategy Pattern: Pluggable
EnvelopeEncryptorimplementations - DTO Records: Immutable data transfer objects using Java records
- ProblemDetail: RFC 7807 standard error responses
This project uses the mycila license-maven-plugin to enforce Apache 2.0 license headers:
# Check for missing headers
mvn license:check
# Add headers to files
mvn license:format- Minimal JPA annotations (avoid
@Columnunless necessary) - Use
@Lobfor large text fields - Record types for immutable DTOs
- Service layer independent of controller layer
This project is licensed under the Apache License 2.0—see the LICENSE file for details.
Contributions are welcome! Please ensure:
- All tests pass (
mvn clean test) - Code includes proper license headers
- Follow existing code style and patterns
- Add tests for new functionality
- Complete ML-KEM implementation with Bouncy Castle PQC
- Add TTL enforcement cleanup jobs
- Implement rate limiting
- Add OpenAPI/Swagger documentation
- Create integration tests for controllers
- Add security headers (CORS, CSP)
- Implement monitoring with Spring Boot Actuator
For questions or issues, please open an issue on GitHub.
Built with ☕ using Java 25 and Spring Boot 4.0