Key Rotation
Key rotation is the practice of periodically replacing encryption keys with new ones. Tayra supports versioned rotation for both kinds of keys it manages:
- Data encryption keys (DEKs) - per-subject keys that wrap field-level data. Rotated via
ITayra.RotateKeyAsync(keyIdBase)ordotnet tayra rotate-data-key. This page covers DEK rotation. - Master keys - one per tenant in envelope-encryption mode; wraps every DEK in the data keystore. Rotated via
IMasterKeyStore.RotateAsync(masterKeyId)ordotnet tayra rotate-master-key. See Envelope Encryption for the master-rotation flow.
Both rotations preserve old versions so existing ciphertext stays readable until re-encrypted.
Why Rotate Keys?
Key rotation limits the impact of a potential key compromise:
- Reduce blast radius - If a key is exposed, only data encrypted with that specific version is affected.
- Compliance requirements - Many regulatory frameworks (PCI DSS, HIPAA) mandate periodic key rotation.
- Cryptographic hygiene - Limiting the amount of data encrypted under a single key reduces the effectiveness of statistical attacks.
Basic Rotation
Use tayra.RotateKeyAsync() to create a new versioned key for a data subject. The old key is preserved so existing ciphertext remains decryptable. Then call ReEncryptAsync to re-encrypt the object's PII fields with the new key.
// Rotate the key - creates a new versioned key (e.g., "cust-subject-rotate-001:v1").
// The old key is preserved so existing data can still be decrypted.
var rotationResult = await tayra.RotateKeyAsync("cust-subject-rotate-001");
// Re-encrypt the object with the new key
await tayra.ReEncryptAsync(rotationCustomer);
Console.WriteLine($"Old key: {rotationResult.OldKeyId}");
Console.WriteLine($"New key: {rotationResult.NewKeyId}");
Console.WriteLine($"Version: {rotationResult.NewVersion}");Rotation Result
RotateKeyAsync returns a KeyRotationResult with details about the rotation:
// KeyRotationResult contains all the details of the rotation
Console.WriteLine($"Rotated from '{rotationResult.OldKeyId}' to '{rotationResult.NewKeyId}'");
Console.WriteLine($"New version number: {rotationResult.NewVersion}");
// OldKeyId: "cust-subject-rotate-001" (original unversioned key)
// NewKeyId: "cust-subject-rotate-001:v1" (new versioned key)
// NewVersion: 1The KeyRotationResult record contains:
| Property | Description |
|---|---|
OldKeyId | The previous key ID (e.g., "cust-subject:v1" or the unversioned base key) |
NewKeyId | The newly created versioned key ID (e.g., "cust-subject:v2") |
NewVersion | The integer version number of the new key |
Versioning Scheme
Tayra uses a colon-separated version suffix for rotated keys:
Base key: "cust-subject-001" (original, unversioned)
After rotate: "cust-subject-001:v1" (first rotation)
After rotate: "cust-subject-001:v2" (second rotation)
After rotate: "cust-subject-001:v3" (third rotation)The rotator discovers existing versions by querying the key store with the base key ID as a prefix. The highest version number is incremented for the new key.
Old Key Preservation
Old keys are never deleted during rotation. This is by design:
- Existing data still needs the old key for decryption until it is re-encrypted.
- Gradual migration - You can re-encrypt records in batches over time, not all at once.
- Rollback safety - If something goes wrong during re-encryption, old ciphertext is still readable.
To clean up a specific old key version after all data has been re-encrypted, delete that key ID directly via your IKeyStore (e.g., DeleteAsync("cust-subject-001:v1")).
ShredAsync Deletes All Versions
tayra.ShredAsync(subjectId) is the full-erasure operation: it deletes the subject's base key and every key under the {subjectId}: prefix - all rotated versions and all group keys. This guarantees crypto-shredding remains complete after rotation, but it also means ShredAsync is not a per-version cleanup tool - it destroys the current key too.
Re-Encryption Workflow
A typical rotation workflow for a batch of records:
- Call
tayra.RotateKeyAsync()to create the new versioned key. - Load each record from your database.
- Call
tayra.ReEncryptAsync()on each record to decrypt with the old key and re-encrypt with the new one. - Save the updated record back to the database.
- Optionally shred old key versions once all records have been migrated.
WARNING
ReEncryptAsync modifies the object's PII fields in-place. Make sure to persist the re-encrypted values to your database after calling it.
When to Rotate
Consider rotating keys:
- On a schedule - Monthly or quarterly, depending on your compliance requirements.
- After a suspected breach - Rotate affected keys immediately and re-encrypt all data.
- When an employee leaves - If the employee had access to key material.
- Before key retirement - Rotate to a new key, re-encrypt all data, then shred the old key.
See Also
- Crypto-Shredding - Delete keys entirely for erasure
- Key Retention - Automate key lifecycle with TTL policies
- Breach Notification - Assess impact and respond to incidents
