Cloud security engineer validating AWS KMS alias routing, key dependencies, and ciphertext migration on dual monitors

Moving an AWS KMS Alias Does Not Move Your Encrypted Data

Tue, Sep 29, 2026

In AWS KMS, an alias is just a pointer: changing it does not magically re-encrypt or migrate data protected under the old key. To validate an alias retargeting, we must prove which key actually encrypted each ciphertext cohort, and ensure any expected re-encryption artifacts exist. This article guides a detailed two-key experiment and decision playbook for an authorized cloud team. We use two customer-managed symmetric CMKs (Key A and Key B), one test alias, and canary plaintexts to trace dependencies.

We record every step: CLI version, account/Region, identity, key ARNs, alias name, encryption contexts, ciphertext blobs, returned KeyId/SourceKeyId fields, and digests. We never assume the alias change itself migrated data; that remains untested. Instead we ask, for each ciphertext group: Can it still decrypt under the approved key path, and have we separately created and stored a re-encrypted copy if needed?

Based on the observed evidence, the team will either ACCEPT the alias update, WAIT for consistency, HOLD if evidence conflicts, or RE-ENCRYPT AND RECONCILE approved data. We explicitly do not retire the old key here. By the end we inventory mixed cohorts and hand off unresolved dependencies. This approach mirrors how cloud-security encryption and recovery practices emphasize tracing actual data evidence rather than just trusting configuration change.

1. Define the change as routing, not data migration

An AWS KMS alias is an independent resource, not a property of its key. Updating an alias simply points it to a new key; it does not re-encrypt existing ciphertext or move data. In our case, alias/test-lab initially targets Key A. When we update alias/test-lab to Key B, we have only changed the route for future requests. All ciphertext encrypted under Key A remains as-is. Crucially, a ciphertext record includes its original KeyId internally, so AWS KMS knows which key material to use. The question for the security team is: “After changing the alias, which key(s) actually decrypt each ciphertext, and have we re-encrypted approved data as needed?”

We classify each ciphertext into one of three immutable cohorts:

  • Cohort A (Legacy): Ciphertext produced before the alias change, encrypted under Key A (alias C_A).

  • Cohort B (New): Ciphertext produced after alias now points to Key B, encrypted under Key B (alias C_B).

  • Cohort A→B (Migrated): Ciphertext that started in A and was explicitly re-encrypted to B (artifact C_A_to_B).

For each cohort, we observe the actual KeyId returned by AWS, not just infer from alias. Our decisions depend on explicit evidence (returned ARNs, cryptographic exceptions, plaintext digests). We make four decisions: ACCEPT (alias handoff verified for allowed data), WAIT (propagation or evidence pending), HOLD (conflicting or incomplete evidence), or RE-ENCRYPT AND RECONCILE (for data that should move but hasn’t). We never retire Key A just because alias moved; that retirement would require a full dependency inventory outside this test. Aliases can change instantly, but KMS operations are eventually consistent; we observe, not assume.

This distinction mirrors cloud-security tools and configuration reviews where configuration changes require confirming actual data effects. An alias update is a control-plane action; proving data-plane migration is a separate task.

2. Prepare an authorized two-key laboratory

We work in a nonproduction AWS account with explicit approval and a controlled budget. We operate only on a dedicated test alias and two test CMKs; no production keys or data are touched. The operator’s IAM role has only the needed KMS permissions on this alias and these keys. Costs are minimal (two CMKs, a few API calls). Before running any command, we confirm:

  • Account and Region: We use a single AWS account (ID 111122223333) in a fixed Region (e.g. us-west-2). This matches the alias scope rules.

  • CLI Version and Identity: All commands use AWS CLI v2 (pinned version) with a named profile. For example:

export AWS_PROFILE=testuser
export AWS_REGION=us-west-2
aws --version
# aws-cli/2.7.21 Python/3.9.16 Linux/Ubuntu/22.04 (installer)

