Chapter 14

Cloud KMS and Cryptographic Controls

Scope. This chapter covers Cloud KMS end to end: key rings, keys, key versions, symmetric and asymmetric operations, signing, protection levels (software, HSM, external), customer-managed and customer-supplied encryption keys, rotation, key separation, envelope encryption, crypto-shredding, and destruction. It is the single owner of cryptographic control material for this book; every other chapter that wires a service to a key cross-references it rather than repeating it. Prerequisites. §2.21 Security Projects (key placement), §2.30 Organization Policy, §3.9 IAM Roles, §3.17 Deny Policies. Verified against. GCP console and API surface as of 2026-09, Google Cloud SDK 583.0.0; see sources at end.

Every other chapter in this book that touches encryption at rest ends the same way: a --kms-key flag, a kms_key_name Terraform argument, and a grant to a service agent. None of them explain what the key actually protects, what happens when it rotates, or what it takes to make destroying it mean something. That material lives here, once, so that Chapters 8 through 13 and 15 can cite a single page instead of restating it with small, dangerous variations.

Cloud KMS is Google Cloud's managed service for creating and controlling cryptographic keys without handling key material directly. It backs three related but distinct controls covered in this chapter: customer-managed encryption keys (CMEK), where Google's infrastructure still performs the cryptographic operation but under a key you administer; customer-supplied encryption keys (CSEK), where you supply key material per request and Google never persists it; and External Key Manager (EKM), where the key never leaves a system you control at all. Each trades operational simplicity for a different degree of control, and §14.15 gives the decision between them.

A Cloud KMS key is not a secret. Secret Manager (§13.1) stores and serves opaque values — passwords, API tokens, TLS private keys — that an application reads and uses directly. A Cloud KMS key never leaves the service: callers send plaintext or ciphertext and get back the other half, and the key material itself is never returned by any API. If your workload needs to read a value in the clear, it belongs in Secret Manager; if it needs to prove control over a resource by encrypting or signing without ever holding the key, it belongs in Cloud KMS.

14.1 Cloud KMS §

Cloud KMS is the managed keystore for symmetric encryption, asymmetric encryption, asymmetric signing, and MAC keys, reached through gcloud kms, the cloudkms.googleapis.com REST API, and the google_kms_* Terraform resources. It underlies CMEK for every service listed in §14.8, and every per-service CMEK example elsewhere in this book eventually calls it.

The defining property is that key material never leaves the service. You send Cloud KMS data to encrypt, or a digest to sign, and it returns the result — you never hold the key. That is the line between Cloud KMS and Secret Manager (§13.1): Secret Manager gives you back the thing you stored; Cloud KMS never does. A credential your application must read is a secret. A key your application must use but must never possess is a KMS key. Storing key material as a Secret Manager secret discards the entire guarantee.

The hierarchy is fixed and every level is a location decision:

project → location → key ring → key → key version (the actual material)

Administration and use are separate role families by design (§14.13, and the principle in Chapter 3):

FamilyRolesHeld by
Administrationroles/cloudkms.admingcp-security-admins@rickcollette.domain only
Useroles/cloudkms.cryptoKeyEncrypterDecrypter, roles/cloudkms.cryptoKeyEncrypter, roles/cloudkms.cryptoKeyDecrypter, roles/cloudkms.signerWorkloads and service agents, on one key
Auditroles/cloudkms.viewergcp-security-viewers@rickcollette.domain

The group that administers a key ring never holds a use role on the keys inside it, and no workload or service agent ever holds roles/cloudkms.admin. All key rings in this book's SaaS estate live in rc-saas-shared-sec-01; §2.21 owns why keys are never colocated with the workloads they protect.

14.2 Key Rings §

A key ring is a named, regional grouping of keys and nothing else — no configuration, no policy beyond IAM, no lifecycle state. It has exactly two properties, and both are permanent for its entire existence: its location, chosen at creation, and its name.

"Key rings can't be deleted."

Neither can keys. And the name of a deleted key cannot be reused inside its ring. A key ring name is therefore a permanent decision in a project you cannot tidy up, which has one practical consequence worth internalizing: a naming experiment in a production security project is forever, and so is the clutter from one.

This book's convention: regional key rings, one per region per purpose, named kr-<region>-<purpose>, in rc-saas-shared-sec-01.

gcloud kms keyrings create kr-us-central1-data \
    --location=us-central1 \
    --project=rc-saas-shared-sec-01

