Skip to content

Software Development at Program Tom LTD

Place for coding, programming, development and software in general.

Menu
  • Blog
  • PDF Booklets
  • Dev Utils & Content
  • Java Spring Boot Or Web Apps
  • English
    • български
    • English
    • Español
    • Português
    • हिन्दी
    • Русский
    • Deutsch
    • Français
    • Italiano
    • العربية
  • About Us
Menu
Encrypting Sensitive fields - GDPR (And Security) Must

Encrypting Sensitive fields – GDPR (And Security) Must

Posted on October 25, 2025 by Toma Velev

Below is a practical, security-first approach you can implement so your Java app can use user location data while DBs, devs, and CMS admins see only ciphertext (or very coarse data). It covers threat model, recommended architecture, concrete crypto patterns (envelope encryption + AEAD), searchable/indexable options, key management, rotation, audit, and a compact Java example you can adapt.

Summary (one-line)

Store encrypted precise location using application-side (field-level) envelope encryption (AES-GCM DEKs + KEKs managed by a KMS/HSM). Keep only ciphertext in the DB; app servers request keys from KMS to unwrap DEKs and decrypt when authorized. Expose only coarse/plain location for admin needs (if needed).


Threat model & goals

  • Who must NOT see plaintext: DB administrators, CMS admins, backup operators, developers without explicit permission.
  • Who CAN see plaintext: application instances/servers with runtime access to decryption keys (subject to RBAC and auditing).
  • Goals: minimize plaintext exposure, support app operations, support key rotation and audit, avoid storing keys with DB, maintain reasonable performance.

High-level architecture (recommended)

  1. Client or App Tier Encryption
    • Preferred: App server performs encryption of location before writing to DB (client-side encryption is also possible but more complex for key distribution).
  2. Envelope encryption pattern (fast + secure):
    • Generate a DEK (Data Encryption Key) per record or per object.
    • Encrypt the latitude/longitude (and optionally metadata) with the DEK using AEAD (e.g., AES-GCM or ChaCha20-Poly1305).
    • Encrypt (wrap) the DEK with a KEK managed by a Key Management Service (KMS) or HSM (AWS KMS, GCP KMS, Azure Key Vault, HashiCorp Vault with HSM). Store the wrapped DEK alongside the ciphertext.
  3. Store in DB: ciphertext, wrapped-DEK, metadata (IV/nonce, algorithm id, version), and a secure integrity tag (AEAD provides it).
  4. Decrypt: App server requests KMS to unwrap KEK → DEK → decrypt ciphertext. RBAC & audit on KMS calls restricts which services/roles can decrypt.
  5. Search / Indexing: store separate blinded/coarse/index fields (see below) to allow queries without revealing exact coordinates.

Why envelope encryption?

  • Performance: symmetric DEKs encrypt data quickly.
  • Key security: KEK stored/managed by KMS (not in DB or app config).
  • Rotation: rotate KEK by re-wrapping DEKs (or re-encrypting data) without re-encrypting the whole DB immediately.

Crypto choices & parameters

  • Algorithm for data: AES-256-GCM (AEAD) or ChaCha20-Poly1305. AEAD is essential (authenticity + confidentiality).
  • DEK: 256-bit random (use a CSPRNG).
  • IV/Nonce: Unique per encryption (GCM needs non-reuse; include nonce with record).
  • KEK storage: KMS/HSM (AWS KMS, GCP KMS, Azure Key Vault, or HSM-backed Vault).
  • Key wrapping: Use KMS Encrypt/Decrypt or WrapKey/Unwrap operations rather than home-grown RSA encryption.
  • Avoid: deterministic encryption for precise coordinates unless you accept privacy loss.

Search, geo-queries, and indexing options

Exact encrypted geometry cannot be queried by DB admins easily. Common approaches:

  1. Store a separate, coarse plaintext field (e.g., country/city or truncated coordinates to 2–3 decimal places) used for UI and admin queries.
    • E.g., store lat_trunc = round(lat, 2) for rough grouping.
    • This leaks coarse location but may be acceptable.
  2. Blind indexes (HMAC-based deterministic index)
    • Store an HMAC of the value using a separate secret (or use deterministic encryption) to allow equality/search by exact match without revealing plaintext. Good for exact-match lookups only (not range).
  3. Searchable encryption techniques (advanced): order/re-range queries are hard; consider:
    • Precompute geo-hashes at multiple precisions and store HMACed or truncated hashes for index/search.
    • For range or radius queries, keep precise data encrypted and compute spatial results in the app after fetching candidate records by coarse filter.
  4. Secure enclave / FHE / MPC — complex and heavy; normally not necessary.

