Data Protection Key Encryption
Chronicle uses ASP.NET Core Data Protection to securely manage encryption keys for OAuth tokens and other sensitive data. In production environments, these keys must be protected with an X.509 certificate to ensure security across multiple Chronicle instances.
Overview
When Chronicle runs in a clustered environment, all instances need access to the same Data Protection keys to correctly encrypt and decrypt tokens. These keys are stored in MongoDB and shared across all instances using Orleans grains.
In production, an encryption certificate is required to protect these keys at rest. In development mode, the certificate is optional for convenience.
Configuration
JSON Configuration
Add the encryption certificate configuration to your chronicle.json:
{
"encryptionCertificate": {
"certificatePath": "/path/to/encryption-cert.pfx",
"certificatePassword": "your-certificate-password"
}
}
Environment Variables
Configure using environment variables (recommended for containerized deployments):
# Path to the PFX certificate file
Cratis__Chronicle__EncryptionCertificate__CertificatePath=/app/certs/encryption-cert.pfx
# Certificate password
Cratis__Chronicle__EncryptionCertificate__CertificatePassword=your-certificate-password
Configuration Properties
| Property | Type | Required (Production) | Description |
|---|---|---|---|
| certificatePath | string | Yes | Path to the PFX certificate file |
| certificatePassword | string | Yes* | Password for the certificate (* can be empty if cert has no password) |
Generating a Certificate
You can use the same certificate for both TLS and Data Protection key encryption, or generate a separate certificate specifically for key encryption.
Using .NET CLI
# Generate a certificate for key encryption
dotnet dev-certs https -ep ./encryption-cert.pfx -p YourSecurePassword123
Using OpenSSL
# Generate a private key
openssl genrsa -out encryption.key 2048
# Create a self-signed certificate
openssl req -x509 -new -nodes -key encryption.key -sha256 -days 3650 \
-out encryption.crt \
-subj "/CN=Chronicle Data Protection/O=Your Organization"
# Convert to PFX format
openssl pkcs12 -export -out encryption-cert.pfx \
-inkey encryption.key -in encryption.crt \
-password pass:YourSecurePassword123
Docker Configuration
Mount the certificate when running Chronicle in Docker:
version: '3.8'
services:
chronicle:
image: cratis/chronicle:latest
ports:
- "8080:8080"
- "35000:35000"
volumes:
- ./certs/encryption-cert.pfx:/app/certs/encryption-cert.pfx:ro
environment:
- Cratis__Chronicle__Storage__ConnectionDetails=mongodb://mongodb:27017
- Cratis__Chronicle__EncryptionCertificate__CertificatePath=/app/certs/encryption-cert.pfx
- Cratis__Chronicle__EncryptionCertificate__CertificatePassword=YourSecurePassword123
Using Same Certificate for TLS and Encryption
You can use the same PFX certificate for both TLS and Data Protection key encryption:
services:
chronicle:
image: cratis/chronicle:latest
volumes:
- ./certs/chronicle.pfx:/app/certs/chronicle.pfx:ro
environment:
# TLS configuration
- Cratis__Chronicle__Tls__CertificatePath=/app/certs/chronicle.pfx
- Cratis__Chronicle__Tls__CertificatePassword=YourPassword
# Data Protection key encryption (same certificate)
- Cratis__Chronicle__EncryptionCertificate__CertificatePath=/app/certs/chronicle.pfx
- Cratis__Chronicle__EncryptionCertificate__CertificatePassword=YourPassword
Multi-Instance Deployments
In clustered deployments, all Chronicle instances must use the same encryption certificate. This ensures that any instance can decrypt Data Protection keys stored in the shared MongoDB database.
services:
chronicle-1:
image: cratis/chronicle:latest
environment:
- Cratis__Chronicle__EncryptionCertificate__CertificatePath=/app/certs/encryption-cert.pfx
- Cratis__Chronicle__EncryptionCertificate__CertificatePassword=SharedPassword
volumes:
- ./certs/encryption-cert.pfx:/app/certs/encryption-cert.pfx:ro
chronicle-2:
image: cratis/chronicle:latest
environment:
- Cratis__Chronicle__EncryptionCertificate__CertificatePath=/app/certs/encryption-cert.pfx
- Cratis__Chronicle__EncryptionCertificate__CertificatePassword=SharedPassword
volumes:
- ./certs/encryption-cert.pfx:/app/certs/encryption-cert.pfx:ro
Development Mode
In development mode (when Chronicle is built with the DEVELOPMENT configuration), the encryption certificate is optional. This allows for easier local development without certificate management overhead.
Auto-Generated Certificates (Development Only)
When no encryption certificate is configured and Chronicle is compiled with DEVELOPMENT mode, Chronicle will automatically generate a self-signed certificate for development purposes. This certificate is:
- Location: Stored in a
certificatesfolder in the current working directory - Filename:
encryption-cert.pfx - Password:
chronicle-auto-generated(auto-assigned) - Validity: 10 years from generation
- Reuse: If the certificate already exists, Chronicle will use it instead of generating a new one
This feature is designed to simplify local development and testing. The certificate is automatically created on first use and persisted for subsequent runs, ensuring encrypted data remains accessible across restarts.
Important: In production builds (without the DEVELOPMENT directive), Chronicle will throw an
EncryptionCertificateNotConfiguredexception if no certificate is configured. Auto-generation only occurs in development mode.
Running Without Configuration
To run without a certificate in development:
# No certificate configuration needed in development
docker run -d \
--name chronicle-dev \
-p 8080:8080 \
-p 35000:35000 \
-e Cratis__Chronicle__Storage__ConnectionDetails=mongodb://localhost:27017 \
cratis/chronicle:latest-development
Warning: Auto-generated certificates should never be used in production environments. They are not cryptographically secure for production use and are intended only for development convenience. Production deployments must configure a proper encryption certificate.
Security Best Practices
- Use strong passwords - Certificate passwords should be complex and unique
- Protect certificate files - Store certificates securely and limit access
- Rotate certificates - Implement a certificate rotation strategy for production
- Separate concerns - Consider using different certificates for TLS and key encryption
- Backup certificates - Ensure certificates are backed up securely; losing the certificate means losing access to encrypted keys
- Use secrets management - In production, use a secrets manager (Azure Key Vault, HashiCorp Vault, etc.) to store certificate passwords
Troubleshooting
Certificate Not Found
If you see an error about the certificate not being found:
- Verify the certificate path is correct and accessible
- Check file permissions on the certificate file
- Ensure the path uses forward slashes in Docker/Linux environments
Invalid Certificate Password
If authentication fails after configuration:
- Verify the certificate password is correct
- Check for special characters that may need escaping in environment variables
- Ensure the password matches what was used during certificate generation
Production Startup Failure
In production, Chronicle will fail to start if no encryption certificate is configured:
InvalidOperationException: An encryption certificate is required in production for Data Protection key security.
Configure 'EncryptionCertificate:CertificatePath' and 'EncryptionCertificate:CertificatePassword'
in your configuration.
Ensure you have configured the certificate path and password as described above.
Next Steps
- Local Certificates - TLS certificate setup for development
- Production Hosting - Production deployment requirements
- Configuration - Complete configuration reference