Never a global key ring for data-at-rest CMEK. The key belongs in the same region as the storage it protects: a global ring buys no locality and couples every consumer in every region to one dependency, so a control-plane problem in the ring's location becomes an availability problem for data that had nothing to do with it.

Pitfall. Because key rings and keys are undeletable, a test key ring created in the security project during an evaluation stays visible in every inventory and every audit forever, and cannot be cleaned up. Do key-management experiments in a sandbox project you can delete outright, never in the real security project.

14.3 Keys §

A key is a named, purpose-typed container for key versions. gcloud kms keys create requires --keyring, --location, and --purpose.

Purpose is fixed for the life of the key — a symmetric encryption key can never later hold a signing version — so it is the one creation-time choice you cannot correct:

--purposeUsed for
encryptionSymmetric encrypt/decrypt, and every CMEK integration (§14.8)
asymmetric-encryptionRSA key pairs where a third party encrypts to you (§14.6)
asymmetric-signingDetached signatures over a digest (§14.7)
macMessage authentication codes
raw-encryption, aes-wrapping, key-encapsulationSpecialized wrapping and KEM uses
gcloud kms keys create k-storage-uploads \
    --keyring=kr-us-central1-data \
    --location=us-central1 \
    --purpose=encryption \
    --rotation-period=90d \
    --next-rotation-time=2026-12-01T00:00:00Z \
    --project=rc-saas-shared-sec-01

This book names keys k-<service>-<purpose>k-storage-uploads, k-sql-prod.

Cloud KMS Autokey provisions a key ring and key automatically on first use. It is generally available, and it now comes in two storage shapes, renamed in 2026: Autokey with dedicated-project key storage (formerly "centralized key management") puts the keys in a separate key project, which is the arrangement this book uses because it keeps key administration in the security project (§2.21); and Autokey with same-project key storage (formerly "delegated key management"), which the release notes record as generally available on 2026-07-29 and which can be used on its own or alongside the dedicated-project form.

Pitfall. --rotation-period without --next-rotation-time starts the clock from creation, which is rarely the schedule you intended and produces rotations at an arbitrary hour. Set both, and read §14.12 first — rotation does considerably less than its name suggests.

14.4 Key Versions §

The key version, not the key, holds the actual cryptographic material. A key holds many versions; exactly one is the primary version, used by default for new encrypt and sign operations.

StateMeaning
ENABLEDUsable; the primary version is one of these
DISABLEDBlocked immediately, and reversibly
Scheduled for destructionIn the waiting period, still restorable (§14.18)
DESTROYEDMaterial gone, irreversibly

Every ciphertext is bound to the version that produced it, and that version must stay enabled to decrypt it. This single fact drives two later sections: rotation (§14.12) never removes an old version while ciphertext under it still exists, and crypto-shredding (§14.17) targets a specific version, never the key as a whole.

gcloud kms keys versions list \
    --key=k-storage-uploads --keyring=kr-us-central1-data \
    --location=us-central1 --project=rc-saas-shared-sec-01

Pitfall. Disabling a version to "test whether anything still uses it" breaks decryption of every ciphertext produced under it, instantly and across every consumer. It is reversible, but the outage is real. Determine what a version still protects from audit logs (§14.19) before touching its state, not by experiment.

14.5 Symmetric Encryption §

gcloud kms encrypt and gcloud kms decrypt call the AEAD symmetric primitive directly, with an optional --additional-authenticated-data-file that binds context: the AAD is verified but not encrypted, so a ciphertext encrypted for tenant A fails to decrypt if presented as tenant B's.

The plaintext limit is hard, and it is the reason envelope encryption exists:

"The plaintext file must not be larger than 64KiB."

Cloud KMS is a key-management API, not a bulk-data encryption service. Anything larger goes through envelope encryption (§14.16) — encrypt the data locally with a data encryption key, then send only that key to Cloud KMS to be wrapped.

gcloud kms encrypt \
    --key=k-storage-uploads --keyring=kr-us-central1-data \
    --location=us-central1 --project=rc-saas-shared-sec-01 \
    --plaintext-file=config.yaml --ciphertext-file=config.yaml.enc

Pitfall. A caller holding roles/cloudkms.cryptoKeyEncrypterDecrypter can decrypt every ciphertext ever produced under that key, including ones it never created. CMEK does not protect data from a compromised principal that can call decrypt — it protects against loss of administrative control over the key. Say that plainly in threat models rather than letting "encrypted with CMEK" stand in for an access control.

14.6 Asymmetric Encryption §

