Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Applied Cryptography for Developers  Module 9 – Data at rest and key management

Key lifecycle, rotation and KMS

The states a key moves through, why ciphertexts name their keys, and what a KMS adds: encryption context, audit logs, resumable rotation and secret scanning.

  • Intermediate
  • 25 minutes
  • Examples run with Node.js 24.21.0, Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 8 of 25 minutes free
Free: A key moves through states, and each state allows different uses · Every ciphertext names its key and version, so keys and algorithms can change · What a KMS adds to a key ring. With a pass: 3 in-depth parts and 3 interview answers, about 17 minutes.

What you will learn

  • Plan a key's life from generation and storage through rotation, revocation and destruction
  • Record a key ID and a version in every ciphertext so that keys and algorithms can change
  • Use a KMS with an encryption context and read the audit log it leaves
  • Run a re-encryption job that can stop and start again without losing a record
  • Detect secrets in source code, sample configuration and logs, and say where each belongs

Before you start

What you’ll learn · Before you start

What you will learn

  • Plan a key's life from generation and storage through rotation, revocation and destruction
  • Record a key ID and a version in every ciphertext so that keys and algorithms can change
  • Use a KMS with an encryption context and read the audit log it leaves
  • Run a re-encryption job that can stop and start again without losing a record
  • Detect secrets in source code, sample configuration and logs, and say where each belongs

Before you start

On this page

A service holds 40 million encrypted rows under a key made three years ago, and the engineer who knew where that key lives has left. If the ciphertexts do not say which key made them, nobody can tell which rows a rotation would touch, so the choice is to re-encrypt everything or to do nothing.

This lesson follows the key lifecycle: The stages of a key's life as NIST SP 800-57 lays them out: generation, activation, use, deactivation, possible compromise, and destruction. Each state allows different uses of the key. from generation to destruction, puts a key ID and a version into every ciphertext so that keys and algorithms can change, and shows what a KMS: A key management service: it generates, stores and uses keys on your behalf through an API, so that key material never leaves it and every use can be logged. adds. A KMS keeps keys where code cannot read them, binds an encryption context to each ciphertext and logs every use.

A key moves through states, and each state allows different uses

NIST SP 800-57 describes the key lifecycle as a few states. A new key is pre-activation: generated but not yet authorised, so it protects nothing. In the active state it may protect new data and open data it protected. When a newer key takes over, it becomes deactivated: it protects nothing new, but it may still open old data until the last copy of that data is gone. A key can also be suspended while a possible exposure is checked: it protects nothing new, and it returns to active or moves on. A key that is exposed, or suspected to be, is compromised: it is revoked and must never protect again. At the end every key is destroyed, and only metadata such as its name and history may stay. Step through one key’s life:

Interactive · step by step One key from its generation to its destruction Step through the life of one key. Two columns; the left one is the normal path, the right one the states a key can step aside into.
One key from its generation to its destruction, step 6 of 6. Showing Pre-activation, Active, Deactivated, Destroyed, Suspended, Compromised. In focus: Destroyed.authoriseda newer key takes overlast copy goneexposure suspectedPre-activationmadeActiveencrypts and decryptsDeactivateddecrypts onlyDestroyedonly metadata staysSuspendedset aside for a timeCompromisedrevoked

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. Generated. A key is made by a key store or an HSM from a good random source. In this first state it may prove that it exists, but it may not protect any data yet.
  2. Active. When the key is authorised, its cryptoperiod begins. It may protect new data, which is its originator-usage period, and it may open the data it protected.
  3. A newer key takes over. A rotation makes another key the active one. This key becomes deactivated. It protects nothing new, but it still opens old data until the last copy of that data is gone, its recipient-usage period.
  4. Set aside. A key can be suspended for a time, for instance while a possible exposure is checked. Nothing may be protected with it, and it may return to active or move on.
  5. Exposed. A key that is exposed, or suspected to be, becomes compromised and is revoked. It must never protect anything again, and everyone who relied on it is told.
  6. Destroyed. Every state ends in destruction. The key material is erased wherever it was kept, and only metadata such as its name, type and history may stay for audit. A copy that survives undoes it.

A key’s cryptoperiod: The span of time in which a key is authorised for use. It has an originator-usage period, when the key may protect data, and a recipient-usage period, when it may still open what it protected. has two parts: the originator-usage period, when it may protect data, and the recipient-usage period, when it may still open what it protected. SP 800-57 gives order-of-magnitude advice for a data-encryption key: a day or a week when it encrypts large volumes quickly, up to two years for smaller volumes, and a recipient period of no more than three years beyond. Rotation is what ends one key’s originator period and starts the next one’s. The automatic rotation of an AWS KMS key makes new key material every 365 days unless you choose another period.