Key management & operational practices

  • Use a managed KMS/HSM (AWS KMS, Cloud HSM, Azure Key Vault, or Vault with HSM). Never store KEKs in source code or DB.
  • Least privilege: app servers use IAM roles to call KMS; developers/DBAs do not have decrypt/unwrapping permissions by default.
  • Audit & logging: enable KMS audit logs (who called decrypt/unwrap) and application access logs.
  • Key rotation: rotate KEK regularly; keep ability to re-wrap existing DEKs. Use versioning metadata with each record (key version id).
  • Backups: backups must remain encrypted; ensure wrapped-DEKs are backed up too and that key material for unwrap is available to recover.
  • Secrets & config: use secrets manager for app config (not plaintext files).
  • Protect runtime: OS/hypervisor-level hardening, process isolation, eliminate printing plaintext to logs, and zeroize keys in memory when possible.

Privacy considerations & policy

  • Data minimization: only store coordinates if necessary. Use precision reduction (store city-level instead of exact when possible).
  • Consent, retention, deletion: implement deletion procedures and retention windows per law (GDPR etc.).
  • Explain to users: documented privacy policy—what is collected, why, retention, and access control.

Java implementation pattern (compact example)

Below is a compact illustrative Java example using the envelope pattern with AES-GCM for data encryption and an abstracted KMS API for wrapping/unwrapping DEKs. Replace the KmsClient.wrapKey(...) / unwrapKey(...) stubs with the SDK calls for your KMS (AWS/GCP/Azure/HashiCorp). Use Google Tink or the AWS Encryption SDK in production to avoid mistakes — the sample uses JCE primitives to show the pattern.

// NOTE: illustrative example — adapt for production (Tink or AWS Encryption SDK recommended)

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;

public class LocationCrypto {
    private static final SecureRandom rnd = new SecureRandom();
    private static final int GCM_IV_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 128; // bits
    private static final int AES_KEY_SIZE = 256; // bits

    // --- Generate a random DEK ---
    public static SecretKey generateDek() throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance("AES");
        kg.init(AES_KEY_SIZE, rnd);
        return kg.generateKey();
    }

    // --- Encrypt location JSON (or bytes) with DEK using AES-GCM ---
    // returns base64(iv || ciphertext)
    public static String encryptWithDek(SecretKey dek, byte[] plaintext) throws Exception {
        byte[] iv = new byte[GCM_IV_LENGTH];
        rnd.nextBytes(iv);
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, dek, spec);
        byte[] ct = cipher.doFinal(plaintext);

        byte[] out = new byte[iv.length + ct.length];
        System.arraycopy(iv, 0, out, 0, iv.length);
        System.arraycopy(ct, 0, out, iv.length, ct.length);
        return Base64.getEncoder().encodeToString(out);
    }

    // --- Decrypt ---
    public static byte[] decryptWithDek(SecretKey dek, String b64IvCiphertext) throws Exception {
        byte[] in = Base64.getDecoder().decode(b64IvCiphertext);
        byte[] iv = new byte[GCM_IV_LENGTH];
        System.arraycopy(in, 0, iv, 0, iv.length);
        byte[] ct = new byte[in.length - iv.length];
        System.arraycopy(in, iv.length, ct, 0, ct.length);

        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, dek, spec);
        return cipher.doFinal(ct);
    }

    // --- Wrap a DEK with KEK via KMS (pseudo) ---
    // In practice call your KMS: e.g., AWS KMS Encrypt or WrapKey
    public static String wrapDekWithKms(SecretKey dek, KmsClient kms) throws Exception {
        byte[] dekBytes = dek.getEncoded();
        byte[] wrapped = kms.wrapKey(dekBytes); // implement via your KMS SDK
        return Base64.getEncoder().encodeToString(wrapped);
    }

    // --- Unwrap DEK via KMS ---
    public static SecretKey unwrapDekWithKms(String wrappedBase64, KmsClient kms) throws Exception {
        byte[] wrapped = Base64.getDecoder().decode(wrappedBase64);
        byte[] dekBytes = kms.unwrapKey(wrapped); // KMS SDK unwrap
        return new SecretKeySpec(dekBytes, "AES");
    }
}

Record stored in DB could look like (JSON):

{
  "user_id": "1234",
  "location_ciphertext": "<base64 iv||ct>",
  "wrapped_dek": "<base64 wrapped dek>",
  "encryption": {
    "alg": "AES-GCM",
    "dek_kek_version": "kms-key-id:v1",
    "created_at": "2025-10-13T12:34:56Z"
  },
  "coarse_location": { "city": "Sofia", "lat_round_2": 42.70, "lon_round_2": 23.32}
}