We record the profile ARN for auditing. No secret keys are exposed in code.

  • Existing Keys and Alias: We assume two CMKs are already created: Key A ARN arn:aws:kms:us-west-2:111122223333:key/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee and Key B ARN arn:aws:kms:us-west-2:111122223333:key/11111111-2222-3333-4444-555555555555 (both with KeyUsage=ENCRYPT_DECRYPT, origin AWS_KMS). A dedicated alias alias/test-lab exists and currently points to Key A. In practice, you might create these:

aws kms create-key \
  --description "Key A test" \
  --key-usage ENCRYPT_DECRYPT \
  --origin AWS_KMS
aws kms create-key \
  --description "Key B test" \
  --key-usage ENCRYPT_DECRYPT \
  --origin AWS_KMS
aws kms create-alias --alias-name alias/test-lab --target-key-id <KeyA-ID>

For clarity, we list the ARNs we will use.

  • Encryption Context and Canary: We choose a small non-sensitive plaintext (a “canary”), e.g. the UTF-8 bytes of “Hello, KMS Alias!” stored in canary.txt. We also define an encryption context map (metadata) such as {"Purpose":"Test"}. This context is non-secret but added to authenticate encryption. We record its exact byte serialization (ordered key/value).

  • Permissions: The test role has kms:Encrypt, kms:Decrypt, kms:ReEncrypt*, and kms:UpdateAlias on the alias and both keys. No other broad permissions.

Importantly, we do not disable or schedule deletion of any key, nor change production aliases. This is a scoped lab.

2.1 Separate alias administration from cryptographic permissions

As a negative control, note that alias update itself does not grant decrypt rights. The IAM role must explicitly have Decrypt permission on Key A and Key B (via key policies), separate from UpdateAlias. We do not rely on alias-level permissions for cryptographic operations, only on the approved key policies. Thus even after changing alias to Key B, a decrypt still requires that Role to have kms:Decrypt on Key A for decrypting any legacy ciphertext. This separation ensures our experiment isolates alias routing from key use permissions. (Our alias test role indeed has both keys in its policy, so the rekey path is authorized.)

3. Create the pre-change ciphertext ledger

Before touching the alias, we create a “ledger” of a pre-change canary. This ensures we have independent proof of the plaintext and its encrypting key. Steps:

  1. Plaintext Oracle: We define an independent oracle digest of the plaintext. For example:

echo -n "Hello, KMS Alias!" > canary.txt
sha256sum canary.txt
# e.g.: 3f786850e387550fdab836ed7e6dc881de23001b...

We save this hash as the expected plaintext.

  1. Encrypt under current alias: Using alias/test-lab (points to Key A now):

aws kms encrypt \
  --key-id alias/test-lab \
  --plaintext fileb://canary.txt \
  --encryption-context Purpose=Test \
  --output json

Expected output (JSON):