An asymmetric-encryption key holds an RSA key pair, and only the private half ever exists inside Cloud KMS. Callers fetch the public key and encrypt locally; only decryption calls the service and requires roles/cloudkms.cryptoKeyDecrypter. Supported algorithms are the rsa-decrypt-oaep-* family across 2048-, 3072-, and 4096-bit moduli, each paired with a SHA digest.

gcloud kms keys versions get-public-key 1 \
    --key=k-partner-inbound --keyring=kr-us-central1-data \
    --location=us-central1 --project=rc-saas-shared-sec-01 \
    --output-file=partner-inbound-pub.pem

Use it when the encrypting party must not be trusted with decryption. A partner system that sends you data needs only your public key — and therefore needs no Google Cloud identity and no IAM grant at all, which is the entire point. That is a materially smaller integration surface than granting an external party any role.

For everything else, prefer symmetric encryption (§14.5) or envelope encryption (§14.16): asymmetric operations are markedly slower and are capped at small payloads by the RSA modulus.

Pitfall. RSA-OAEP payload capacity is a few hundred bytes, not a few kilobytes, and shrinks as the digest grows. A design that "encrypts the record with the partner's public key" works in testing on a short record and fails in production on a long one. Encrypt a data encryption key with RSA and the record with that key (§14.16).

14.7 Signing §

An asymmetric-signing key produces a detached signature over a digest computed locally, never over the file itself — which is why signing a multi-gigabyte artifact costs one small API call.

gcloud kms asymmetric-sign \
    --key=k-artifact-signing --keyring=kr-us-central1-data \
    --location=us-central1 --project=rc-saas-shared-sec-01 \
    --version=1 --digest-algorithm=sha256 \
    --input-file=release.tar.gz.sha256 --signature-file=release.tar.gz.sig

Verification never calls Cloud KMS. Fetch the public key once and verify locally, so verifiers — including every consumer outside your organization — need no KMS grant. This is what makes KMS-backed signing workable as a supply-chain control (Chapter 37): the signing capability is tightly held, and the verifying capability is free and universal.

Grant the narrowest of the three roles. A build pipeline that signs needs roles/cloudkms.signer alone, not roles/cloudkms.signerVerifier and not roles/cloudkms.verifier.

Prefer elliptic-curve algorithms (ec-sign-p256-sha256, ec-sign-p384-sha384, ec-sign-ed25519) for new signing keys: smaller signatures at lower cost for the same security margin, unless a consumer requires RSA compatibility.

Pitfall. The signature covers the digest you supplied, and Cloud KMS cannot tell whether that digest is of the artifact you think it is. A pipeline that computes the digest from a mutable location, or signs before a final build step rewrites the artifact, produces a valid signature over the wrong bytes — and it verifies cleanly forever.

14.8 Customer-Managed Encryption Keys §

CMEK is the pattern behind every --kms-key flag and kms_key_name argument in this book. The service performs the encryption — it still calls Cloud KMS's symmetric encrypt operation on your behalf, and your data still passes through Google's infrastructure in the clear during processing — but the key that operation uses is one you created, administer, and can revoke or destroy independently of the service and the data it protects. That independence, not secrecy of the key from Google, is what CMEK buys: without it, Google Default Encryption already encrypts every byte at rest, but you have no lever to cut a specific resource off from its own ciphertext.

Every CMEK-consuming service performs encrypt and decrypt calls through a Google-managed service agent, not through the workload's own service account, and that agent needs roles/cloudkms.cryptoKeyEncrypterDecrypter granted on the specific key, never on the key ring or project. Grant it once per service, and never grant the agent roles/cloudkms.admin — administration of the key stays with gcp-security-admins@rickcollette.domain, use stays with the service agent, per §3.9's separation of administration from use.

ServiceService agent address form
Compute Engine (disks)service-PROJECT_NUMBER@compute-system.iam.gserviceaccount.com
GKE (Kubernetes Engine)service-PROJECT_NUMBER@compute-system.iam.gserviceaccount.com
Cloud SQLservice-PROJECT_NUMBER@gcp-sa-cloud-sql.iam.gserviceaccount.com
BigQuerybq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com
Pub/Subservice-PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com
Secret Managerservice-PROJECT_NUMBER@gcp-sa-secretmanager.iam.gserviceaccount.com
Cloud Storageretrieved dynamically per bucket; see §11.19

