Skip to content

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) or dotnet 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) or dotnet 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.

cs
// 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}");
anchor

Rotation Result

RotateKeyAsync returns a KeyRotationResult with details about the rotation:

cs
// 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: 1
anchor

The KeyRotationResult record contains:

PropertyDescription
OldKeyIdThe previous key ID (e.g., "cust-subject:v1" or the unversioned base key)
NewKeyIdThe newly created versioned key ID (e.g., "cust-subject:v2")
NewVersionThe 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:

  1. Existing data still needs the old key for decryption until it is re-encrypted.
  2. Gradual migration - You can re-encrypt records in batches over time, not all at once.
  3. 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:

  1. Call tayra.RotateKeyAsync() to create the new versioned key.
  2. Load each record from your database.
  3. Call tayra.ReEncryptAsync() on each record to decrypt with the old key and re-encrypt with the new one.
  4. Save the updated record back to the database.
  5. 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