S3 KMS.AccessDeniedException: Why GetObject Isn't Enough

Logeshwaran
—

If an Amazon S3 download fails with KMS.AccessDeniedException, giving the caller s3:GetObject is not enough when the object uses SSE-KMS. For a general purpose bucket and a customer managed KMS key, the S3 permissions map identifies kms:Decrypt as conditionally required for GetObject. The counterintuitive part is that adding kms:Decrypt to an IAM role can still do nothing: the KMS key policy must authorize that access path. In cross-account access, the key policy in the key owner's account and IAM permission in the caller's account must work together.

⚡ Quick Answer

• Confirm the caller → run aws sts get-caller-identity.

• Confirm the object → identify whether that exact object uses SSE-KMS and which KMS key protects it.

• General purpose bucket → verify s3:GetObject or s3:GetObjectVersion plus effective kms:Decrypt authorization for the customer managed key.

• Same account → the key policy must directly authorize the principal or enable the account to delegate KMS permission through IAM.

• Cross-account → permission is required in the KMS key policy in the owning account and in an IAM policy in the external caller's account.

If kms:Decrypt is already in IAM and the download still fails, go to key policy vs IAM and then the still-denied checklist.

Jake runs a small phone shop. His S3 bucket holds order exports, product photos, and nightly backups. One morning he can open the bucket, see yesterday's backup, and read the file name, yet the download fails with an access-denied message mentioning AWS KMS.

“I already gave the role S3 read access,” Jake tells Ethan. “Why is KMS even involved?”

Ethan gives him the mental model that matters for the rest of this problem: “Your file is behind two different permission doors. S3 decides whether you may read the object. KMS decides whether the encryption key may be used for that read.”

That is why adding another broad S3 policy often changes nothing. The failure may no longer be at the S3 door.

What KMS.AccessDeniedException on an S3 download means

Amazon S3 can encrypt objects with server-side encryption using AWS Key Management Service keys. The common name is SSE-KMS.

With SSE-KMS, S3 stores the encrypted object while AWS KMS controls use of the KMS key involved in protecting the object's data key. When you request the object, S3 authorization and KMS authorization can both matter.

For a GetObject operation on a general purpose bucket, the S3 policy-actions map lists:

  • s3:GetObject when you retrieve the object without a versionId.
  • s3:GetObjectVersion when you retrieve a particular version.
  • kms:Decrypt when retrieving and decrypting an object encrypted with a customer managed KMS key.

Those KMS permissions do not belong in an S3 bucket policy. KMS actions are authorized through IAM identity policies and KMS key policies.

Layer Permission to inspect What failure usually means
Current S3 object s3:GetObject The identity cannot read that object through the requested S3 path.
Specific S3 version s3:GetObjectVersion The caller can perhaps read the current object but not the requested historical version.
Customer managed KMS key kms:Decrypt The S3 object may be readable, but the caller cannot use the key needed for the encrypted object.
Cross-account bucket Bucket/object permission for the external principal KMS authorization does not itself grant S3 object access.

🙋‍♂️ Jake's Reality Check

"If I can see the object name in the S3 console, doesn't that prove I can read it?"

No. Listing a bucket, displaying an object name, reading an object's metadata, and downloading the object's body are different authorization paths. Seeing the file is not proof that the caller has effective KMS permission.

First check: who is actually making the request?

Do not edit IAM until you know the identity behind the failing request.

A principal is the AWS identity involved in authorization. It might be an IAM role, an assumed-role session, an IAM user, or an AWS service using a role.

From the same CLI environment that fails, run:

aws sts get-caller-identity

Write down:

  • The account ID.
  • The ARN.
  • Whether the ARN represents an IAM user, role, or assumed-role session.

This catches one of the simplest but most expensive debugging mistakes: editing Role A while your EC2 instance, Lambda function, CI runner, shell profile, or federated session is using Role B.

An assumed-role session ARN can look different from the IAM role ARN shown on the role page. Trace the session back to its underlying role and inspect the permissions that apply to that role and session.