{
  "CiphertextBlob": "<base64 C_A>",
  "KeyId":
    "arn:aws:kms:us-west-2:111122223333:key/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}

We record:

  • CiphertextBlob (Base64, call it C_A file),

  • KeyId (this must be Key A’s ARN),

  • EncryptionContext (the returned data won’t include it, but we log that we used {"Purpose":"Test"}).

  • Algorithm (SYMMETRIC_DEFAULT).

  • RequestId/Date (if shown).

Validation: The returned "KeyId" field confirms the ciphertext was encrypted by Key A. We save C_A to disk (e.g. base64-decode it to binary file C_A.bin) and compute its SHA-256 digest.

  1. Decrypt verification: To ensure C_A is valid, we decrypt explicitly with Key A:

aws kms decrypt \
  --ciphertext-blob fileb://C_A.bin \
  --key-id arn:aws:kms:us-west-2:111122223333:key/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee \
  --encryption-context Purpose=Test \
  --output text --query Plaintext

This should succeed, returning the Base64-encoded plaintext. We Base64-decode to check it equals our original (compare SHA256). This confirms our oracle and logs alignment. If this fails (e.g. Key A disabled), we would hold, but assume it passes.

We now have a ledger entry for Cohort A: plaintext hash, C_A blob, Key A, context. This is our ground truth that “C_A decrypts to original under Key A”. Keep C_A unchanged throughout (we only copy it for re-encryption).

4. Retarget the alias and observe the transition

Next we update the alias to point to Key B. This is a single API call:

aws kms update-alias \
  --alias-name alias/test-lab \
  --target-key-id \
    arn:aws:kms:us-west-2:111122223333:key/11111111-2222-3333-4444-555555555555

Expected result: HTTP 200, no body, no error (empty JSON). We record the command and note the time.

Because KMS alias changes are eventually consistent, we do not instantly assume the alias has switched for all clients. We observe the alias target:

aws kms list-aliases --query "Aliases[?AliasName=='alias/test-lab']"

This should eventually show "TargetKeyId": "arn:...:key/11111111-2222-3333-4444-555555555555". We may need to poll a few seconds.

Importantly, an alias update response means the update request was accepted, but not that every AWS client sees it yet. We will wait until our observations converge. We do not remove Key A or alter policies yet; we’ll consider retirement later with evidence.

4.1 A successful update is not an observation of every client

Even after UpdateAlias returns success, different KMS nodes may momentarily disagree on the alias target. For example, a second or two after the call, encrypting via alias/test-lab could still return Key A’s ID. We treat such outliers as propagation delay, not errors. We continue sampling:

aws kms encrypt \
  --key-id alias/test-lab \
  --plaintext fileb://canary.txt \
  --encryption-context Purpose=Test \
  --output json

We log each result’s "KeyId". Initially, we might see:

  • First tries: "KeyId": "...key/aaaaaaaa-..." (Key A)

  • After some time: "KeyId": "...key/11111111-..." (Key B) repeatedly.

We classify each sample by its "KeyId". We do not “relabel” an early Key A result as if it were Key B; it truly belongs to Cohort A. We define a policy: if any alias-encrypt still returns Key A, we WAIT (propagation incomplete) rather than accept. After a short bounded wait (say 30 seconds or a few iterations), we expect all alias-based encrypts return Key B. At that point we record alias -> Key B as observed. (If we never see Key B, that’s a HOLD or error.)

5. Prove which key encrypted the new cohort

Once the alias consistently resolves to Key B, we encrypt a new canary through the alias:

aws kms encrypt \
  --key-id alias/test-lab \
  --plaintext fileb://canary.txt \
  --encryption-context Purpose=Test \
  --output json

Expected output:

{
  "CiphertextBlob": "<base64 C_B>",
  "KeyId":
    "arn:aws:kms:us-west-2:111122223333:key/11111111-2222-3333-4444-555555555555",
  "EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}

The returned KeyId should be Key B’s ARN, proving this new ciphertext (call it C_B) was encrypted under Key B. We save C_B and note its digest. This is Cohort B, the “post-handoff” data. It is not bitwise equal to C_A (encryption is randomized). We verify: decrypting C_B with Key B yields our canary plaintext.

By labeling C_B under actual Key B (not calling it “alias encryption”), we avoid confusion over timing. We now have separate evidence: Cohort A (via Key A) and Cohort B (via Key B). No data in Cohort B was written to S3 or database yet; it only exists as this captured result. If an application had produced C_B, we haven’t recorded that storage step, an important gap discussed next.

6. Decrypt historical ciphertext through three paths

Now we test decrypting the original Cohort A ciphertext (C_A) in three ways: using Key A explicitly, using the alias (now pointing to B), and omitting KeyId (using alias context).

  • Explicit Key A:

aws kms decrypt \
  --ciphertext-blob fileb://C_A.bin \
  --key-id arn:aws:kms:us-west-2:111122223333:key/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee \
  --encryption-context Purpose=Test \
  --output text --query Plaintext

Expected: succeeds, returns Base64 plaintext matching our canary. This confirms Key A still decrypts its own ciphertext.

  • Alias (to Key B):

aws kms decrypt \
  --ciphertext-blob fileb://C_A.bin \
  --key-id alias/test-lab \
  --encryption-context Purpose=Test

Expected: Key Mismatch Error. Since the alias now refers to Key B but the ciphertext needs Key A, AWS should return an IncorrectKeyException with a message that the key cannot decrypt this data. This shows the alias is no substitute for the true key.

  • No KeyId (alias implicitly):

aws kms decrypt \
  --ciphertext-blob fileb://C_A.bin \
  --encryption-context Purpose=Test \
  --output text --query Plaintext

With symmetric ciphertext, omitting KeyId lets KMS read metadata to find the original key. AWS KMS will detect that C_A was encrypted under Key A. Expected: It should succeed and decrypt under Key A, despite alias pointing elsewhere. If Key A’s material is intact and we have permission, AWS routes decryption correctly. This contrasts with using alias: even though the alias name isn’t specified, KMS sees the metadata and uses Key A’s material.

By capturing all three results, we differentiate: successful decrypt under A (explicit and implicit), vs explicit request to wrong key. This underscores expected-key binding: the ciphertext carries its own key identity metadata, so alias alone does not matter. We record each result or exception. If, for instance, the no-KeyId call had failed with AccessDenied or IncorrectKeyException, that would indicate either missing Key A permission or alias misassignment, and we would HOLD until clarified. But in a normal case, it confirms our expectation: Key A can decrypt its data even after alias changed.

6.1 Expected-key binding is stronger than a convenient alias

These decrypt tests emphasize that KMS enforces explicit key verification. Even when alias/test-lab now points at Key B, it does not trickle down to allow Key B to decrypt Key A’s ciphertext. The ciphertext blob itself, for symmetric keys, encodes the original KeyId, so AWS KMS checks it. If that KeyId doesn’t match the provided or implied key, KMS errors out. In short, an alias “shortcut” cannot bypass explicit key binding. We never saw KMS quietly use Key B on C_A just because alias was B; it properly threw an IncorrectKeyException. This negative control confirms that alias switching does not override the cryptographic provenance embedded in ciphertext.

7. Classify errors without hiding the cause

During decryption or any operation, different errors have different meanings. In our tests we categorize them:

  • IncorrectKeyException (HTTP 400): Means KMS explicitly recognized a key mismatch. This is what we expect when using the wrong key for ciphertext.

  • AccessDeniedException (HTTP 400): Would mean our IAM role lacks decrypt permission on the specified key, not that the key was wrong. We are careful to use a role that has Decrypt rights on both keys.

  • InvalidCiphertextException: Means the ciphertext or its context is corrupted or wrong, unrelated to alias. We should not get this unless we tamper with the blob.

  • Wrong Region: If we use a key ID from a different Region, KMS returns NotFound. We ensure all commands use the same Region and account.

  • Other transient errors (Throttling, InternalError): May occur but aren’t cryptographic issues. We can retry once if they happen.

For our experiment, we log the exact AWS error code and message. For example, an expected Key mismatch might look like:

An error occurred (IncorrectKeyException) when calling the Decrypt operation:
The ciphertext was encrypted under a different KMS key (KeyId mismatches).

We do not blanket-catch exceptions. We let the manifest clearly show “IncorrectKeyException” vs “AccessDenied” vs “InvalidCiphertext”. This precision matters for decision criteria. (In production, a developer might see a generic “Decrypt failed”, but here we must parse out cause.)

If a decryption fails unexpectedly (e.g. we see AccessDeniedException on a key we thought we could use), that is new evidence (maybe a policy issue) and we would mark HOLD.

7.1 Context and ciphertext are part of the evidence

Encryption context is authenticated data: if we supply the wrong context, KMS will fail with InvalidCiphertextException. In our tests, we consistently use the same context on encrypt and decrypt calls. As a negative test, one could try decrypting with a wrong context or omitting it; KMS would error. We record that error to show that a context mismatch is distinguished from a key mismatch. For example, decrypting with no context or a wrong Purpose= value yields InvalidCiphertextException. This tells us context is also “evidence” tied to ciphertext. For brevity we note this behavior but focus on matching contexts to avoid confounding our key-based decisions.

8. Re-encrypt one approved canary into a new artifact

Suppose we decide that Cohort A’s data should be available under Key B as well (for example, to migrate it). We explicitly re-encrypt just one canary from Cohort A to B. We use ReEncrypt:

aws kms reencrypt \
  --ciphertext-blob fileb://C_A.bin \
  --source-key-id \
    arn:aws:kms:us-west-2:111122223333:key/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee \
  --destination-key-id \
    arn:aws:kms:us-west-2:111122223333:key/11111111-2222-3333-4444-555555555555 \
  --source-encryption-context Purpose=Test \
  --destination-encryption-context Purpose=Test \
  --output json

Expected output (JSON):

{
  "CiphertextBlob": "<base64 C_A_to_B>",
  "KeyId":
    "arn:aws:kms:us-west-2:111122223333:key/11111111-2222-3333-4444-555555555555",
  "SourceKeyId":
    "arn:aws:kms:us-west-2:111122223333:key/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "DestinationKeyId":
    "arn:aws:kms:us-west-2:111122223333:key/11111111-2222-3333-4444-555555555555",
  "DestinationEncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}

We record:

  • CiphertextBlob (new ciphertext C_A_to_B.bin),

  • KeyId (equals DestinationKeyId, i.e. Key B’s ARN),

  • SourceKeyId (Key A ARN),

  • DestinationEncryptionAlgorithm, etc.

This confirms AWS decrypted with Key A and re-encrypted under Key B, as requested. We then do a sanity decrypt:

aws kms decrypt \
  --ciphertext-blob fileb://C_A_to_B.bin \
  --key-id arn:aws:kms:us-west-2:111122223333:key/11111111-2222-3333-4444-555555555555 \
  --encryption-context Purpose=Test \
  --output text --query Plaintext

It should output our original plaintext (via Key B). We verify it matches the oracle digest.

We never deleted or overwrote C_A.bin; we keep it as evidence of the original. Getting new ciphertext does not remove or modify the old blob. This illustrates you can fork ciphertext into a new key without destroying history.

9. Reconcile output bytes and application references

We now have two ciphertext blobs for the same plaintext (C_A and C_A_to_B), one under Key A and one under Key B. We proved plaintext equality. But applications or storage won’t magically switch. We must confirm whether any data store has been updated to use C_A_to_B instead of C_A.

In a real migration, an application would need to write C_A_to_B back to S3, database, or other systems. Our lab doesn’t have a real data repository, but we treat the question conceptually: “Has the system or its references been updated?” We look for evidence (e.g., an application config file). In this isolated test, we find none; all we did was call KMS. Thus, no consumer has been automatically “reconciled” to use C_A_to_B.

The output ledger:

Action

Ciphertext

Expected Key

Plaintext Match

Stored Artifact

Notes

Pre-change encrypt

C_A

Key A

Yes

Yes (kept)

Original canary encrypted.

Post-alias encrypt

C_B

Key B

Yes

No

New canary under alias B.

ReEncrypt A→B (explicit)

C_A_to_B

Key B

Yes

No

New ciphertext, stored only in experiment.

Alias retarget back to A

N/A

N/A

N/A

N/A

Alias now points to Key A again; no artifact change.

Because no application or storage reference was modified, Key A still “owns” C_A, and Key B “owns” C_B and C_A_to_B independently. The only way to reconcile is to manually update those references. This is the crux of migration: you must write and validate the new ciphertexts in place of the old ones. Simply changing an alias in KMS will not propagate to S3 or DBs.

We log that no database/S3 object was updated in this experiment; if this were a real migration, an engineer would need to schedule and verify each object rewrite (which is out of scope here).

10. Explain the limits of alias rollback

Finally, we demonstrate what happens if we roll back the alias to Key A.

aws kms update-alias \
  --alias-name alias/test-lab \
  --target-key-id \
    arn:aws:kms:us-west-2:111122223333:key/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee

Alias now points to Key A again. This is a simple routing change. We observe again that new encrypts via alias yield Key A, but existing ciphertexts C_B and C_A_to_B remain as they were. They do not get “reconverted” to Key A ciphertext. We log that C_B still exists encrypted by B, and C_A_to_B still encrypted by B. For example, trying to decrypt C_B with alias/test-lab (now A) will fail with a key mismatch. To use C_B, one would need to point alias to B or call Decrypt with B explicitly.

In short, rollback of the alias does not roll back the historical encryption. We end up with a mixed dependency: Key A is again the alias target, but the artifacts under Key B remain in storage. The inventory now has two keys with data dependencies: Key A has C_A, Key B has C_B and C_A_to_B.

10.1 Rollback of routing is not rollback of encrypted history

This shows the limitation: returning the alias to Key A does not eliminate the new artifacts. If an engineer thought “alias → B; oh that was wrong, let’s put it back to A and be done,” that would be incorrect: Key B still has valid ciphertext (C_B etc.) that is now orphaned. The data universe has split into Key A data and Key B data. This must be tracked carefully. We record both sets of ciphertexts and their paths in our ledger. Neither key should be disabled at this point. Deleting Key B would immediately orphan C_B and C_A_to_B and lose data. So retirement decisions are on hold.

This mixed-cohort state is why alias switch-over in production requires coordination: every consumer of the data must be updated or else Key B’s data will become inaccessible when you eventually close Key B.

11. Separate logical keys from key-material rotation

It’s worth contrasting alias retargeting with key rotation. Automatic rotation in KMS generates new key material for the same logical CMK identifier every year, but does not re-encrypt existing data. In other words, a “logical key” remains in place (its ARN stays the same) even though its internal key material changes. Existing ciphertexts remain decryptable under the retained old material of that same key. We must not confuse alias-update with rotation: alias-update introduces a different logical key (Key B) to replace Key A’s pointer, so it does not preserve the “same key” guarantee. Existing ciphertext under A will NOT automatically decrypt under B’s material.

Thus, thinking “alias change = new version of same key” is wrong. It is creating a separate key. Rotation, by contrast, means new material under the same key identity, so no data copy is needed. We do not perform rotation in this test (both keys are static CMKs). We mention this to avoid the false belief that alias-change is like enabling rotation.

12. Decide what the observed handoff permits

At this point we have collected evidence. How do we act? The decision categories are:

  1. ACCEPT the alias routing (for specific cohorts) if:

  • Cohort A ciphertexts decrypt successfully under Key A (our explicit tests) and we see no decryption failures there, and

  • 2.  Cohort B ciphertexts decrypt under Key B as expected, and

  • 3.  (Optional) The application has actually switched to Cohort B objects (if this were a real app scenario), or at least we have a strategy to write them.

In our experiment, we see Key A decrypt C_A, and Key B decrypt C_B; we consider that part of a successful transition. If we needed all data on B, we must generate and store C_A_to_B for every object. We only did one canary, so we’re only partially done. However, for the parts we’ve observed, the alias change is confirmed correct. We could ACCEPT the alias change for that test data, acknowledging that other data remains to be re-encrypted.

  1. WAIT: If our observations of the alias target were inconclusive or inconsistent. For example, if even after some time, alias-based encryption sometimes returned Key A and sometimes Key B beyond a reasonable window, we’d hold off on conclusions. Or if the list-aliases call did not yet show the update, we might wait longer. We set a bounded policy (say 1 minute) and if KMS still hasn’t converged, we might inspect logs or raise a HOLD.

  2. HOLD contradictory evidence. For example, if decrypting C_A with Key A unexpectedly failed with AccessDenied (meaning permissions are off), or if decrypting with no KeyId did not recover under A, or if ReEncrypt failed. These might indicate misconfiguration. Or if decrypting C_A resulted in some error other than the known IncorrectKeyException (maybe InvalidCiphertextException if context mismatched). Any such anomaly triggers a HOLD for further investigation.

  3. RE-ENCRYPT AND RECONCILE for any data cohort we’ve decided should use the new key but haven’t yet updated. In our lab, Cohort A’s one artifact was explicitly re-encrypted to B (C_A_to_B). If that had been a requirement (say we wanted all legacy data on Key B), we should do it for each object. Any data left as Cohort A we plan to move should be re-encrypted. The decision to actually do re-encryption depends on data ownership policies and downtime windows, but our evidence shows how to do it. We would then store those new ciphertexts and verify plaintext as we did.

Crucially, do not use this alias test as a signal to retire Key A. We have not proven all data under Key A is safe to drop. Some consumers might still need Key A’s encryption material (backups, caches, logs). Retiring a key is a separate operation requiring a full data dependency inventory. As cloud-security encryption and recovery practices warn, absence of evidence of use is not evidence of absence. Our canary approach is only a limited sample.

12.1 The evidence missing from a canary-only retirement claim

The only evidence we gathered is a few test ciphertexts. One cannot generalize to all objects. For example, if there were a database of encrypted rows, we have no proof those rows got re-encrypted. There might be caches or S3 objects we didn’t test. Without scanning every data store, we should HOLD any decision to fully decommission Key A. A prudent policy might be: leave Key A enabled but inactive until we have a process to reconcile each data location. Our demonstration has not covered keys in other accounts or regions, or any cross-service envelopes. In other words, we would not retire Key A solely because the alias changed. The alias test may help the cloud security team decide to plan re-encryption, but not to shut off Key A yet.

In summary, based on our canary evidence we conclude:

  • The alias update did route encryption requests to Key B as intended (ACCEPT for that controlled scenario).

  • Key A still legitimately decrypts its old ciphertext (expected), so we do not retroactively deny that (no forced retire).

  • We have created an explicit migrated copy (C_A_to_B), but in the real world we must do that for every object if needed (RE-ENCRYPT AND RECONCILE).

  • No inconsistent behavior was observed, so we don’t HOLD for further fixes, but we also WAITED until alias propagation finished before finalizing.

13. Assign ownership to the remaining key dependencies

After this exercise, who is responsible for what? We define roles:

  • Key Administrator: Owns Key A and Key B resources, including key policies and IAM. Ensures these keys remain active and aren’t deleted until all data is migrated.

  • Application/Data Owner: Owns the data encrypted under each key. If some application is still producing or consuming alias/test-lab, they must update to the new ciphertext or explicitly acknowledge they still need Key A.

  • Evidence Custodian: Keeps the logs and ledger (or CI/CD report) of this procedure for auditing and incident response. This includes the plaintext or its digest, ciphertext digests, and API call records with request IDs.

  • Migration Backlog: Any data objects not covered by the canary are added to a backlog. Each item requires either re-encryption or a decision to maintain Key A. The backlog must list object IDs, current key (A or B), and owner.

We use the internal policy: “no key retirement without owner sign-off and documented proof”. For example, if an S3 object is still encrypted with Key A, the S3 owner must endorse migrating it or accepting Key A’s continued use. The Key Admin will hold Key A while this backlog is cleared. This inventory makes sure that after alias change, no resource is inadvertently left unlocked or orphaned.

Lastly, AWS Organizations or configuration tools could enforce that no CMK is inadvertently scheduled for deletion if it’s referenced by resource metadata (see AWS guidance on guarding against in-use deletions). Our lab only scoped this alias; a full-scale migration would integrate with such governance.

14. Build cloud-security review skills with Refonte Learning

Understanding these nuanced behaviors is exactly the kind of hands-on skill the Cloud Security Engineer Essentials program at Refonte Learning develops. The program (3 months, ~10–12 hours/week) covers practical competencies like IAM, data encryption techniques, cloud compliance, incident response, and logging/monitoring. In particular, participants learn how to audit encryption key use and design safe rollout plans, much like we did here with KMS keys and aliases.

By following guided labs and expert mentorship, you can build confidence in tracing your cloud data’s cryptographic provenance and governance. If you’re committed to mastering real-world cloud security (beyond just theory), consider applying to the Refonte Cloud Security Engineer Essentials program. It emphasizes exactly this kind of evidence-based approach to key and data handling, helping ensure future cloud projects are reviewed with the rigor shown above.