PROJECT_NUMBER is the numeric project number of the project that owns the resource, not the security project that owns the key — a Cloud SQL instance in rc-saas-prod-data-01 grants its own project's service agent on a key living in rc-saas-shared-sec-01. The grant is a single cross-project IAM binding on the key resource:

gcloud kms keys add-iam-policy-binding k-sql-prod \
    --keyring=kr-us-central1-data --location=us-central1 \
    --project=rc-saas-shared-sec-01 \
    --member="serviceAccount:service-135792468@gcp-sa-cloud-sql.iam.gserviceaccount.com" \
    --role="roles/cloudkms.cryptoKeyEncrypterDecrypter"

Pitfall. CMEK does not protect against a principal that already holds roles/cloudkms.cryptoKeyEncrypterDecrypter and legitimate access to the encrypted resource — that principal can read the plaintext exactly as it could under Google Default Encryption. CMEK's value is administrative: the ability to sever access by disabling or destroying the key, and the audit trail of every cryptographic operation, neither of which Google Default Encryption offers. constraints/gcp.restrictNonCmekServices (§2.30) enforces that specified services may not create resources without a customer-managed key at all, and constraints/gcp.restrictCmekCryptoKeyProjects restricts which projects may host the keys those services are allowed to use — set it to allow only rc-saas-shared-sec-01.

14.9 Customer-Supplied Encryption Keys §

CSEK inverts CMEK's trust model: you supply raw key material with every API request, and Google never stores it.

"Google does not store your keys on its servers and cannot access your protected data unless you provide the key."

For Cloud Storage the guarantee is stated more narrowly still — Google keeps only a hash, to validate future requests:

"Cloud Storage stores only a cryptographic hash of the key so that future requests can be validated against the hash."

Only two services accept a customer-supplied key: Compute Engine, for persistent disk encryption, and Cloud Storage, for object encryption. Nothing else in this book's scope does.

Losing the key permanently loses the data. There is no recovery path, no support escalation, and no version history to fall back on, because Google never had a copy. That is not a caveat to the model — it is the model, and it is the reason CSEK belongs only to estates that already run a tested external key lifecycle. For everyone else, CMEK's administered-but-recoverable model (§14.8) is the safer default with nearly all of the control.

Pitfall. CSEK's key must be supplied on every subsequent operation, including ones a team does not think of as operations on the data — creating a snapshot of a CSEK-encrypted disk, creating an image from it, or attaching it to a new instance. An automated backup job that does not carry the key silently stops producing usable backups.

14.10 Cloud HSM §

--protection-level=hsm performs every cryptographic operation for that key inside a hardware security module rather than in software. The certification is exact, and must not be paraphrased:

"FIPS 140-2 Level 3"

gcloud kms keys create k-payments-pan \
    --keyring=kr-us-central1-data --location=us-central1 \
    --purpose=encryption --protection-level=hsm \
    --project=rc-saas-shared-sec-01

Choose HSM when a framework or contract names it, not as a general upgrade. Software protection already meets most estates' bar; HSM costs more per key version and per operation, and buys nothing an auditor will credit unless the requirement specifically calls for hardware-backed key storage. PCI DSS and several government frameworks do name it.

constraints/cloudkms.allowedProtectionLevels (§2.30) enforces hsm across a regulated folder, which is the right way to apply it — as an inherited property of where a project sits, not a per-key decision someone has to remember.

Pitfall. Protection level is fixed at key creation and cannot be raised later. Discovering during an audit that a key holding regulated data is software-protected means creating a new HSM key and re-encrypting everything under it — rotation (§14.12) will not do this, because rotation never re-encrypts.

14.11 External Key Manager §

EKM moves the key entirely outside Google's infrastructure. Cloud KMS holds only a reference and calls an external manager — Fortanix, Futurex, and Thales are the supported partners — for every operation, over the internet (--protection-level=external) or a private VPC path (--protection-level=external-vpc). Chapter 20 covers the network posture for the VPC variant.

The trade-off is availability, not confidentiality:

"Communicating with an external service over the internet can lead to problems with reliability, availability, and latency."

An unreachable external key manager makes the data unavailable, not slow. Every operation returns FAILED_PRECONDITION until connectivity returns, and the downstream consequences can be permanent: Spanner automatically deletes databases whose encryption key stays unavailable for more than 30 consecutive days.

Adopt EKM only where a regulation or contract requires that the key never reside in Google's infrastructure at all. For every other estate, CMEK (§14.8) delivers equivalent administrative control — you can revoke and destroy — without making an external system a single point of failure for data availability. This is a case where the stronger-sounding control is the wrong default.