If the workload assumes a role with a session policy, remember that the resulting session can be more restricted than the role's identity policy alone suggests.

Console route

  1. Open IAM.
  2. Open Roles for a role-based workload.
  3. Select the exact role used by the request.
  4. Review attached and inline permission policies.
  5. Check whether a permissions boundary is attached.
  6. If the account is in AWS Organizations, determine whether a service control policy limits KMS or S3.

If the caller and the bucket are in different accounts, write down both account IDs now. Cross-account KMS troubleshooting gets much easier once you stop referring to everything as “my account” and “the other account.”

Confirm the exact object's encryption key

Do not assume the bucket's current default encryption configuration tells you which KMS key protects an older object.

A bucket can use one default KMS key today while objects written months ago remain encrypted under another key.

In the S3 console, open:

Amazon S3 → Buckets → your bucket → the exact object → Properties

Identify the object's server-side encryption method and the KMS key associated with it.

From the CLI, object metadata can be inspected with:

aws s3api head-object \
  --bucket amzn-s3-demo-bucket \
  --key backups/orders.json

Then inspect the key:

aws kms describe-key \
  --key-id arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab

Use the actual ARN from your environment. A full ARN is especially valuable when multiple accounts or Regions are involved.

Suppose Jake's bucket used Key A until June and Key B after June. His IAM policy allows kms:Decrypt on Key B.

July's backup downloads.

February's backup fails.

Nothing is inconsistent. The February object still depends on Key A.

✅ Why this is the one to use

Troubleshoot the KMS key recorded for the failing object, not the key you remember configuring most recently.

KMS key policy vs IAM policy: the rule that causes most confusion

A KMS key policy is a resource policy attached to one KMS key. Every KMS key has exactly one key policy.

An IAM identity policy is attached to an IAM user, group, or role.

With AWS KMS, you cannot assume an IAM Allow automatically gives access to every KMS key. The key policy is central to whether an IAM permission can become effective.

If a key policy explicitly enables the AWS account to use IAM policies for the key, IAM administrators in that account can delegate key permissions to roles and users.

A common account-enabling key-policy pattern is:

{
  "Sid": "EnableIAMUserPermissions",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::111122223333:root"
  },
  "Action": "kms:*",
  "Resource": "*"
}

That statement does not mean every identity in account 111122223333 automatically has kms:*.

Its role in this design is to let the account use IAM policies to delegate KMS permissions.

An IAM role can then receive narrowly scoped permission:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadEncryptedBackups",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/backups/*"
    },
    {
      "Sid": "DecryptBackupKey",
      "Effect": "Allow",
      "Action": "kms:Decrypt",
      "Resource": "arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
    }
  ]
}

If the key policy does not permit the account's IAM delegation path, attaching that IAM Allow can have no effect on access to the key.

The alternative is to authorize a principal directly in the key policy:

{
  "Sid": "AllowBackupReader",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::111122223333:role/BackupReader"
  },
  "Action": "kms:Decrypt",
  "Resource": "*"
}

The wildcard in that key policy has a specific context. A key policy is already attached to one KMS key; unlike an IAM policy, the key policy does not identify a separate resource ARN for the key it governs.

Do not use that as a reason to put Resource: "*" into every IAM KMS policy. In an IAM policy, scope kms:Decrypt to the actual KMS key ARN whenever the workload needs a known key.

Same-account fix: S3 object, IAM role, and KMS key in one account

Assume:

  • Bucket owner: account 111122223333.
  • KMS key owner: account 111122223333.
  • Reader role: arn:aws:iam::111122223333:role/BackupReader.

Start with S3:

