Skip to content
1Claw Academy
Curriculum/Security Foundations5 minBeginner · Lesson 3 of 19

Lab: build envelope encryption by hand

Lab

Do the KEK and DEK dance yourself with openssl, then destroy a key and watch the data become unreadable.

The previous lesson described envelope encryption. Ten minutes with openssl makes it concrete, and nothing here needs an account, a network, or 1Claw. Everything happens in a scratch directory you delete afterwards.

Concept

You need openssl, which ships with macOS and every Linux distribution. Run openssl version to confirm.

  1. 1

    Make a scratch directory so nothing lands in a real project.

    bash
    mkdir -p /tmp/envlab && cd /tmp/envlab
  2. 2

    Generate the key encryption key. In production this lives inside an HSM and never comes out; here it is a file, which is exactly the weakness the HSM removes.

    bash
    openssl rand -hex 32 > kek.hex
  3. 3

    Generate a data encryption key for one secret. Every secret gets its own, which is what limits the blast radius of a single compromise.

    bash
    openssl rand -hex 32 > dek.hex
  4. 4

    Encrypt the secret under the DEK. Save the IV: it is not sensitive, but without it the ciphertext is undecryptable, which is a very common way to lose data.

    bash
    printf 'sk-live-abc123' > secret.txt
    openssl rand -hex 16 > iv.hex
    openssl enc -aes-256-cbc \
      -K "$(cat dek.hex)" -iv "$(cat iv.hex)" \
      -in secret.txt -out secret.enc
  5. 5

    Wrap the DEK under the KEK. This is the envelope: the file you store next to the ciphertext is the encrypted key, never the key itself.

    bash
    openssl rand -hex 16 > wiv.hex
    openssl enc -aes-256-cbc \
      -K "$(cat kek.hex)" -iv "$(cat wiv.hex)" \
      -in dek.hex -out dek.wrapped
  6. 6

    Look at what a server would actually hold. There is no plaintext key anywhere in this list.

    bash
    ls -l secret.enc dek.wrapped iv.hex wiv.hex
  7. 7

    Read the secret back the way the vault does: unwrap the DEK with the KEK, then decrypt with the DEK.

    bash
    openssl enc -d -aes-256-cbc \
      -K "$(cat kek.hex)" -iv "$(cat wiv.hex)" \
      -in dek.wrapped -out dek.recovered
    
    openssl enc -d -aes-256-cbc \
      -K "$(cat dek.recovered)" -iv "$(cat iv.hex)" \
      -in secret.enc
    # -> sk-live-abc123
  8. 8

    Before erasing anything, rotate the KEK. Take a fingerprint of the ciphertext, unwrap the DEK with the old KEK, generate a new KEK, and re-wrap the same DEK under it.

    bash
    shasum -a 256 secret.enc
    
    openssl enc -d -aes-256-cbc \
      -K "$(cat kek.hex)" -iv "$(cat wiv.hex)" \
      -in dek.wrapped -out dek.plain
    
    openssl rand -hex 32 > kek2.hex
    openssl enc -aes-256-cbc \
      -K "$(cat kek2.hex)" -iv "$(cat wiv.hex)" \
      -in dek.plain -out dek.wrapped2
  9. 9

    Now fingerprint the ciphertext again, and compare the sizes of what rotation actually rewrote.

    bash
    shasum -a 256 secret.enc
    wc -c secret.enc dek.wrapped2
  10. 10

    Identical hash. You rotated the root of the hierarchy and the encrypted data was never opened, read or rewritten: rotation touched 80 bytes of wrapped key and left the ciphertext alone. On a real vault that is the difference between re-encrypting a small key and re-encrypting a database.

    text
    <hash>  secret.enc     (before)
    <hash>  secret.enc     (after, identical)
    
          16 secret.enc
          80 dek.wrapped2
          96 total
  11. 11

    Now do cryptographic erasure. Delete only the wrapped DEK and try again.

    bash
    rm dek.wrapped dek.recovered
    
    openssl enc -d -aes-256-cbc \
      -K "$(cat kek.hex)" -iv "$(cat wiv.hex)" \
      -in dek.wrapped -out dek.recovered
    # -> Can't open "dek.wrapped" for reading, No such file or directory
  12. 12

    The ciphertext is untouched and permanently unreadable. Clean up.

    bash
    cd /tmp && rm -rf envlab

Three properties you just demonstrated rather than read about. The KEK never touched the secret, only the DEK. Rotating the KEK would mean re-encrypting one small wrapped key, not the data. And deleting a key erased the data instantly, without overwriting a single byte of it.

Tip

Real implementations use AES-GCM rather than CBC, because GCM authenticates as well as encrypts, so tampering is detected instead of producing garbage plaintext. CBC is used here only because it needs fewer flags to demonstrate.

Watch out

You just held a KEK in a file, which is the thing the next lesson exists to fix. Everything above is correct and still weak, because the root of the hierarchy sat on disk where any process could read it.

Check your understanding

3 questions
1

You deleted the wrapped DEK but kept the ciphertext. Why is the data unrecoverable?

2

Why does each secret get its own DEK rather than sharing one?

3

The IV was stored in plaintext next to the ciphertext. Is that a problem?