- Newest
- Most votes
- Most comments
At a quick glance, it looks like your client-side code is supplying ExternalId in the encryption context, which is a way to provide AAD (additional authenticated data) to cryptographic operations for use in making authorisation decisions.
Your key policy should use this type of comparison operation to evaluate the value provided in the encryption context (https://docs.aws.amazon.com/kms/latest/developerguide/conditions-kms.html#conditions-kms-encryption-context):
"Condition": { "StringEquals": { "kms:EncryptionContext:ExternalId": "aaaabbbb-a54d-4e7d-93ea-123456789012" } }
You should also split the key policy statement granting the remote account access into two separate statements. One should contain the cryptographic operations that accept encryption context attributes, and the other statement should contain kms:DescribeKey which does not accept them and would always fail to match the condition.
The service authorisation reference shows which permissions accept encryption context attributes (and all other service-specific condition keys): https://docs.aws.amazon.com/service-authorization/latest/reference/list_awskeymanagementservice.html
The issue you're experiencing is related to how the ExternalId condition is being used in the key policy and how it's being passed in the client-side code. The ExternalId is typically used with IAM roles for cross-account access, but in your case, you're trying to use it directly with KMS operations.
To make this work, you need to modify your client-side code to pass the ExternalId when assuming a role that has permission to use the KMS key. Here's how you can adjust your approach:
-
In the account that's using the KMS key (111222333555), create an IAM role with permissions to use the KMS key.
-
In the key owner's account (666777888999), modify the key policy to allow the IAM role from the other account to use the key, instead of allowing the root account.
-
In your client-side code, use the AWS SDK to assume the IAM role, passing the ExternalId when doing so.
-
Use the temporary credentials obtained from assuming the role to create the KMS client.
Here's an example of how you might modify your code:
import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts"; import { KMSClient } from "@aws-sdk/client-kms"; import { KmsKeyringNode, buildClient, CommitmentPolicy } from '@aws-crypto/client-node'; const stsClient = new STSClient({ region: "your-region" }); async function assumeRole() { const params = { RoleArn: "arn:aws:iam::666777888999:role/CrossAccountKMSRole", RoleSessionName: "CrossAccountKMSSession", ExternalId: "aaaabbbb-a54d-4e7d-93ea-123456789012" }; const command = new AssumeRoleCommand(params); const response = await stsClient.send(command); return response.Credentials; } async function initializeKeyRing() { const credentials = await assumeRole(); const kmsClient = new KMSClient({ credentials: { accessKeyId: credentials.AccessKeyId, secretAccessKey: credentials.SecretAccessKey, sessionToken: credentials.SessionToken }, region: "your-region" }); const generatorKeyId = env().GeneratorKeyId; const keyIds = [env().GeneratorKeyId]; const keyRing = new KmsKeyringNode({ generatorKeyId: generatorKeyId, keyIds: keyIds, clientProvider: () => kmsClient }); return keyRing; } // Rest of your code remains the same
This approach ensures that the ExternalId is used when assuming the role, which then has the necessary permissions to use the KMS key. The KMS operations will be performed using the temporary credentials obtained from assuming the role, satisfying the ExternalId condition in the key policy.
Sources
Allowing users in other accounts to use a KMS key - AWS Key Management Service
Encrypt - AWS Key Management Service
AWS KMS condition keys - AWS Key Management Service
This answer seems making sense. But, I got this error
TypeError: client.generateDataKey is not a function at Object.generateDataKey (/Users/slin/Work/aurascape/ai-mgmt/mgmt.lib/node_modules/@aws-crypto/kms-keyring/build/main/helpers.js:15:35) at KmsKeyringNode._onEncrypt (/Users/slin/Work/aurascape/ai-mgmt/mgmt.lib/node_modules/@aws-crypto/kms-keyring/build/main/kms_keyring.js:57:49) at KmsKeyringNode.onEncrypt (/Users/slin/Work/aurascape/ai-mgmt/mgmt.lib/node_modules/@aws-crypto/material-management/build/main/keyring.js:26:38) at NodeDefaultCryptographicMaterialsManager.getEncryptionMaterials (/Users/slin/Work/aurascape/ai-mgmt/mgmt.lib/node_modules/@aws-crypto/material-management-node/build/main/node_cryptographic_materials_manager.js:34:45) at Object._encryptStream (/Users/slin/Work/aurascape/ai-mgmt/mgmt.lib/node_modules/@aws-crypto/encrypt-node/build/main/encrypt_stream.js:46:10) at Object._encrypt (/Users/slin/Work/aurascape/ai-mgmt/mgmt.lib/node_modules/@aws-crypto/encrypt-node/build/main/encrypt.js:18:37)
Relevant content
- AWS OFFICIALUpdated 2 years ago

Yes, after making this change to my key policy, it works! Thanks a lot!