Key idea

Plan the whole life of a key, from the day it is made to the day it is destroyed, because each state lets it do something different.

Every ciphertext names its key and version, so keys and algorithms can change

A ciphertext that does not name its key can only be opened by trying keys, and trying keys is how rotations go wrong. The fix is a short header in front of every ciphertext: a version, which fixes the algorithm and the layout, and a key ID, which picks the key. The header is not secret, but it must not be changeable, so it goes into the associated data of the AEAD. ASVS 5.0 asks for this agility: requirement 11.2.2 says it must be possible to replace keys and re-encrypt data, and requirement 11.1.2 asks for an inventory of every key, algorithm and certificate in use. The program keeps a key ring with those rules, rotates it, and retires a key twice:

A key ring: key ids in the header, one error for every failure JavaScript · keyring-js/keyring.mjs
// A key ring with a versioned ciphertext: version byte, key id, nonce and AES-256-GCM output, with the header as associated
// data. A key is active (it encrypts and decrypts), decrypt-only (a newer key took over) or gone. The program rotates, shows
// that old records still open, that a header cannot be altered, that every failure looks the same, and what retiring a key
// does to records that still name it. The key bytes are lab values; production keys come from a KMS.
import { createCipheriv, createDecipheriv, createHmac, randomBytes } from 'node:crypto';

const VERSION = 1;
const fail = () => new Error('cannot decrypt');