{
  "Effect": "Allow",
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/backups/*"
}

Then the KMS IAM permission:

{
  "Effect": "Allow",
  "Action": "kms:Decrypt",
  "Resource": "arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
}

Finally confirm that the key policy supports that IAM authorization path, or that the role itself is permitted directly by the key policy.

Do not solve a single GetObject problem by attaching AmazonS3FullAccess and kms:*. That gives the role capabilities unrelated to the failed operation and makes it harder to see which permission was actually missing.

For a normal GetObject path on a general purpose bucket, keep the policy tied to the actual S3 prefix and actual customer managed key.

Cross-account SSE-KMS download: why both KMS policies are required

Cross-account KMS is where a correct-looking IAM policy most often sends people in circles.

Assume:

  • Account A: 111122223333 owns the bucket and customer managed KMS key.
  • Account B: 444455556666 owns role ExternalReader.
  • That role must download Account A's encrypted backup.

Three authorization pieces matter:

Location Permission Purpose
Account A, S3 Allow the external role to read the required object Opens the S3 side of the request.
Account A, KMS key policy Allow Account B or the intended external role to use the key for the permitted cryptographic operation The key owner establishes what external use is possible.
Account B, IAM Delegate kms:Decrypt to ExternalReader The external account decides which identity actually receives the permission.

The KMS key policy and external IAM policy are not interchangeable.

If Account A's key policy permits Account B but Account B never delegates that permission to ExternalReader, the role cannot use it.

If Account B gives ExternalReader an IAM Allow but Account A's key policy does not authorize that cross-account use, the IAM statement cannot create access on its own.

Account A: key policy

{
  "Sid": "AllowExternalReader",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::444455556666:role/ExternalReader"
  },
  "Action": "kms:Decrypt",
  "Resource": "*"
}

Account B: IAM policy

{
  "Effect": "Allow",
  "Action": "kms:Decrypt",
  "Resource": "arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
}

Then Account A still needs an S3 bucket/object permission path that permits the external role to read the object.

Ethan's shorthand for Jake is: “The bucket owner says you may take the box. The key owner says you may unlock the box. Your own account says your role is allowed to use that permission.”

⚠️ What this actually breaks

Do not try to solve a cross-account customer-managed-key problem by editing only Account B's IAM policy. Cross-account KMS use requires an authorization path from the key-owning account as well.

AWS managed aws/s3 key vs customer managed KMS key

An AWS managed KMS key and a customer managed KMS key are not equally configurable.

A customer managed key is a KMS key that you create and administer. You control its key policy.

An AWS managed key is created for use by an AWS service. Its policy is controlled by the AWS service rather than edited by you in the same way as a customer managed key.

That matters for cross-account designs. If another account must use an SSE-KMS object and you need to explicitly authorize that external account or role at the key-policy layer, use a customer managed KMS key that gives you that policy control.

Do not discover this only after the bucket policy is perfect and the external role still cannot decrypt anything.

🙋‍♂️ Jake's Reality Check

"Can I just edit the aws/s3 key policy and add the other account?"

No. If your design requires key-policy control for another account, plan around a customer managed key rather than assuming an AWS managed key can be customized for that purpose.

KMS grants: the third authorization path people forget

Key policy versus IAM policy is the central question in this error, but AWS KMS has another permission mechanism: grants.

A KMS grant can give a grantee principal permission to perform specified operations on one KMS key. Grants can be used by AWS services and applications that need delegated key usage.

That means two systems with similar IAM policies might still have different effective KMS behavior because one access path also depends on a grant.

You can inspect grants with:

aws kms list-grants \
  --key-id arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab

A grant can allow access; it does not create a Deny.

Do not create a new grant merely because you saw an AccessDenied. First identify the caller, object, key, key policy, and IAM path. Checking grants is about understanding the authorization already present, not piling another layer onto a configuration you have not understood.

Grants also have a consistency detail worth knowing. A newly created, retired, or revoked grant might not become visible everywhere immediately. AWS KMS provides grant tokens when a newly created grant must be used before normal propagation completes.

That eventual-consistency behavior is specific enough that “I just created a grant and immediately got AccessDenied” does not automatically prove the grant itself is wrong.

Console route: fix the error without randomly widening policies

If you prefer the AWS Management Console, use a fixed order. It keeps each permission layer separate.

  1. Open the exact object. In S3, identify whether the object is SSE-KMS encrypted and record its KMS key.
  2. Identify the caller. Open IAM and find the real role or user performing the request.
  3. Check S3 object access. Confirm s3:GetObject, or s3:GetObjectVersion if a specific version is requested.
  4. Open AWS KMS. Select the exact customer managed key associated with the object.
  5. Read the key policy. Determine whether the principal is directly authorized or the account is enabled to delegate permission through IAM.
  6. Check the caller's IAM permissions. Confirm the required KMS action is scoped to the actual key ARN.
  7. For cross-account access, inspect both accounts. Check Account A's key policy and Account B's IAM delegation.
  8. Check restriction layers. Look for permissions boundaries, session policies, SCPs, bucket-policy Deny statements, KMS conditions, and endpoint policies.
  9. Retry the same object. Do not switch to another file that may use a different key.

If a different object succeeds, compare its encryption key and version before assuming the permission update partly worked.

That comparison often exposes the real cause faster than another hour in IAM.

CLI route: diagnose the caller, object, key, and policy

1. Show the caller

aws sts get-caller-identity

2. Inspect the object

aws s3api head-object \
  --bucket amzn-s3-demo-bucket \
  --key backups/orders.json

3. Inspect the KMS key

aws kms describe-key \
  --key-id arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab

4. Read the key policy

aws kms get-key-policy \
  --key-id arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab \
  --policy-name default

5. Retry the exact object

aws s3api get-object \
  --bucket amzn-s3-demo-bucket \
  --key backups/orders.json \
  orders.json

If the request specifies a version, include the version ID and check s3:GetObjectVersion rather than assuming s3:GetObject is the relevant S3 action.

Do not try to “test” an S3 object's ciphertext by downloading bytes and manually calling KMS Decrypt against the entire object. S3 manages the SSE-KMS envelope-encryption workflow for the object. Your troubleshooting job is to authorize the actual S3-integrated request path.

IAM already has kms:Decrypt but S3 still says AccessDenied

This is the most useful diagnostic section when the obvious IAM statement is already present.

Cause Clue What to fix
Wrong KMS key The failing object references a different key ARN. Authorize the key that protects this object.
Wrong caller get-caller-identity does not show the identity you edited. Fix the real caller's permission path.
Key policy does not enable IAM IAM contains Allow, but no applicable key-policy authorization exists. Correct the customer managed key policy.
Cross-account key side missing External role allows the key, but key owner did not authorize external use. Add appropriate external permission to the key policy.
Cross-account IAM side missing Key policy permits the account, but the actual external role lacks permission. Delegate the permitted KMS action to the role.
Permissions boundary Role IAM Allow exists, but the boundary does not allow the same action/resource. Change the boundary only if the intended workload should have that permission.
SCP restriction Account belongs to an organization whose policy restricts KMS or S3. Review the organization's allowed permission boundary.
Endpoint policy restriction Same credentials behave differently when traffic goes through a VPC endpoint. Review the relevant endpoint policy.
Specific object version Current version succeeds, historical version fails. Check s3:GetObjectVersion and the historical version's KMS key.

An Allow does not erase an applicable Deny

Adding another Allow is not useful if a relevant policy layer explicitly denies the operation.

Review:

  • IAM identity policies.
  • KMS key policy statements.
  • S3 bucket policy statements.
  • Permissions boundaries.
  • AssumeRole session policies.
  • AWS Organizations service control policies.
  • S3 or KMS VPC endpoint policies where applicable.
  • Conditions inside otherwise correct-looking Allow statements.

The last item is easy to miss. A statement can clearly say Effect: Allow, name the correct role, and contain kms:Decrypt, yet still not apply because its Condition block does not match the request.

⚠️ What this actually breaks

Do not remove a security condition simply because the request succeeds afterward. Conditions often enforce deliberate boundaries such as allowed service paths, network paths, accounts, or encryption context. Fix the mismatch rather than deleting the guardrail.

KMS conditions: when the principal and action look correct

Key policies and IAM policies can use conditions to narrow when KMS permission applies.

For example, KMS condition keys can restrict service-mediated use, encryption context, accounts, resources, or other request properties.

One condition you may encounter is kms:ViaService. It can make permission apply only when the KMS operation is requested through an allowed AWS service.

This produces a useful debugging clue: a direct KMS API experiment and an S3-integrated request are not necessarily equivalent tests if the policy deliberately restricts service-mediated use.

When you read a KMS policy statement, do not stop after these fields:

"Effect": "Allow",
"Action": "kms:Decrypt"

Read the principal, resource context, and complete Condition block.

If an encryption-context condition exists, compare it with the request made through S3 rather than removing the condition immediately.

Current object works but an older S3 version fails

Versioned buckets create two separate traps at once.

First, retrieving the current object without specifying a version uses the s3:GetObject permission path.

Retrieving a specific version with versionId uses s3:GetObjectVersion.

Second, an older version can have been written under a different KMS key.

So this sequence is possible:

  • The current object downloads.
  • An old version fails.
  • The role has s3:GetObject.
  • The role has KMS permission on today's key.

That does not prove the old version should work.

For the historical version, check:

  1. Is the request explicitly supplying versionId?
  2. Does the caller have s3:GetObjectVersion?
  3. Which KMS key protects that particular version?
  4. Does the caller have effective authorization for that key?

Version history preserves old configuration decisions. Your current bucket settings do not rewrite that history automatically.

An encrypted archived object can fail for a reason unrelated to IAM

Not every failed download of an encrypted object is fixed by changing IAM or KMS.

If the object is stored in S3 Glacier Flexible Retrieval, S3 Glacier Deep Archive, the S3 Intelligent-Tiering Archive Access tier, or the S3 Intelligent-Tiering Deep Archive Access tier, the object must first have a restored copy available before normal retrieval.

That gives you two independent checks:

  • Availability: is the archived object currently restored for retrieval?
  • Authorization: does the caller have the required S3 and KMS permissions?

A restore operation does not grant KMS access.

A KMS policy change does not restore an archived object.

Keep those branches separate so you do not spend an hour making a perfect key policy for an object that is still in an archive state that cannot yet be retrieved.

Directory buckets and S3 Express use a different access branch

Before you copy a general purpose bucket fix, confirm the bucket type.

S3 directory buckets use session-based authorization. The access path can require s3express:CreateSession. The CLI and current SDKs can obtain and refresh the session token used for object operations.

The GetObject API reference also calls out additional KMS permission requirements for SSE-KMS directory-bucket retrieval, including kms:GenerateDataKey and kms:Decrypt.

That means a directory bucket should not be diagnosed by blindly applying the general purpose bucket permission table.

✅ Why this is the one to use

Identify the bucket type before changing KMS permissions. General purpose buckets and directory buckets do not have identical object-authorization paths.

For a directory bucket, investigate:

  • Session permission such as s3express:CreateSession.
  • The session-token path used by the SDK or CLI.
  • The KMS key associated with the object.
  • The KMS actions required for that directory-bucket access path.
  • Key-policy and IAM authorization for those actions.

This is one of the cases where “I copied the exact policy from another S3 bucket and it still fails” can be a perfectly logical result.

Use CloudTrail when every policy looks correct

Once you have checked the caller, object, key, and visible permission statements, stop guessing.

CloudTrail helps answer what actually happened.

For cross-account KMS operations, activity involving a key in another account is recorded in both the caller's account and the key owner's account.

That is useful because each side can answer a different question:

  • Who made the request?
  • Which key ARN was involved?
  • Which account owned that key?
  • Which cryptographic operation was requested?
  • Which Region handled it?
  • Was the role you edited actually the caller?

If CloudTrail points at a key ARN different from the one in your IAM policy, the problem is no longer mysterious.

If it shows a different assumed role from the role you edited, the IAM problem is equally clear.

If it shows the correct role and key, move outward to key-policy conditions, boundaries, SCPs, endpoint policies, and grants.

What changed in 2026: KMS now shows the last cryptographic key use

🕐 What changed between versions

  • On April 27, 2026, AWS KMS added visibility into the last cryptographic operation performed with KMS keys.
  • The information can include the timestamp, operation type, and associated CloudTrail event ID.
  • The feature helps you trace recent key activity when multiple old and new keys exist in the same environment.

This feature does not grant access.

It does not prove that your current caller has permission.

It also records the last successful cryptographic operation rather than every possible use of the key.

There can be a delay before last-usage information appears, so do not treat the screen as a second-by-second request monitor.

Its value in this problem is investigative. If Jake has five old backup keys and no longer remembers which ones are still involved in production, recent-use information can help him find a useful CloudTrail event and follow the real activity.

That is much better than attaching Decrypt permission to all five keys “just to see which one works.”

I changed a KMS grant and the first retry still fails

AWS KMS follows an eventual-consistency model for grant changes.

A newly created grant might not be recognized across every KMS path immediately. A recently retired or revoked grant can have the same propagation consideration.

When a grant is newly created, the returned grant token can be supplied where immediate use of that new grant is required.

This matters because a rapid troubleshooting loop can make the problem worse:

  1. Create a grant.
  2. Retry immediately.
  3. See AccessDenied.
  4. Add broader IAM permissions.
  5. Edit the key policy.
  6. Add a wildcard.
  7. Retry again and no longer know which change mattered.

Ethan tells Jake, “One justified change at a time. Otherwise troubleshooting turns into archaeology.”

Do not use eventual consistency as an excuse for an obviously wrong policy. Confirm the principal, key ARN, action, and intended authorization path first.

Why kms:* is the wrong emergency fix

The fastest-looking emergency policy is often this:

{
  "Effect": "Allow",
  "Action": "kms:*",
  "Resource": "*"
}

That is not a diagnosis. It is a new security problem.

kms:* includes management actions unrelated to reading one S3 object. A workload that needs a cryptographic operation does not need authority to administer keys merely to make an AccessDenied disappear.

The safer debugging rule is:

  • Identify the exact S3 operation.
  • Identify the exact KMS key.
  • Identify the exact KMS action required for that path.
  • Grant only that operation to the intended principal.

For the standard general purpose bucket GetObject path described earlier, that means focusing on kms:Decrypt for the exact customer managed key instead of using kms:*.

For a directory bucket or a different API path, check that path's permission requirements rather than copying the general purpose bucket policy blindly.

A decision path that avoids random policy edits

1. Is the object SSE-KMS encrypted?

If no, stop debugging KMS for that object.

If yes, continue.

2. What bucket type is this?

If it is a general purpose bucket, use the general purpose S3 permission path.

If it is a directory bucket, include S3 Express session authorization and its KMS requirements.

3. Is the request for the current object or a specific version?

Current object: check s3:GetObject.

Specific version: check s3:GetObjectVersion.

4. Which exact KMS key protects this object or version?

Read the object's encryption information. Do not substitute the bucket's current default key.

5. Is this same-account or cross-account?

Same account: confirm direct key-policy permission or IAM delegation enabled by the key policy.

Cross-account: confirm the owning key policy and the external account's IAM delegation.

6. Does IAM already allow the expected KMS action?

If no, add narrowly scoped permission.

If yes, do not add a bigger Allow yet.

7. Is another layer reducing or denying the permission?

Inspect conditions, boundaries, session policies, SCPs, endpoint policies, bucket policy, and grants.

8. Does CloudTrail identify a different caller or key?

If yes, fix the mismatch.

9. Is the object archived?

If yes, make sure a restored copy is available before expecting normal retrieval.

This path turns “KMS AccessDenied” from one giant problem into a series of yes-or-no questions.

Worked example: Jake's cross-account backup reader

Jake moves reporting into a separate AWS account so a reporting role can read daily sales exports without getting access to the rest of his application account.

The setup is:

  • Production account: 111122223333.
  • Reporting account: 444455556666.
  • Bucket: amzn-s3-demo-bucket.
  • Prefix: reports/.
  • Reporting role: ExternalReader.
  • KMS key: arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab.

The reporting role can list enough of the bucket to find the report, but downloading reports/daily.csv fails around KMS authorization.

Jake adds this in the reporting account:

{
  "Effect": "Allow",
  "Action": "kms:Decrypt",
  "Resource": "arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
}

The download still fails.

Why?

The reporting account has said, “ExternalReader may use this key if the key owner permits it.”

But the production account has not said, “This external principal is allowed to use my key.”

Jake then adds a key-policy statement in the production account:

{
  "Sid": "AllowReportingReader",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::444455556666:role/ExternalReader"
  },
  "Action": "kms:Decrypt",
  "Resource": "*"
}

That handles the KMS side.

He still needs the S3 bucket/object authorization path for reports/*. KMS permission does not grant S3 permission.

This example explains the entire topic in one sentence: cross-account encrypted S3 access is a combination of S3 resource access, key-owner permission, and caller-account delegation.

When nothing works: collect evidence before escalating

If every visible statement looks right, stop broadening policies.

Collect a small evidence package:

  • The exact failing command or API request.
  • The output of aws sts get-caller-identity.
  • The bucket name.
  • The exact object key.
  • The object version ID, if one is requested.
  • The bucket type: general purpose or directory bucket.
  • The bucket Region.
  • The KMS key ARN associated with the failing object.
  • The key owner's account ID.
  • The caller's account ID.
  • The applicable key-policy statement.
  • The applicable IAM KMS statement.
  • The applicable S3 identity or bucket-policy statement.
  • The role's permissions boundary, if one exists.
  • Relevant session policy information.
  • Applicable SCPs.
  • Relevant VPC endpoint policies.
  • Any matching KMS grants.
  • CloudTrail request/event IDs and timestamps.
  • Whether every object fails or only objects from a particular date or prefix.

That last clue is powerful.

If every object fails, think identity, account, key policy, or broad restriction.

If only older objects fail, think historical key or object version.

If only one network path fails, inspect endpoint restrictions.

If only cross-account access fails while same-account access succeeds, inspect the external-account key-policy and IAM pairing.

If the current version succeeds but an old version fails, inspect s3:GetObjectVersion and that version's KMS key.

16 S3 KMS AccessDenied questions people hit next

Why do I get KMS.AccessDeniedException when downloading from S3?

The object is probably using SSE-KMS and the caller does not have an effective KMS authorization path for the key that protects that object. Check S3 object permission, the exact key ARN, the key policy, IAM permission, and any restrictions or Deny statements.

Does s3:GetObject automatically include kms:Decrypt?

No. S3 object permission and KMS key permission are separate. For a general purpose bucket using a customer managed KMS key, kms:Decrypt is conditionally required for the encrypted GetObject path.

Why does kms:Decrypt in IAM still return AccessDenied?

The KMS key policy must support that authorization path, and another policy layer or condition must not prevent it. Also verify that IAM names the KMS key actually used by the object.

Do same-account S3 downloads always need both an IAM policy and key policy entry for the role?

No. A key policy can directly authorize a principal, or the key policy can enable the account to delegate KMS permission through IAM. The correct answer depends on the customer managed key's policy design.

Do cross-account S3 KMS downloads need both key policy and IAM permission?

Yes. Cross-account KMS access requires permission from the key-owning account's key policy and IAM delegation in the external caller's account. S3 access is a separate permission layer as well.

Why can I list an S3 bucket but not download a KMS-encrypted file?

Listing and GetObject are different S3 operations, and SSE-KMS retrieval can introduce a KMS authorization requirement. Seeing an object name does not prove that you have permission to retrieve and decrypt its contents.

Why do new files download but older SSE-KMS files fail?

Older objects may have been encrypted under an earlier customer managed key. Changing a bucket's current default KMS key does not automatically make historical objects use that new key.

Why does the current S3 object work but an older version fails?

A specific object version can require s3:GetObjectVersion, and that historical version might also have been encrypted with a different KMS key. Check both the S3 version permission and that version's encryption metadata.

Can an SCP override my kms:Decrypt Allow?

An applicable organizational restriction can limit what principals in the account can do even when an identity policy contains an Allow. Include SCPs in the investigation when the account belongs to AWS Organizations.

Can a permissions boundary cause KMS AccessDenied?

Yes. A role's identity policy does not grant a permission beyond the maximum permissions permitted by its boundary. If the boundary excludes the necessary KMS or S3 action, another identity-policy Allow does not solve the problem.

Can a VPC endpoint policy cause S3 or KMS AccessDenied?

Yes. If the request is made through an endpoint whose policy restricts the principal, action, or resource, the same credentials can behave differently through another network path. Review endpoint policy when that symptom appears.

Should I grant kms:* while troubleshooting S3?

No. Grant the cryptographic operation required by the actual S3 access path rather than KMS administrative permissions. For a general purpose bucket GetObject using a customer managed key, focus on the documented Decrypt requirement and the specific key ARN.

Can I edit the aws/s3 managed key policy for cross-account access?

Do not design the solution around editing an AWS managed key policy as though it were a customer managed key. When you need explicit control of external-account KMS authorization, use a customer managed KMS key.

Can KMS grants affect whether an S3-related KMS operation succeeds?

KMS grants are another mechanism for allowing key operations and are commonly used in service-integrated workflows. Inspect existing grants when the policy documents alone do not explain the effective authorization, but do not create grants blindly as a troubleshooting shortcut.

Why can an archived SSE-KMS object not be downloaded even after permissions are fixed?

Objects in supported S3 archival storage classes and archive tiers must first have a restored copy available for retrieval. Restoration and KMS authorization are separate requirements.

Do directory buckets use the same KMS permissions as normal S3 buckets?

Do not assume so. Directory buckets use a session-based S3 Express authorization path, and the GetObject API describes SSE-KMS directory-bucket retrieval with both kms:GenerateDataKey and kms:Decrypt requirements. Check the bucket type before copying a general purpose bucket policy.

Final checklist before you touch another policy

Run this list from top to bottom:

  • Confirm the caller with aws sts get-caller-identity.
  • Confirm the exact bucket and object.
  • Confirm whether the request specifies a version ID.
  • Confirm whether the bucket is general purpose or a directory bucket.
  • Confirm whether the object is SSE-KMS encrypted.
  • Record the exact KMS key ARN protecting that object or version.
  • Confirm the S3 permission for that retrieval path.
  • Confirm the KMS action required by that path.
  • For same-account access, verify direct key-policy permission or IAM delegation enabled by the key policy.
  • For cross-account access, verify both the key-owner policy and the external IAM delegation.
  • Check whether the object uses an AWS managed key when your design requires key-policy customization.
  • Check conditions in KMS and S3 policy statements.
  • Check permissions boundaries.
  • Check session policies.
  • Check relevant SCPs.
  • Check endpoint policies when the request goes through VPC endpoints.
  • Inspect KMS grants if the normal policies do not explain the authorization.
  • Check whether an archived object requires restoration.
  • Use CloudTrail to confirm caller, key, operation, account, and Region.

Jake's original problem sounded like one sentence: “S3 says AccessDenied even though S3 read is allowed.”

It was really several smaller questions: who is reading, which object is being read, which version, which key encrypted it, who owns that key, whether IAM is actually allowed to delegate access to it, whether another account is involved, and whether another policy layer narrows the result.

Once those questions are separated, KMS AccessDenied stops being a guessing game.

If you're still blocked after this checklist, keep the policies narrow and take the evidence with you: caller ARN, object key, version ID, key ARN, account IDs, Region, CloudTrail event, and the exact policy statements involved. That is far more useful than a screenshot showing that a role has “KMS access” somewhere in a long list of permissions.

📌 If you keep one line from this page

For an SSE-KMS S3 download, permission to read the object and permission to use the object's actual KMS key are separate authorization decisions.

And when the key belongs to another account, both sides of that KMS relationship must permit the use.

Revision note. Written October 1, 2026, for the one encrypted file that won't come down while its neighbors do. Look at that object's key before you widen anything; the narrow fix is nearly always the right one.

Related