在Cognito自定义邮件发送Lambda中正确使用KMS解密的方法
问题描述
我已按如下方式配置AWS Cognito用户池及自定义邮件发送Lambda:
resource "aws_kms_key" "cognito-send-mails" { description = "This key is used to decrypt Cognito codes for CustomEmailSender lambda" deletion_window_in_days = 10 enable_key_rotation = true } resource "aws_cognito_user_pool" "users" { # ... lambda_config { kms_key_id = aws_kms_key.cognito-send-mails.arn custom_email_sender { lambda_arn = aws_lambda_function.cognito-send-emails.arn lambda_version = "V1_0" } } } resource "aws_lambda_function" "cognito-send-emails" { # ... environment { variables = { KEY_ID = aws_kms_key.cognito-send-mails.arn } } } resource "aws_lambda_permission" "cognito-send-mails" { statement_id = "AllowExecutionFromCognitoUserPool" action = "lambda:InvokeFunction" function_name = aws_lambda_function.cognito-send-emails.function_name principal = "cognito-idp.amazonaws.com" source_account = data.aws_caller_identity.current.account_id } resource "aws_iam_role_policy" "cognito-send-mails-kms" { name = "cognito-${local.deployment-name}-send-emails-lambda-kms" role = aws_iam_role.lambda_exec_role.id policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "kms:Decrypt" ], "Resource": "${aws_kms_key.cognito-send-mails.arn}" } ] } EOF }
Lambda已成功触发,收到的请求如下:
{ "callerContext": { "awsSdkVersion": "aws-sdk-unknown-unknown", "clientId": "..." }, "region": "...", "request": { "clientMetadata": null, "code": "AYADe1...ANY=", "type": "customEmailSenderRequestV1", "userAttributes": { "cognito:email_alias": "...", "cognito:user_status": "UNCONFIRMED", "email": "...", "email_verified": "false", "name": "...", "sub": "..." } }, "triggerSource": "CustomEmailSender_SignUp", "userName": "...", "userPoolId": "...", "version": "1" }
我的目标是解密code字段以构建邮件内容。由于只能使用基于API生成的库(我用的是Haskell的amazonka),必须直接调用AWS KMS API。我调用KMS API的参数如下:
Decrypt { encryptionAlgorithm = null, encryptionContext = null, grantTokens = null, keyId = "arn:aws:kms:***:***:key/***", ciphertextBlob = "AYADe1...ANY=" }
但遇到了如下错误:
ServiceError { abbrev = "KMS", status = Status {statusCode = 400, statusMessage = "Bad Request"}, headers = [ ("x-amzn-RequestId","***"), ("Cache-Control","no-cache, no-store, must-revalidate, private"), ("Expires","0"), ("Pragma","no-cache"), ("Date","***"), ("Content-Type","application/x-amz-json-1.1"), ("Content-Length","39") ], code = "InvalidCiphertext", message = null, requestId = "***" }
我尝试过对code进行base64解码后再重新编码,但仍得到相同的错误码。请问仅使用KMS API的正确解密流程是什么?
解决方案
按照以下步骤调整KMS API调用参数,即可正确解密Cognito传递的code字段:
正确处理密文格式
Cognito传递的code是base64编码的完整KMS密文块,KMS的DecryptAPI要求ciphertextBlob参数是原始二进制数据,而非base64字符串。你需要直接对code执行base64解码,得到二进制字节流后传入API,不要解码后重新编码。必须指定匹配的加密上下文
Cognito加密验证码时会自动添加固定的加密上下文,解密时必须完全匹配才能成功。加密上下文的键值对为:aws:cognito:userPoolId: Lambda请求中的userPoolId字段值aws:cognito:tokenUsage: 根据触发类型选择:注册场景填REGISTER,忘记密码场景填FORGOT_PASSWORD(对应triggerSource字段的后缀)
显式指定加密算法
Cognito默认使用RSAES_OAEP_SHA_256算法加密验证码,调用Decrypt时必须显式指定该算法,不能留空。
调整后的Haskell调用示例:
Decrypt { encryptionAlgorithm = Just RSAES_OAEP_SHA_256, encryptionContext = Just (fromList [ ("aws:cognito:userPoolId", "<你的userPoolId>"), ("aws:cognito:tokenUsage", "REGISTER") -- 按实际触发场景调整 ]), grantTokens = Nothing, keyId = Just "arn:aws:kms:***:***:key/***", ciphertextBlob = "<base64解码后的二进制字节流>" }
另外,确认Lambda角色的KMS权限配置正确(你的现有配置已包含kms:Decrypt权限),且使用的KMS密钥ARN与Cognito用户池配置的完全一致。
内容的提问来源于stack exchange,提问作者GlinesMome
相关产品推荐
相关产品推荐