Important production notes

  • Use a vetted library (Google Tink, AWS Encryption SDK, or libsodium wrappers) rather than JCE glue if possible.
  • Never log plaintext coordinates or keys. Add application-level guards to avoid accidental logging.

Permission model & access controls

  • KMS policies: only application runtime roles can call Decrypt/Unwrap. Developers and DB admins do not have KMS decrypt privileges unless explicitly required and audited.
  • App roles: use short-lived instance credentials (IAM roles or workload identity) so keys aren’t embedded.
  • Admin workflows: build a secure “break glass” procedure if a human needs to see plaintext (with approvals and logging).

Key rotation & rewrap strategy

  • Rotate KEK: create a new KEK version in KMS and re-wrap each existing DEK (no need to re-encrypt the ciphertext). Store KEK version with each record.
  • Rotate DEK: if you must rotate DEKs, decrypt with old DEK and re-encrypt with new DEK, then wrap new DEK.
  • Bulk re-wrap: do in background with job that has limited scope, audit, and rate limits.

Monitoring, logging, and audits

  • Log KMS decrypt/unwrapping calls with identity + purpose.
  • Monitor unusual patterns (high-volume decrypt calls).
  • Keep immutable audit logs for compliance.

Practical tradeoffs & recommended defaults

  • Default: encrypt precise coordinates, store only coarse/plain city or truncated lat/lon for indexing and admin views.
  • If you need range queries: use multi-precision geohashes stored as HMACs or do range computation in the app after a coarse pre-filter.
  • Use a managed SDK (AWS Encryption SDK, Google Tink) to avoid subtle mistakes.

Compliance & policy reminders

  • Update privacy policies & data processing agreements.
  • Apply data protection techniques to backups and exports.
  • Implement deletion/forgotten-user flows that remove ciphertext and wrapped key material (or mark as deleted and rotate KEK).

Short checklist (practical steps to implement)

  1. Choose KMS/HSM (AWS KMS, GCP KMS, Azure Key Vault, or Vault+HSM).
  2. Pick a crypto library: Google Tink or AWS Encryption SDK for Java.
  3. Implement envelope encryption in app before DB insert. Store wrapped-DEK + iv + algorithm metadata.
  4. Keep coarse/plain index fields for admin search (or HMAC/hashed indexes).
  5. Deny KMS decrypt to DB/backup/admin roles; give to app role only.
  6. Add audit logging for KMS decrypts.
  7. Test key rotation, restore from backup, and “break glass” flows.
  8. Review logs to ensure no plaintext is accidentally logged.

If you want, I can:

  • Produce a concrete production-ready Java example using Google Tink or the AWS Encryption SDK with the specific KMS you use (AWS/GCP/Azure/HashiCorp).
  • Or draft a DB schema + migration plan that adds wrapped DEK fields, metadata, and coarse indexes.
  • Feature Flags – Enable Functionality from the BackEnd
  • Integrating xAI Grok API with Spring Boot
  • How to Progresively Integrate AI
  • What is an AI Agent
  • Flutter image scaling

Categories

  • Apps (22)
  • ChatGPT (23)
  • Choosing a Framework (38)
  • Flutter (256)
  • Graphical User Interface (14)
  • Marketing (116)
  • Software Development (281)
  • Spring (44)
  • StartUp (22)
  • Uncategorized (14)
  • Uncategorized (4)
  • Vaadin (14)

Tags

Algorithms (9) crypto (29) flutterdev (39) General (86) Java (7) QR & Bar Codes (3) Software Dev Choices (33) Spring Boot (1) standards (1) Theme (3) User Authentication & Authorization (9) User Experience (10) Utilities (19) WordPress (11)

Product categories

  • All Technologies (84)
    • Flutter Apps (24)
    • GPT (4)
    • Java (38)
    • Native Android (3)
    • PHP (9)
    • Spring (Boot) / Quarkus (35)
    • Utils (15)
    • Vaadin 24+ (27)
    • Vaadin 8 (1)
  • Apps (18)
    • Employees DB (1)
    • Notes (6)
    • Personal Budget (1)
    • Recipes Book (1)
    • Stuff Organizer (1)
    • To-Do (2)
  • PDF Books (3)
  • Source Code Generators (8)

Recent Posts

  • Feature Flags – Enable Functionality from the BackEnd
  • Integrating xAI Grok API with Spring Boot
  • How to Progresively Integrate AI
  • What is an AI Agent
  • Flutter image scaling

Post Categories

  • Apps (22)
  • ChatGPT (23)
  • Choosing a Framework (38)
  • Flutter (256)
  • Graphical User Interface (14)
  • Marketing (116)
  • Software Development (281)
  • Spring (44)
  • StartUp (22)
  • Uncategorized (14)
  • Uncategorized (4)
  • Vaadin (14)