class KeyRing {
  #keys = new Map(); // id -> { bytes, state }
  add(id, bytes) {
    this.#keys.set(id, { bytes, state: 'decrypt-only' });
  }
  activate(id) {
    if (!this.#keys.has(id)) throw new Error(`no key ${id}`);
    for (const k of this.#keys.values()) if (k.state === 'active') k.state = 'decrypt-only'; // the old active key stops encrypting
    this.#keys.get(id).state = 'active';
  }
  retire(id) {
    if (this.#keys.get(id)?.state === 'active') throw new Error('the active key cannot be retired');
    this.#keys.delete(id);
  }
  get activeId() {
    return [...this.#keys].find(([, k]) => k.state === 'active')?.[0];
  }
  encrypt(text, aad = '') {
    const id = this.activeId;
    const header = Buffer.concat([Buffer.from([VERSION, id.length]), Buffer.from(id)]);
    const nonce = randomBytes(12);
    const c = createCipheriv('aes-256-gcm', this.#keys.get(id).bytes, nonce);
    c.setAAD(Buffer.concat([header, Buffer.from(aad)]));
    return Buffer.concat([header, nonce, c.update(text, 'utf8'), c.final(), c.getAuthTag()]);
  }
  // Every failure is the same error: a short blob, another version, an unknown key, a changed header, a bad tag.
  decrypt(blob, aad = '') {
    try {
      if (blob.length < 2 + 12 + 16 || blob[0] !== VERSION) throw fail();
      const idLen = blob[1];
      const header = blob.subarray(0, 2 + idLen);
      const key = this.#keys.get(blob.subarray(2, 2 + idLen).toString());
      if (!key) throw fail();
      const d = createDecipheriv('aes-256-gcm', key.bytes, blob.subarray(2 + idLen, 14 + idLen));
      d.setAAD(Buffer.concat([header, Buffer.from(aad)]));
      d.setAuthTag(blob.subarray(-16));
      return Buffer.concat([d.update(blob.subarray(14 + idLen, -16)), d.final()]).toString('utf8');
    } catch {
      throw fail();
    }
  }
}
const keyIdOf = (blob) => blob.subarray(2, 2 + blob[1]).toString(); // the header is readable without any key
const lab = (label) => createHmac('sha256', 'msc key ring lab').update(label).digest();
const tryDecrypt = (ring, blob, aad) => {
  try {
    return ring.decrypt(blob, aad);
  } catch (e) {
    return e.message;
  }
};

const ring = new KeyRing();
ring.add('k1', lab('k1'));
ring.activate('k1');
const rows = ['alice', 'bhavna', 'chitra'].map((name, i) => ({ id: i, blob: ring.encrypt(`note for ${name}`, `row ${i}`) }));
console.log(`k1 active: ${rows.length} records, all name ${[...new Set(rows.map((r) => keyIdOf(r.blob)))].join(', ')} in their header`);

// Rotation: k2 is added and activated. k1 keeps opening what it sealed, and no longer seals.
ring.add('k2', lab('k2'));
ring.activate('k2');
const fresh = { id: 3, blob: ring.encrypt('note for deepak', 'row 3') };
rows.push(fresh);
const byKey = rows.reduce((m, r) => m.set(keyIdOf(r.blob), (m.get(keyIdOf(r.blob)) ?? 0) + 1), new Map());
console.log(`after the rotation, active ${ring.activeId}: records by key ${[...byKey].map(([k, n]) => `${k}=${n}`).join(' ')}; old records still open: ${rows.every((r) => tryDecrypt(ring, r.blob, `row ${r.id}`).startsWith('note for'))}`);

// What the header protects.
const moved = Buffer.from(rows[0].blob);
moved[2] = '2'.charCodeAt(0); // k1 becomes k2 in the header
console.log(`header changed from k1 to k2: ${tryDecrypt(ring, moved, 'row 0')}; another row's associated data: ${tryDecrypt(ring, rows[0].blob, 'row 9')}`);
const unknown = Buffer.from(rows[0].blob);
unknown[3] = '9'.charCodeAt(0); // k1 becomes k9
console.log(`unknown key k9: ${tryDecrypt(ring, unknown, 'row 0')}, a truncated blob: ${tryDecrypt(ring, rows[0].blob.subarray(0, 10), 'row 0')}: the same error each time`);

// Retiring a key that records still name.
try {
  ring.retire('k2');
} catch (e) {
  console.log(`retire the active key: ${e.message}`);
}
const lost = new KeyRing();
lost.add('k2', lab('k2'));
lost.activate('k2');
console.log(`k1 retired too early: ${rows.filter((r) => tryDecrypt(lost, r.blob, `row ${r.id}`).startsWith('note for')).length} of ${rows.length} records open`);
for (const r of rows.filter((r) => keyIdOf(r.blob) === 'k1')) r.blob = ring.encrypt(ring.decrypt(r.blob, `row ${r.id}`), `row ${r.id}`); // re-encrypt under the active key
ring.retire('k1');
console.log(`k1 retired after re-encrypting its ${byKey.get('k1')} records: ${rows.filter((r) => tryDecrypt(ring, r.blob, `row ${r.id}`).startsWith('note for')).length} of ${rows.length} records open`);

Output

k1 active: 3 records, all name k1 in their header
after the rotation, active k2: records by key k1=3 k2=1; old records still open: true
header changed from k1 to k2: cannot decrypt; another row's associated data: cannot decrypt
unknown key k9: cannot decrypt, a truncated blob: cannot decrypt: the same error each time
retire the active key: the active key cannot be retired
k1 retired too early: 1 of 4 records open
k1 retired after re-encrypting its 3 records: 4 of 4 records open

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node keyring.mjs

After the rotation the ring encrypted a new record under k2 while the three k1 records still opened, and every record named its key, so none had to be tried against the other. A header changed from k1 to k2, an unknown key k9, another row’s associated data and a truncated blob all gave the same error. Retiring k1 too early left 1 of 4 records readable, because the three that still named it could not be opened. Re-encrypting those three first left 4 of 4.

Checkpoint

Check: The states of a key · A key ID and version in every ciphertext

  1. Question 1 of 2 A key has been replaced by a newer one, but records made with it still exist. In which state is it?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Deactivated, which allows opening old data but protects nothing new

    A retired-for-encryption key is not yet a retired key: it decrypts until the last copy of the data it protected is gone, and only then is it destroyed.

  2. Question 2 of 2 The key ID is in the header of a ciphertext and also in its associated data. What does the second do?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: A changed id makes the tag fail, instead of sending the reader to another key

    The header is read before anything is decrypted, so it must be covered by the tag. Otherwise an attacker could change the version or the key id and steer the reader.

What a KMS adds to a key ring

A key ring inside your process still lets any code in that process read the key. Moving the keys out of reach brings in five terms:

  • A KMS, a key management service, generates, stores and uses keys on your behalf, so that the key material never leaves it and every use can be logged.
  • An HSM: A hardware security module: tamper-resistant hardware that holds keys and does cryptographic operations inside it, so that the keys cannot be read out. Many KMS products run on HSMs. is tamper-resistant hardware that holds keys and does the cryptography inside it; many KMS products run on HSMs.
  • An encryption context: Non-secret key and value pairs, such as a tenant id, that a KMS binds to a ciphertext as associated data. The same pairs must be given to decrypt, and the service writes them to its log in clear text. is non-secret data, such as a tenant id, bound to a ciphertext so that it opens only when the same data is presented again.
  • An audit log records each use of a key: who called, which operation, which key and context, and whether the call succeeded.
  • A secrets manager: A service that stores secrets such as passwords and tokens, gives each application access to only its own, can rotate them, and logs their use. stores and rotates secrets such as passwords and tokens, and hands them to applications when they run.

Production keys belong in a service like this, and the key ring above is the part of your code that talks to it.

A KMS call with an encryption context, and the audit log it leaves Pro: opens with a Pro, Premium or Ultimate pass

A KMS call carries more than a key name: it carries who is asking and a context that must match. This part runs a mock KMS through eight calls and reads the log they leave.

In this part:

  • An encryption context binds a ciphertext to the facts of its use
  • A mock KMS that refuses the wrong context, with its audit log
  • KMS, HSM or secrets manager: what each one holds

1 diagram · 1 runnable example (JavaScript) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Rotating at scale: cryptoperiods, a re-encryption job and its numbers Pro: opens with a Pro, Premium or Ultimate pass

Rotation is a project, not a button: choose a period, move the records, and retire the old key only when nothing names it. This part works out the numbers, and runs a job that crashes half way through a batch.

In this part:

  • What a managed rotation changes, and what it leaves
  • A re-encryption job that survives a crash
  • Build the key ring and its job
  • A key ring with versioned ciphertexts and a re-encryption job
    • Results of the sample tests

1 diagram · 1 runnable example (Python) · 1 exercise with sample tests · about 8 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Keeping secrets out of source, samples and logs: a scanner that finds seven, then none Pro: opens with a Pro, Premium or Ultimate pass

A key that is perfectly managed in a KMS still fails if a copy sits in a repository, a sample file or a log. This part sets the places a secret may live in order, scans a weak repository and a fixed one, and gives the order of a response when a secret leaks.

In this part:

  • The storage ladder, from never to a KMS
  • A scanner on a weak repository and on the fixed one
  • When a secret leaks: the order of the response

1 runnable example (JavaScript) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practise

A practice session asks about the states of a key and the header of a ciphertext, and gives the reason with every answer:

Practice that adapts to you

Practise this lesson

6 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 In which state does a key begin, the moment it is generated?

    Choose one answer.

    Show the answer: Question 1 of 6

    Answer: Pre-activation, where it protects nothing yet

    A key enters pre-activation on generation, and moves to active when it becomes available for use.

  2. Question 2 of 6 A data-encryption key stopped encrypting a year ago. It must still open records that nobody has re-encrypted. In which period is it?

    Choose one answer.

    Show the answer: Question 2 of 6

    Answer: Its recipient-usage period

    SP 800-57 recommends a recipient-usage period of no more than three years beyond the originator-usage period for such a key.

  3. Question 3 of 6 A key is declared compromised. Which statement is right?

    Choose one answer.

    Show the answer: Question 3 of 6

    Answer: It must never protect new data again, and it moves towards destruction with its metadata kept for audit

    Compromise overrides the calendar: stop protecting, replace, and decide what to re-encrypt.

  4. Question 4 of 6 What does the header of a versioned ciphertext name?

    Choose one answer.

    Show the answer: Question 4 of 6

    Answer: The version, which fixes the algorithm and the layout, and the key ID

    The version also lets a new algorithm arrive without breaking old records.

  5. Question 5 of 6 In the run, k1 was retired while 3 of the 4 records still named it. How many of the 4 could still be read?

    Type a number.

    Show the answer: Question 5 of 6

    Answer: 1 record

    Only the record under k2 opened. The three that named k1 could not be opened, because the key was gone.

  6. Question 6 of 6 A decrypt call receives a blob that names the key k9, which is not in the ring. What should the caller see?

    Choose one answer.

    Show the answer: Question 6 of 6

    Answer: The same error as for a bad tag, with the reason only in the operators' log

    An unknown key, a changed header and a truncated blob are all one case for the caller: it cannot decrypt.

Interview questions

  1. Fresher to mid · Warm-up

    Why should every ciphertext carry a version and a key ID, and what does a rotation look like when it does?

    A ciphertext is read long after it is written, perhaps by newer code, so it must say how to read it. The version fixes the algorithm and the layout, and the key ID picks the key, so the reader never tries keys in turn. Both go into the associated data, so a changed id makes the tag fail instead of steering the reader. Then a rotation has three steps. Add the new key and make it the active one, so that new data is written under it. Leave the old key in the ring for decrypting only, so that nothing stops opening. Then re-encrypt the old records, counting by key id, and retire the old key only when the count is zero. In the lesson's run, retiring a key while 3 of 4 records still named it left 1 of 4 readable.

  2. Senior to staff

    An AES key encrypts 40 TB across 200 million objects in object storage, written by many services. Plan its rotation: how new writes switch, how reads keep working, how the re-encryption job is ordered and resumed, what limits you expect, and when the old key goes.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    A developer committed a cloud access key to a private repository three days ago. Walk through the response in order, say how you find out what the key did in those three days, and say what you change so that it cannot recur.

    Model answer, Pro. First step and follow-ups

  4. Senior to staff

    Design the encryption contexts for a multi-tenant service that uses a KMS: what goes into a context, what must never go in one, what it lets you enforce in policy, and what the audit log can then answer.

    Model answer, Pro. First step and follow-ups

Flashcards · 6 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What are the states of a key's lifecycle in NIST SP 800-57?

    Pre-activation: generated, protects nothing. Active: protects new data and opens old. Suspended: protects nothing new while a possible exposure is checked. Deactivated: protects nothing new but may still open old data. Compromised: exposed or suspected, revoked, never protects again. Destroyed: only metadata such as its name and history may stay.

  2. What is a cryptoperiod, and what does SP 800-57 advise for a data-encryption key?

    The span in which a key is authorised for use. The originator-usage period is when it may protect data; the recipient-usage period is when it may still open what it protected. Advice: a day or a week when it encrypts large volumes quickly, up to two years for smaller volumes, with a recipient period of no more than three years beyond. AWS KMS automatic rotation makes new key material every 365 days by default.

  3. Why put a version and a key ID in the header of every ciphertext?

    A ciphertext that does not name its key can only be opened by trying keys, which is how rotations go wrong. The version fixes the algorithm and layout and the key ID picks the key. The header is not secret but must not be changeable, so it goes into the AEAD's associated data. ASVS 5.0 asks for it: 11.2.2 wants keys that can be replaced, with the data re-encrypted, and 11.1.2 a list of all keys, algorithms and certificates in use.

  4. What happened in the key ring when k1 was retired too early?

    After the rotation, new records used k2 while the three k1 records still opened. Retiring k1 too early left only 1 of 4 records readable, because the three that still named k1 could not be opened; re-encrypting those three first left 4 of 4. A changed header, an unknown key, another row's associated data and a truncated blob all gave the same error.

  5. What do a KMS, an HSM, an encryption context and an audit log each add?

    A key ring inside your process lets any code in it read the key. A KMS generates, stores and uses keys for you, so key material never leaves it. An HSM is tamper-resistant hardware that does the cryptography inside it. An encryption context is non-secret data, such as a tenant id, bound to a ciphertext so it opens only when presented again. An audit log records who called, which operation, key and context, and whether it succeeded.

  6. What does a secrets manager do?

    A service that keeps secrets such as passwords and tokens, rotates them, and passes them to applications while they run.

Key takeaways

  • A key moves through pre-activation, active, suspended, deactivated, compromised and destroyed states, and each allows different uses, so its whole life is planned on the day it is made.
  • A version and a key ID in the authenticated header of every ciphertext let keys and algorithms change: after a rotation old records still opened, and a key retired too early left 1 of 4 records readable.
  • A KMS keeps keys out of the application’s reach and adds an encryption context, an audit log and, with an HSM, hardware protection.

Terms in this lesson

cryptoperiod
The span of time in which a key is authorised for use. It has an originator-usage period, when the key may protect data, and a recipient-usage period, when it may still open what it protected.
encryption context
Non-secret key and value pairs, such as a tenant id, that a KMS binds to a ciphertext as associated data. The same pairs must be given to decrypt, and the service writes them to its log in clear text.
HSM
A hardware security module: tamper-resistant hardware that holds keys and does cryptographic operations inside it, so that the keys cannot be read out. Many KMS products run on HSMs.
key lifecycle
The stages of a key's life as NIST SP 800-57 lays them out: generation, activation, use, deactivation, possible compromise, and destruction. Each state allows different uses of the key.
KMS
A key management service: it generates, stores and uses keys on your behalf through an API, so that key material never leaves it and every use can be logged.
secrets manager
A service that stores secrets such as passwords and tokens, gives each application access to only its own, can rotate them, and logs their use.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.