Pitfall. The external key manager becomes a tier-0 dependency of every service encrypting under it, and it is usually run by a team that does not know that. Its maintenance windows, its certificate expiries, and its own availability target now bound your data plane. Confirm the operating agreement before the architecture decision, not after.

14.12 Key Rotation §

gcloud kms keys set-rotation-schedule or keys create --rotation-period schedules automatic rotation, and --next-rotation-time sets the first occurrence. Rotation on schedule performs exactly one action:

"The schedule automatically creates a new primary version for the key according to the --next-rotation-time and --rotation-period flags."

Critically, that is all it does:

"Rotating keys creates new active key versions, but doesn't re-encrypt your data and doesn't disable or delete previous key versions."

New encrypt calls use the new primary version; every ciphertext already produced under the previous version still needs that version enabled to decrypt. This is why rotation is not a remediation for a compromised key — a rotated key still has an enabled version that a holder of the old key material (or a principal who retained decrypt access to it) can use against already-encrypted data. Destruction, in §14.17 and §14.18, is the actual remediation; rotation only limits how much future ciphertext a given version protects.

gcloud kms keys set-rotation-schedule k-storage-uploads \
    --keyring=kr-us-central1-data --location=us-central1 \
    --project=rc-saas-shared-sec-01 \
    --rotation-period=90d --next-rotation-time=2026-12-01T00:00:00Z

Every enabled version of a rotated key is a billed resource for as long as it stays enabled; only a destroyed version stops billing (§14.18). A key rotated quarterly for years and never pruned accumulates dozens of billed, unused versions — disable versions no longer needed to decrypt live data once you have confirmed nothing still depends on them.

14.13 Key Separation §

§2.21 settled where keys live: a dedicated security project, administered separately, with destruction delayed and alerted. What it does not settle is how many keys to create and along which axes — and that choice, not the placement, determines the blast radius when one key is compromised or destroyed.

AxisSeparate so that…Bounds
EnvironmentA production key cannot decrypt staging ciphertext, and retiring an environment cannot touch a live oneA leaked credential
Data classRegulated payment data and application logs never share a keyA compliance scope
TenantOne tenant's data can be erased without touching another'sAn erasure request
RegionA regional key never becomes a dependency for a workload elsewhereA jurisdictional or availability incident

Tenant separation is the one that is impossible to retrofit. Crypto-shredding (§14.17) erases a tenant only if that tenant's data is under a key no other tenant shares — a shared key makes destruction all-or-nothing across everyone it protects. A multi-tenant estate that starts with one key and later accepts a contractual erasure obligation must re-encrypt every tenant's data under a new per-tenant key to meet it, and rotation will not do that (§14.12). Decide the tenant axis before the first production tenant, not at the first deletion request.

Each axis answers a different question when something goes wrong. A single key used across all four answers none of them.

Pitfall. The opposite failure is real too. A key per tenant per environment per data class per region multiplies into thousands of undeletable keys (§14.2), each with its own rotation schedule, IAM bindings, and billing line — and an inventory nobody can audit is its own control failure. Separate along the axes you have an actual obligation to bound, and no further.

14.14 Key Access Justifications §

Key Access Justifications (KAJ) attaches a justification code to every cryptographic operation Google's systems perform against an enrolled key, so that access can be programmatically approved or denied based on why it is being requested rather than only who is requesting it. It works alongside Access Approval and Access Transparency (§3.29§3.30) to make Google's own access to CMEK-protected data visible and, where policy allows, conditional. --allowed-access-reasons on gcloud kms keys create or keys update enrolls a key, accepting values such as customer-initiated-access, google-initiated-review, google-initiated-system-operation, and third-party-data-request.

KAJ can only be used inside an Assured Workloads folder, and its production readiness is per-service, not global: Google states plainly that a service's Preview-status integration must not be used in production, and that reliable justification generation requires every service involved in a request to have reached GA status for KAJ specifically.

No list of KAJ-supported services appears in this book. The set is per-service, changes as integrations reach GA, and is gated behind Assured Workloads, so any list printed here would be a snapshot with no expiry date on it. Read the current one from the Key Access Justifications documentation for the regime you are in.

Treat any service not confirmed GA for KAJ as a Preview dependency: do not build a production control around it, and re-verify the service's KAJ status before relying on it.

gcloud kms keys create k-regulated-data \
    --keyring=kr-us-central1-data --location=us-central1 \
    --purpose=encryption --project=rc-saas-shared-sec-01 \
    --allowed-access-reasons=customer-initiated-access,google-initiated-review

