PII Protection¶
Curve provides automatic PII (Personally Identifiable Information) protection with declarative annotations.
Quick Start¶
import io.github.closeup1202.curve.spring.pii.annotation.PiiField;
import io.github.closeup1202.curve.spring.pii.type.PiiType;
import io.github.closeup1202.curve.spring.pii.strategy.PiiStrategy;
public class UserPayload implements DomainEventPayload {
@PiiField(type = PiiType.EMAIL, strategy = PiiStrategy.MASK)
private String email; // "user@example.com" → "u***@ex***.com"
@PiiField(type = PiiType.PHONE, strategy = PiiStrategy.ENCRYPT)
private String phone; // "+1234567890" → "AES-encrypted"
@PiiField(type = PiiType.NAME, strategy = PiiStrategy.HASH)
private String name; // "John Doe" → "5a4b3c2d..."
}
How It Works¶
flowchart LR
Obj[Java Object] -->|Jackson Serialize| PII[PII Processor]
PII -->|Strategy: MASK| Masked["j***@doe.com"]
PII -->|Strategy: ENCRYPT| Encrypted["ENC(a8f9...)"]
PII -->|Strategy: HASH| Hashed["hmac-sha256(...)"]
Masked --> Json[JSON Output]
Encrypted --> Json
Hashed --> Json
Process:
- Serialization Interception: Curve intercepts Jackson serialization process.
- Annotation Detection: Scans for
@PiiFieldannotations on fields. - Strategy Execution: Applies the configured strategy (MASK, ENCRYPT, HASH).
- Output Generation: Replaces the original value with the protected value in the JSON output.
Protection Strategies¶
1. MASK - Pattern-Based Masking¶
Partially hides data while keeping it recognizable.
Examples:
| PII Type | Original | Masked |
|---|---|---|
john.doe@example.com | j***@ex***.com | |
| PHONE | +1-555-123-4567 | +1-***-***-4567 |
| SSN | 123-45-6789 | ***-**-6789 |
| NAME | John Michael Doe | J*** M*** D*** |
| ADDRESS | 123 Main St, City | *** Main St, *** |
| CREDIT_CARD | 1234-5678-9012-3456 | ****-****-****-3456 |
When to use:
- Logs and audit trails
- Customer support dashboards
- Non-production environments
2. ENCRYPT - Reversible Encryption¶
AES-256-GCM encryption for reversible protection.
Output:
When to use:
- Data that needs to be decrypted later
- Cross-service communication
- Secure storage
Configuration:
Key Management
Store encryption keys in secure vaults (AWS Secrets Manager, HashiCorp Vault, etc.)
3. HASH - Irreversible Hashing¶
HMAC-SHA256 hashing for one-way protection with salt-based keyed hashing.
Output:
When to use:
- Analytics and aggregation
- Deduplication
- Data that should never be reversed
Configuration:
Supported PII Types¶
| PII Type | Description | Example |
|---|---|---|
EMAIL | Email addresses | john@example.com |
PHONE | Phone numbers | +1-555-123-4567 |
SSN | Social Security Numbers | 123-45-6789 |
NAME | Full names | John Doe |
ADDRESS | Physical addresses | 123 Main St |
CREDIT_CARD | Credit card numbers | 1234-5678-9012-3456 |
IP_ADDRESS | IP addresses | 192.168.1.1 |
GENERIC | Custom sensitive data | Any string |
Configuration¶
Enable PII Protection¶
curve:
pii:
enabled: true # Default: true
crypto:
# AES-256 encryption key (Base64-encoded 32-byte key)
default-key: ${PII_ENCRYPTION_KEY}
# Salt for hashing (recommended)
salt: ${PII_HASH_SALT}
Environment Variables¶
PII_ENCRYPTION_KEY=K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols= # Base64-encoded 32-byte key
PII_HASH_SALT=random-salt-for-hashing-123
KMS Integration¶
Curve supports external Key Management Services for enterprise-grade encryption key management.
AWS KMS (Envelope Encryption)¶
curve:
pii:
enabled: true
kms:
enabled: true
type: aws
# KMS module configuration
curve-kms:
aws:
key-id: "arn:aws:kms:us-east-1:123456789:key/abc-123"
region: us-east-1
cache-ttl-minutes: 60
cache-max-size: 100
How it works:
- AWS KMS generates a Data Encryption Key (DEK)
- DEK encrypts the PII data locally (AES-256-GCM)
- Encrypted DEK is stored alongside the ciphertext
- Decryption requests KMS to decrypt the DEK first
HashiCorp Vault (Static Key)¶
curve:
pii:
enabled: true
kms:
enabled: true
type: vault
# KMS module configuration
curve-kms:
vault:
path: "secret/data/curve/encryption-key"
key-field: "key"
How it works:
- Curve fetches the encryption key from Vault K/V secret engine
- Key is used for local AES-256-GCM encryption
- Key rotation is handled by updating the Vault secret
Advanced Usage¶
Custom Masking Patterns¶
Create custom masking logic:
public class CustomMaskingStrategy implements PiiMaskingStrategy {
@Override
public String mask(String value, PiiType type) {
// Custom masking logic
return maskCustom(value);
}
}
Register as Spring bean:
@Configuration
public class PiiConfig {
@Bean
public PiiMaskingStrategy customMaskingStrategy() {
return new CustomMaskingStrategy();
}
}
Conditional Protection¶
Protect fields conditionally based on environment:
public class UserPayload {
@PiiField(
type = PiiType.EMAIL,
strategy = PiiStrategy.MASK,
condition = "#{environment.getProperty('app.environment') == 'prod'}"
)
private String email;
}
Best Practices¶
DO¶
- Use MASK for logs - Keeps data recognizable for debugging
- Use ENCRYPT for storage - Allows decryption when needed
- Use HASH for analytics - Irreversible for aggregation
- Rotate keys regularly - Update encryption keys periodically
- Store keys securely - Use secret management services
DON'T¶
- Store encryption keys in source code
- Use HASH when you need to decrypt later
- Over-mask data (e.g., masking non-sensitive fields)
- Use weak keys (must be exactly 32 bytes, Base64-encoded)
Compliance¶
Curve's PII protection helps with:
- GDPR (General Data Protection Regulation)
- CCPA (California Consumer Privacy Act)
- HIPAA (Health Insurance Portability and Accountability Act)
- PCI DSS (Payment Card Industry Data Security Standard)
Legal Disclaimer
Curve provides tools for PII protection, but compliance is the responsibility of the application owner.
Examples¶
Complete User Event¶
public class UserRegisteredPayload implements DomainEventPayload {
private Long userId;
private String username; // Not sensitive
@PiiField(type = PiiType.EMAIL, strategy = PiiStrategy.MASK)
private String email;
@PiiField(type = PiiType.PHONE, strategy = PiiStrategy.ENCRYPT)
private String phone;
@PiiField(type = PiiType.NAME, strategy = PiiStrategy.HASH)
private String firstName;
@PiiField(type = PiiType.NAME, strategy = PiiStrategy.HASH)
private String lastName;
@PiiField(type = PiiType.ADDRESS, strategy = PiiStrategy.MASK)
private String address;
@PiiField(type = PiiType.IP_ADDRESS, strategy = PiiStrategy.HASH)
private String lastLoginIp;
private Instant createdAt;
}
Published Event:
{
"userId": 12345,
"username": "john_doe",
"email": "j***@ex***.com",
"phone": "AQIDAHj8...9f3a2b1c0",
"firstName": "5a4b3c2d...",
"lastName": "7f8e9d1a...",
"address": "*** Main St, ***",
"lastLoginIp": "8f7e6d5c...",
"createdAt": "2026-02-03T10:30:00Z"
}
What's Next?¶
-
Failure Recovery
Handle failures with DLQ
-
Transactional Outbox
Guarantee event delivery