dougua9165 2019-08-01 22:14
浏览 234
已采纳

golang:带有SSE的S3对象的预签名URL的GET请求失败,出现403错误

I'm getting 403 Forbidden HTTP response when I make a GET request to a presigned url generated by the AWS Presign method in Go.

The error message is:

The request signature we calculated does not match the signature you provided. Check your key and signing method

X-Amz-SignedHeaders is: host;x-amz-server-side-encryption-customer-algorithm;x-amz-server-side-encryption-customer-key;x-amz-server-side-encryption-customer-key-md5

I'm writing the object to S3 like this:

type DocumentStore struct {
    bucketName               string
    bucketEncryptionKeyAlias string
    aws                      *session.Session
}

func (s *DocumentStore) PutDocument(ctx context.Context, envelope []byte, metadata map[string]string) (PutDocumentResult, error) {
    uploader := s3manager.NewUploader(s.aws)

    var objectKey = uuid.New().String()

    if _, err := uploader.UploadWithContext(ctx, &s3manager.UploadInput{
        Bucket:               aws.String(s.bucketName),
        Key:                  aws.String(objectKey),
        ContentType:          aws.String("application/octet-stream"),
        Body:                 bytes.NewReader(envelope),
        ServerSideEncryption: aws.String(s3.ServerSideEncryptionAwsKms),
        SSEKMSKeyId:          aws.String(s.bucketEncryptionKeyAlias),
        Metadata:             aws.StringMap(metadata),
    }); err != nil {
        return PutDocumentResult{}, fmt.Errorf("put document failed on upload: %v", err.Error())
    }

    return PutDocumentResult{
        BucketName: s.bucketName,
        ObjectKey:  objectKey,
    }, nil
}

I'm signing the url like this:

func (s *DocumentStore) NewSignedGetURL(ctx context.Context, objectKey string, ttl time.Duration) (string, error) {
    svc := s3.New(s.aws)

    req, _ := svc.GetObjectRequest(&s3.GetObjectInput{
        Bucket: aws.String(s.bucketName),
        Key: aws.String(objectKey),
        SSECustomerKey: aws.String(s.bucketEncryptionKeyAlias),
        SSECustomerAlgorithm: aws.String(s3.ServerSideEncryptionAwsKms),
    })

    url, err := req.Presign(ttl)
    if err != nil {
        return "", fmt.Errorf("failed to presign GetObjectRequest for key %q: %v", objectKey, err)
    }

    return url, nil
}

And I'm calling the methods like this:

result, err := target.PutDocument(context.TODO(), envelope, metadata)
if err != nil {
    t.Errorf("PutDocument failed: %v", err)
    return
}

getURL, err := target.NewSignedGetURL(context.TODO(), result.ObjectKey, time.Minute*5)
if err != nil {
    t.Errorf("failed to sign url: %v", err)
    return
}

req, _ := http.NewRequest("GET", getURL, nil)
req.Header.Add("x-amz-server-side-encryption-customer-algorithm", s3.ServerSideEncryptionAwsKms)
req.Header.Add("x-amz-server-side-encryption-customer-key", test.cfg.AWS.BucketKMSAlias)
req.Header.Add("x-amz-server-side-encryption-customer-key-md5", "")

resp, err := http.DefaultClient.Do(req.WithContext(context.TODO()))
if err != nil {
    t.Errorf("failed to request object from signed url: %v", err)
    return
}
defer resp.Body.Close()

data, err := ioutil.ReadAll(resp.Body)
if err != nil {
    t.Errorf("failed to read object stream from S3: %v", err)
    return
}

if resp.StatusCode != http.StatusOK {
    t.Errorf("failed to get object. Http status: %s(%d)
%s", resp.Status, resp.StatusCode, data)
    return
}

I can read the download the file from the aws cli like this:

aws --profile dispatcher_stage --region us-east-1 s3 cp s3://[bucket-name]/0c/09179312-e283-431c-ab71-6a0c437177fe . --sse aws:kms --sse-kms-key-id alias/[key-alias-name]

What am I missing?

  • 写回答

1条回答 默认 最新

  • dshgnt2008 2019-08-01 23:57
    关注

    I figured it out: the GetObject request doesn't need the SSE parameters as long as the user has Decrypt permission on the KMS key. Here's the relevant changes:

    I'm now signing the url like this:

    func (s *DocumentStore) NewSignedGetURL(ctx context.Context, objectKey string, ttl time.Duration) (string, error) {
        svc := s3.New(s.aws)
    
        req, _ := svc.GetObjectRequest(&s3.GetObjectInput{
            Bucket: aws.String(s.bucketName),
            Key: aws.String(objectKey),
        })
    
        url, err := req.Presign(ttl)
        if err != nil {
            return "", fmt.Errorf("failed to presign GetObjectRequest for key %q: %v", objectKey, err)
        }
    
        return url, nil
    }
    

    And I'm downloading the object like this:

    getURL, err := target.NewSignedGetURL(context.TODO(), result.ObjectKey, time.Minute*5)
    if err != nil {
        t.Errorf("failed to sign url: %v", err)
        return
    }
    
    req, _ := http.NewRequest("GET", getURL, nil)
    req.Header.Add("host", req.Host)
    
    resp, err := http.DefaultClient.Do(req.WithContext(context.TODO()))
    if err != nil {
        t.Errorf("failed to request object from signed url: %v", err)
        return
    }
    defer resp.Body.Close()
    
    data, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        t.Errorf("failed to read object stream from S3: %v", err)
        return
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 如何在3D高斯飞溅的渲染的场景中获得一个可控的旋转物体
  • ¥88 实在没有想法,需要个思路
  • ¥15 MATLAB报错输入参数太多
  • ¥15 python中合并修改日期相同的CSV文件并按照修改日期的名字命名文件
  • ¥15 有赏,i卡绘世画不出
  • ¥15 如何用stata画出文献中常见的安慰剂检验图
  • ¥15 c语言链表结构体数据插入
  • ¥40 使用MATLAB解答线性代数问题
  • ¥15 COCOS的问题COCOS的问题
  • ¥15 FPGA-SRIO初始化失败