14.15 Encryption Architecture §

Three layers of at-rest encryption control are available, in increasing order of administrative effort and decreasing order of Google's operational involvement.

Google Default Encryption requires no configuration: every object, disk, and database in Google Cloud is encrypted at rest with Google-managed keys the moment it is written. It protects against physical media theft and against another customer's workload ever reaching your bytes. It gives you no lever over the key at all — no rotation you control, no destruction you can trigger, no audit trail of key use distinct from the resource's own access logs.

CMEK (§14.8) adds a key you administer in front of that same infrastructure-performed encryption. It protects against loss of administrative control over data: you can revoke a service agent's grant or destroy a key version to make a resource's ciphertext permanently unreadable, independent of whoever still holds access to the resource itself. It does not protect against a principal that already holds both resource access and decrypt access on the key — that principal's read path is unchanged.

CSEK and EKM (§14.9, §14.11) go further: the key material is never held by Google at all, either because you supply it per-request and it is discarded after use (CSEK) or because it lives permanently in an external system Cloud KMS only calls out to (EKM). Both trade Google's operational resilience for a stronger control guarantee, and both fail in the same direction — losing the key, or losing reachability to it, makes the data permanently or temporarily unavailable rather than merely less protected.

LayerYou controlFails asTypical driver
Google Defaultnothingnever (Google-managed)default; no action required
CMEKkey lifecycle, IAM, destructionunavailable if key destroyed/revokedindependent revocation, audit
CSEKkey material per requestunrecoverable if key lostCompute Engine, Storage only
EKMkey location and reachabilityunavailable if unreachablekey must never leave your control

Default to CMEK for anything that needs an administrative kill switch — which in this book's estate means everything landing in a security-boundary project. Reserve CSEK and EKM for a specific, named requirement that CMEK cannot satisfy, because both convert a key-management problem into an availability dependency the workload owner must now operate.

14.16 Envelope Encryption §

Envelope encryption is how every application in this book encrypts data larger than the 64 KiB direct-encrypt limit (§14.5), and how tokenization protects its wrapped key (§15.5).

The pattern, in four steps:

  1. Generate a data encryption key (DEK) locally.
  2. Encrypt the payload with the DEK using a fast local AEAD cipher — the payload never touches Cloud KMS.
  3. Send only the small DEK to Cloud KMS to be wrapped by the key encryption key (KEK).
  4. Store the wrapped DEK alongside the ciphertext.

Envelope encryption, with application memory on the left and Google-held Cloud KMS on the right. Inside application memory: generate a data encryption key locally, encrypt the payload with it using local AEAD, and store the wrapped key together with the ciphertext. The data encryption key alone travels right to Cloud KMS, which holds the key encryption key, to be wrapped; the wrapped key is returned and stored alongside the ciphertext. The payload never transits Cloud KMS, and the key encryption key never leaves it.

Decryption reverses it: send the wrapped DEK back to be unwrapped, then decrypt locally.

"The KEK never leaves Cloud KMS."

That is the whole benefit. Payload size is unbounded because the payload never transits the service, and the DEK is in the clear only in application memory, for the duration of a single operation.

Use Tink rather than composing this by hand.

"Tink is an open source cryptography library written by cryptographers and security engineers at Google."

Hand-rolled envelope encryption fails in ways that pass testing and fail in review — nonce reuse, missing authentication, the wrong key-wrapping mode. Tink's KMS-envelope AEAD primitives make those states structurally hard to reach, which is a better guarantee than care.

gcloud kms keys create k-app-envelope \
    --keyring=kr-us-central1-data --location=us-central1 \
    --purpose=encryption --project=rc-saas-shared-sec-01

Anti-pattern. The google_kms_secret and google_kms_secret_ciphertext Terraform data sources decrypt or embed ciphertext at plan time, putting recoverable material into state — which §13.13 establishes is itself a secret store. Wrap and unwrap in application code or Tink, never in a Terraform data source.

Pitfall. Storing the wrapped DEK somewhere other than beside its ciphertext — a separate table, a different bucket, a config file — creates two objects with independent lifecycles and backup schedules. Restore one without the other and the data is unrecoverable even though nothing was destroyed and the key is perfectly healthy.

14.17 Crypto-Shredding §

Crypto-shredding destroys a key version to make every ciphertext under it permanently unrecoverable — an erasure mechanism for when deleting the underlying copies directly is impractical, and the usual answer to a right-to-erasure request in a multi-tenant estate.

