Skip to content

HIPAA Spring Boot Starter

HIPAA-by-default for Spring Boot. Annotate a field with @Phi and get encryption at rest, an immutable, tamper-evident audit trail, and minimum-necessary access control — for free, by adding one dependency.

This starter turns three of the HIPAA §164.312 technical safeguards into inherited platform behavior, so your application code stays focused on the domain instead of re-implementing crypto, audit logging, and RBAC on every project.

Honest scope. This is a building block, not a HIPAA compliance program. It ships with illustrative key management (a key read from config) — in production you must supply a KMS/Vault-backed KeyProvider and a durable AuditSink. Compliance also requires administrative and physical safeguards, BAAs, risk analysis, and more that no library can provide.

Companion write-up: HIPAA by Default: Field-Level Encryption, Immutable Audit, and Minimum-Necessary Access

What you get

Feature How HIPAA §164.312 control
PHI field encryption at rest @Phi field + PhiEncryptionListener (AES-256-GCM) (a)(2)(iv) Encryption and decryption; (a)(1) Access control
Tamper-evident audit trail @AuditPhiAccess + hash-chained AuditEvents in an AuditSink (b) Audit controls; (c) Integrity
Minimum-necessary access control @MinimumNecessary(roles...) + PhiAccessContext (a)(1) Access control

Usage (the whole thing, in ~20 lines)

// 1. Add the dependency (see below) and set the key:
//    hipaa.phi.key=<Base64-encoded 32-byte AES key>

// 2. Annotate your entity — one listener, one @Phi per protected field:
@Entity
@EntityListeners(PhiEncryptionListener::class)
class Patient(
    @field:Phi @Column(length = 512) var name: String = "",
    @field:Phi @Column(length = 512) var ssn: String = "",
    var mrn: String = "",                 // non-PHI, stored in clear text
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null,
)

// 3. Annotate the service method that reads PHI:
@Service
class PatientService(private val repo: PatientRepository) {
    @AuditPhiAccess(action = "read", resourceType = "Patient")   // → audit trail
    @MinimumNecessary("clinician", "admin")                      // → access control
    fun getById(id: Long): Patient = repo.findById(id).orElseThrow()
}
// Done. ssn/name are encrypted at rest, every read is audited, and callers
// without the clinician/admin role get PhiAccessDeniedException.

Add the dependency

This is a multi-module Gradle build; publish the starter module to your repository, then depend on it:

dependencies {
    implementation("io.nirmitee.hipaa:starter:0.1.0")
}

Quickstart

# Generate the Gradle wrapper (pinned to 8.10) and build + test both modules:
gradle wrapper --gradle-version 8.10 --distribution-type bin
JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home ./gradlew build

# Run the demo:
JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home ./gradlew :demo:bootRun

Then, in another shell:

# Create a patient
curl -s -X POST localhost:8080/patients -H 'Content-Type: application/json' \
  -d '{"name":"Jane Doe","ssn":"123-45-6789","dateOfBirth":"1990-02-15","phone":"555-0100","mrn":"MRN-1"}'
# → {"id":1,"mrn":"MRN-1"}

# Prove encryption at rest: raw JDBC read shows ciphertext for PHI, clear text for mrn
curl -s localhost:8080/patients/1/raw
# → {"ID":1,"MRN":"MRN-1","NAME":"<base64 ciphertext>","SSN":"<base64 ciphertext>",...}

# Read as a clinician (allowed + audited); PHI comes back decrypted
curl -s localhost:8080/patients/1 -H 'X-Actor: dr-house' -H 'X-Roles: clinician'
# → {"name":"Jane Doe","ssn":"123-45-6789",...}

# Read without the role → 403 access denied
curl -s -o /dev/null -w '%{http_code}\n' localhost:8080/patients/1 -H 'X-Actor: intruder' -H 'X-Roles: billing'
# → 403

# Inspect the tamper-evident audit chain
curl -s localhost:8080/audit
# → {"chainValid":true,"events":[{"seq":0,"actor":"dr-house","action":"read","resourceType":"Patient","resourceId":"1",...}]}

Module layout

hipaa-spring-boot-starter/
├── starter/                       # io.nirmitee.hipaa — the auto-configuration library
│   └── src/main/kotlin/io/nirmitee/hipaa/
│       ├── annotations/           # @Phi, @PhiInside
│       ├── crypto/                # PhiCipher (AES-256-GCM), KeyProvider, PhiEncryptionListener
│       ├── audit/                 # @AuditPhiAccess, AuditEvent (hash chain), AuditSink, PhiAuditAspect
│       ├── access/                # @MinimumNecessary, PhiAccessContext, MinimumNecessaryAspect
│       └── config/                # HipaaAutoConfiguration, HipaaProperties
└── demo/                          # io.nirmitee.hipaa.demo — Spring Boot web app + H2 showing it end to end

Extending it in production

  • Key management: define your own KeyProvider bean backed by AWS KMS, GCP KMS, HashiCorp Vault, or an HSM. The starter's bean is @ConditionalOnMissingBean, so yours wins.
  • Durable audit: define your own AuditSink bean that appends to an append-only table or WORM store while preserving the hash chain.
  • Identity: back PhiAccessContext with Spring Security's SecurityContext instead of the request-header filter used by the demo.

See docs/architecture.md for the mechanics.

License

Apache-2.0. See LICENSE.

About

HIPAA-by-default for Spring Boot: annotate @phi and get field-level encryption, a tamper-evident audit trail, and minimum-necessary access control — HIPAA §164.312 technical safeguards as inherited platform behavior.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages