You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AWS S3预签名URL上传签名不匹配,Java SDK生成URL缺失KMS参数

S3预签名URL上传签名不匹配&Java SDK不返回KMS参数问题解决

调用S3预签名URL执行上传时出现报错:The request signature we calculated does not match the signature you provided. Check your key and signing method。


可正常工作的NodeJS SDK配置

Node依赖:
"aws-sdk": "^2.873.0"

生成预签名URL代码:

let params = {
  Bucket: s3_credentials.s3Bucket,
  Key: s3key,
  ContentType: contentType,
  ServerSideEncryption: 'aws:kms',
  SSEKMSKeyId: s3_credentials.kmsKeyName,
  Expires: 500 // 过期时间单位为秒
};

const signedUrl = s3.getSignedUrl('putObject', params);

生成的URL会自动包含Content-Type、x-amz-server-side-encryption、x-amz-server-side-encryption-aws-kms-key-id参数,直接调用即可上传成功。


存在问题的Java SDK配置

Maven依赖:

<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>aws-sdk-java</artifactId>
    <version>2.17.66</version>
</dependency>

现有生成预签名URL代码:

PutObjectRequest objectRequest = PutObjectRequest.builder()
    .bucket(SPEC_BUCKET_NAME)
    .key(s3Key)
    .contentType(contentType)
    .serverSideEncryption(ServerSideEncryption.AWS_KMS)
    .ssekmsKeyId(getKmsId())
    .expires(expiration)
    .build();

PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder().putObjectRequest(objectRequest).signatureDuration(Duration.ofMinutes(60)).build();

PresignedPutObjectRequest presignedRequestResponse = s3Presigner.presignPutObject(presignRequest);

String presignedUrl = presignedRequestResponse.url().toString();

KMS ID获取代码:

private String getKmsId() {
  KmsClient kmsClient = KmsClient.builder().build();
  DescribeKeyRequest req = DescribeKeyRequest.builder().keyId("alias/xxx").build();
  DescribeKeyResponse res = kmsClient.describeKey(req);

  logger.info(res.keyMetadata().toString());
  return res.keyMetadata().keyId();
}

也尝试过直接传入KMS别名,问题依旧没有解决:

.serverSideEncryption(ServerSideEncryption.AWS_KMS)
.ssekmsKeyId("alias/xxxx")

问题原因&解决方案

Java SDK v2的默认行为和NodeJS SDK v2不同:Java SDK不会自动把服务端加密、Content-Type这类配置参数放到预签名URL的查询串中,只会将这些参数纳入签名计算逻辑,要求调用方在上传时主动在请求头带上对应参数,否则就会出现签名不匹配的报错。

可根据业务场景选择以下两种解决方式:

  • 方式一:上传时主动携带请求头
    调用生成的预签名URL发起PUT请求时,主动在请求头中加入Content-Type、x-amz-server-side-encryption: aws:kms、x-amz-server-side-encryption-aws-kms-key-id: 你的KMS ID三个参数,和生成预签名URL时的配置完全一致即可,不需要修改生成预签名URL的代码。
  • 方式二:强制将加密参数写入预签名URL
    如果上传端无法自定义请求头,可以在生成预签名URL时显式指定需要签名的头部,SDK会自动将这些头部转换为URL查询参数,修改后代码如下:
    PutObjectRequest objectRequest = PutObjectRequest.builder()
        .bucket(SPEC_BUCKET_NAME)
        .key(s3Key)
        .contentType(contentType)
        .serverSideEncryption(ServerSideEncryption.AWS_KMS)
        .ssekmsKeyId(getKmsId())
        .build();
    
    PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
        .putObjectRequest(objectRequest)
        .signatureDuration(Duration.ofMinutes(60))
        // 显式指定需要加入URL的签名参数
        .putSignedHeader("x-amz-server-side-encryption", ServerSideEncryption.AWS_KMS.toString())
        .putSignedHeader("x-amz-server-side-encryption-aws-kms-key-id", getKmsId())
        .putSignedHeader("Content-Type", contentType)
        .build();
    
    PresignedPutObjectRequest presignedRequestResponse = s3Presigner.presignPutObject(presignRequest);
    String presignedUrl = presignedRequestResponse.url().toString();
    
    修改后生成的预签名URL会和NodeJS生成的结构一致,直接调用即可完成上传。

额外注意事项

  • 生成预签名URL的凭证和上传时使用的凭证必须一致,且需要具备s3:PutObject、kms:GenerateDataKey权限
  • KMS密钥策略需要允许对应凭证调用密钥,跨账号场景需要额外配置跨账号访问权限
  • 生成预签名URL时的ContentType和上传时的ContentType必须完全一致,任意偏差都会触发签名错误

内容的提问来源于stack exchange,提问作者mgnfcnt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 13:27:01