It is valid only when all three of these hold, and each is easy to violate silently:

  1. Every copy is under that version — primary storage, backups, snapshots, read replicas, exports, and search or cache indexes — and under no other version or key.
  2. No plaintext exists outside those copies. Destroying a key does nothing to a value already written to a log line, an application cache, or an analytics extract.
  3. The key was never shared across tenants, environments, or data classes (§14.13).

The third condition is the one that has to be designed in advance, and §14.13 explains why: a key protecting more than what you intend to erase turns destruction into an all-or-nothing action across everything under it. That is a data-loss incident, not a scoped erasure. Per-tenant key separation is what makes crypto-shredding a designed capability rather than a lucky accident.

Verify all three before treating a destruction as satisfying an erasure obligation. A version destruction that misses one backup still under the same key has erased nothing, while producing a confident audit record saying it did — which is worse than not having tried, because it stops anyone looking further.

Pitfall. Backups are where this fails in practice. A backup taken before a per-tenant key migration is encrypted under the old shared key, so destroying the new per-tenant key leaves the tenant's data fully recoverable from that backup. Erasure scope must include every backup generation still within retention, or retention must expire first.

14.18 Key Destruction §

Keys and key rings can never be deleted:

"Key rings can't be deleted. You can delete keys and key versions in some circumstances, but names of deleted keys can't be reused."

Only key versions can be destroyed, and destruction is deliberately slow. Scheduling destruction moves the version into a waiting period during which it can still be restored:

"The default scheduled for destruction duration is 30 days."

That duration is set per key with --destroy-scheduled-duration at creation or update, and floored organization-wide by constraints/cloudkms.minimumDestroyScheduledDuration (§2.30) so that no key's owner can set an effectively-immediate destruction window. constraints/cloudkms.disableBeforeDestroy can additionally require a version to sit disabled before it becomes eligible for destruction.

gcloud kms keys versions destroy 3 \
    --key=k-storage-uploads --keyring=kr-us-central1-data \
    --location=us-central1 --project=rc-saas-shared-sec-01

Once the waiting period elapses, destruction is irreversible and the key material is unrecoverable — including by Google. §2.21 already establishes that DestroyCryptoKeyVersion must be alerted on; this section adds the cost dimension:

"Destroyed key versions are not billed resources."

An enabled version, rotated or not, is billed for as long as it stays enabled — the same fact raised in §14.12 from the rotation-hygiene angle. Destroying versions with no remaining dependent ciphertext is therefore both a security practice and a cost-control one.

In Terraform, protect the key ring itself from accidental removal from state — recall that Cloud KMS will refuse the delete regardless, but prevent_destroy stops Terraform from even attempting an operation that would orphan the resource from management:

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 8.0"
    }
  }
}

resource "google_kms_key_ring" "data" {
  name     = "kr-us-central1-data"
  location = "us-central1"
  project  = "rc-saas-shared-sec-01"

  lifecycle {
    prevent_destroy = true
  }
}

resource "google_kms_crypto_key" "storage_uploads" {
  name            = "k-storage-uploads"
  key_ring        = google_kms_key_ring.data.id
  purpose         = "ENCRYPT_DECRYPT"
  rotation_period = "7776000s"

  version_template {
    algorithm        = "GOOGLE_SYMMETRIC_ENCRYPTION"
    protection_level = "SOFTWARE"
  }

  lifecycle {
    prevent_destroy = true
  }
}

prevent_destroy on the key mirrors the API's own refusal and documents intent for anyone reading the configuration, since the key ring's is already enforced server-side regardless.

14.19 Compliance Considerations §

Several regulatory and contractual frameworks name specific Cloud KMS controls directly rather than accepting Google Default Encryption as sufficient. PCI DSS and a number of government and financial frameworks require hardware-backed key storage, which --protection-level=hsm (§14.10) satisfies with its FIPS 140-2 Level 3 certification — cite the certification level exactly in any compliance evidence; do not round it to a general "FIPS-compliant" claim. Data-residency and sovereignty obligations, owned by §15.8§15.9, are satisfied at the key layer by choosing a key ring's location to match the required jurisdiction and never allowing a global ring for that data (§14.2).

