Skip to content
back to the map
technique
stage: proteger
4 min read

KMS & crypto - keys and envelopes

Cloud encryption starts at the KMS: whoever has access to the key decides what is 'encrypted'. Without governing keys, 'encrypted at rest' is just a checkbox in the console.

AWS KMSHashiCorp Vault TransitGoogle Cloud KMSAzure Key Vault

What it is

KMS () is the component that creates, stores, uses and revokes cryptographic keys without exposing the secret to your code. The application asks the KMS to 'encrypt this' or 'decrypt this', and the key never leaves the key service. The rest of the system uses the ciphertext.

This detail matters because most encryption failures happen on the wrong plane: keys in environment variables, keys committed to Git, keys in config files. Taking the key out of the application's path is what the KMS does.

Why 'encrypted at rest' is only the beginning

Storage services already come with encryption at rest enabled by default (S3 SSE, EBS encryption, RDS storage encryption). This protects the disk against physical removal, but not against whoever has API access: someone with s3:GetObject reads the encrypted object and asks the KMS to decrypt it.

If the key policy allows overly broad use, any identity that already has read permission gets effective access to the plaintext. The right question is not 'is it encrypted?', but 'who can ask the KMS to decrypt this?'.

Envelope encryption: key protects key

Sending an entire large file to the KMS on every encryption is expensive and slow. The standard solution is : the KMS generates a symmetric data key, the payload is encrypted locally with AES, and the data key is encrypted with the KMS master key. The result travels with the data.

In the flow below, the client receives the plaintext and ciphertext of the data key, encrypts the document with the local key, and discards the plaintext. Only the data key's ciphertext stays persisted next to the file.

envelope.py - conceptual examplepython
import boto3
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

kms = boto3.client("kms")
KEY_ID = "arn:aws:kms:us-east-1:111122223333:key/abcd1234-..."

def envelope_encrypt(plaintext: bytes, kms_key_id: str = KEY_ID):
    # 1. KMS returns a plaintext data key + the same key encrypted
    resp = kms.generate_data_key(KeyId=kms_key_id, KeySpec="AES_256")
    plaintext_key = resp["Plaintext"]
    encrypted_key = resp["CiphertextBlob"]

    # 2. encrypts the payload locally, never sending it to the KMS
    nonce = b"\x00" * 12
    ciphertext_body = AESGCM(plaintext_key).encrypt(nonce, plaintext, None)

    # 3. discards the data key plaintext, only ciphertext travels with the file
    del plaintext_key
    return {"encrypted_key": encrypted_key, "ciphertext_body": ciphertext_body, "nonce": nonce}

Key policy: who uses the key

The key policy is the document that governs a KMS key. It decides who can use the key, for which operations and under which conditions. The default for AWS-generated keys is liberal: the root account can do everything, but if nobody restricts it, any principal with kms:* across the whole environment gets through.

A good key policy separates administrative use (create, rotate, delete) from cryptographic use (encrypt, decrypt). The application team only gets the second, via grants limited to a bucket or table ARN pattern.

key-policy.json - separated by rolejson
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AdminsManageKey",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:role/SecTeam" },
      "Action": [
        "kms:Create*", "kms:Describe*", "kms:Enable*", "kms:Disable*",
        "kms:ScheduleKeyDeletion", "kms:RotateKeyOnDemand"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AppEncryptsOnly",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:role/app-frontend" },
      "Action": [
        "kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey", "kms:DescribeKey"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "kms:ViaService": "s3.us-east-1.amazonaws.com"
        }
      }
    }
  ]
}

Rotation and revocation

Automatic annual rotation is enabled by default on KMS keys and costs almost nothing to use: the old key remains valid for decrypting old data, and the new one encrypts the next. When you need to, you force manual rotation after suspected misuse.

Revocation is different: you either disable the key (blocks decryption) or schedule its deletion. Blocking the key breaks legitimate applications that try to read old data. Hence the practical rule: monitor usage, separate keys by sensitivity, and never mix a production key with a test key on the same resource.

Where it fits in day-to-day work

  • standard storage: S3, EBS, RDS, DynamoDB and similar already encrypt with KMS, but the key policy must say who can call decryption
  • envelope encryption: sensitive payloads in a queue or blob go through the local key + encrypted data key pattern
  • BYOK/HYOK: regulated companies import their own key (Bring Your Own Key) or keep the key in an external HSM (Hold Your Own Key)
  • auditing: every GenerateDataKey, Encrypt or Decrypt call becomes a CloudTrail event with principal ARN and

Strengths

  • the key never touches the application: even an attacker with RCE on the server cannot extract the key from the KMS
  • centralized rotation: rotating in one place migrates the whole tree of data keys in use
  • native : every use is signed and logged with identity, resource and timestamp

Limitations

  • per-call cost: KMS charges per operation, and a badly designed envelope turns into a heavy bill per request
  • latency per hop: each Encrypt/Decrypt adds a trip to the KMS, and latency-sensitive paths need a locally cached key
  • BYOK/HYOK adds operational complexity: HSMs in two regions, manual rotation, and separate disaster recovery procedures
KMSkey management serviceA managed service that creates, stores and controls the use of cryptographic keys. The application rarely touches the master key: it asks the KMS to encrypt or decrypt, and the provider records every use in an auditable log. Without a KMS, the key ends up on disk and becomes a target.see in glossary →Envelope encryptionA two-layer pattern: a data key (DEK) encrypts the payload, and a KMS key (KEK) encrypts the DEK. The application stores the encrypted DEK alongside the data and only calls the KMS to unwrap the DEK when it needs to use it. It reduces KMS calls and limits exposure of the master key.see in glossary →IAMidentity and access managementThe set of policies, roles and credentials that says who every human, service or workload is, and what each one can do. In the cloud, IAM became the new perimeter: the network edge got porous, so identity carries the authorization.see in glossary →Correlation idrequest idA unique id attached to a request and to every log line it creates, shown to the user in error messages so support can find the details without leaking them.see in glossary →Audit trailcloudtrailAn immutable record of every API call made in the cloud account: who called what, from where, with what result. It is the account's black box: after a leak, it answers 'what did the attacker do, and when?'. Without a trail, there is no investigation or informed containment.see in glossary →

connections on the map

updated on 14/10/2026 · sources to go deeper · report an error

in other domains

Study before

Unlocks

test what you learned

Loading questions…