Auditors most often ask three questions this chapter answers directly: which principals can administer versus use each key (§14.8, verified against the actual IAM policy on the key, not a role name in documentation), whether destroyed-version audit log entries (DestroyCryptoKeyVersion) are retained and alerted (§2.21), and whether rotation evidence exists without conflating "rotated" with "old data re-encrypted" (§14.12) — a rotation schedule alone does not demonstrate that data encrypted years ago is protected by a currently-reviewed key version. Build evidence exports from gcloud kms keys versions list and Cloud Audit Logs rather than from the rotation schedule configuration alone.

Chapter Summary §

  • Cloud KMS manages keys that never leave the service; Secret Manager (§13.1) stores values applications read directly — the two are not interchangeable.
  • Key rings and keys can never be deleted, and a key ring's location and name are permanent decisions; only key versions can be scheduled for destruction.
  • The direct symmetric encrypt/decrypt plaintext limit is 64 KiB, which is why envelope encryption (§14.16) exists for anything larger.
  • CMEK gives administrative control over already-Google-encrypted data — the ability to revoke or destroy a key independent of who holds resource access — not protection against a principal already holding both resource and decrypt access.
  • §14.8's service-agent table is load-bearing for every sibling chapter's CMEK example; grant the agent only roles/cloudkms.cryptoKeyEncrypterDecrypter on the specific key, never admin.
  • Rotation creates a new primary version and never re-encrypts existing ciphertext; old versions must stay enabled for as long as data under them exists, so rotation alone is not a remediation for key compromise.
  • CSEK is supported only for Compute Engine and Cloud Storage; Google stores no key material, only a validation hash, so a lost CSEK key permanently loses the data it protected.
  • Cloud HSM is certified FIPS 140-2 Level 3; EKM makes external reachability an availability dependency, with Spanner's 30-day automatic deletion as a concrete failure consequence.
  • Key separation by environment, data class, tenant, and region bounds a different failure each; per-tenant separation specifically is the precondition for crypto-shredding.
  • Crypto-shredding erases data only if every copy — backups, snapshots, replicas, exports, cached plaintext — is under the destroyed version and no other; verify all before relying on it as an erasure mechanism.
  • Key Access Justifications requires an Assured Workloads folder and has per-service GA status; a Preview-status service integration must not be treated as production-ready.
  • Cloud KMS Autokey's GA status could not be confirmed from documentation; treat it as unconfirmed rather than production-ready.
  • Destroyed key versions stop being billed; enabled versions of a rotated key continue to be billed indefinitely, which makes pruning unused versions both a security and cost practice.

Security Checklist §

ControlWhy it mattersHow to verify
Key rings and keys live only in rc-saas-shared-sec-01, never a workload projectEnforces independent administrative control (§2.21, §14.8)gcloud kms keyrings list --location=us-central1 --project=rc-saas-shared-sec-01
No global key ring backs data-at-rest CMEKA global dependency for a regional workload with no locality benefitgcloud kms keyrings list --location=global --project=rc-saas-shared-sec-01
Service agents hold only roles/cloudkms.cryptoKeyEncrypterDecrypter, never roles/cloudkms.adminSeparates use from administrationgcloud kms keys get-iam-policy KEY --keyring=RING --location=REGION
gcp-security-admins@rickcollette.domain administers keys; no workload principal doesAdministration stays separate from use (§3.9)inspect the same IAM policy for admin-role members
constraints/gcp.restrictNonCmekServices set for regulated data servicesBlocks Google-managed-key-only resource creationgcloud org-policies describe constraints/gcp.restrictNonCmekServices --project=PROJECT_ID
constraints/gcp.restrictCmekCryptoKeyProjects allows only the security projectPrevents CMEK keys sourced from an unapproved projectsame command against that constraint
constraints/cloudkms.minimumDestroyScheduledDuration floors the destroy windowStops an effectively-immediate key destructionsame command against that constraint
DestroyCryptoKeyVersion and SetIamPolicy on keys are alertedDetects both destruction and access-scope changessee the logging query in §2.21
Old key versions disabled or destroyed once no ciphertext depends on themUnused enabled versions are both an attack surface and a costgcloud kms keys versions list --key=KEY --keyring=RING --location=REGION
Per-tenant keys exist wherever crypto-shredding is a designed erasure pathA shared key makes destruction all-or-nothingreview key-to-tenant mapping against §14.13's separation axes
KAJ-enrolled keys used only for services confirmed GA for KAJA Preview integration cannot reliably generate justificationscheck the current supported-services list before relying on a key's enrollment
Autokey, if used at all, treated as non-production pending GA confirmationUndocumented release stage means no support commitmentre-check release notes before any production adoption

Sources §