使用带强制Content-MD5的AWS S3预签名URL上传失败排查
问题排查:AWS S3预签名URL带Content-MD5校验上传失败的原因
我来帮你搞定这个问题——你踩了一个非常常见的MD5编码误区!
核心错误点
你从md5命令拿到的是十六进制字符串格式的MD5哈希值,但AWS S3要求的Content-MD5请求头,必须是原始MD5二进制哈希值的Base64编码结果,而不是把十六进制字符串直接转成Base64。
你的代码里做了base64.StdEncoding.EncodeToString([]byte(md5)),这相当于把"ce0a4a83c88c2e7562968f03076ae62f"这个纯文本字符串转成了Base64,完全不符合AWS的要求,所以会返回InvalidDigest错误。
修正方案
你需要先把十六进制的MD5字符串解码成原始二进制字节,再对这些字节做Base64编码。修改代码里的MD5处理部分(记得补充encoding/hex包的导入):
import ( "encoding/base64" "encoding/hex" "fmt" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" ) func main() { sess, err := session.NewSession(&aws.Config{ Region: aws.String("eu-central-1")}, ) if err != nil { fmt.Println("error creating session", err) return } svc := s3.New(sess) resp, err := svc.PutObjectRequest(&s3.PutObjectInput{ Bucket: aws.String("bucket"), Key: aws.String("testfile.txt"), }) if err != nil { fmt.Println("error creating put request", err) return } md5Hex := "ce0a4a83c88c2e7562968f03076ae62f" // 第一步:把十六进制字符串解码为原始二进制MD5值 md5Bytes, err := hex.DecodeString(md5Hex) if err != nil { fmt.Println("error decoding MD5 hex string", err) return } // 第二步:对二进制值做Base64编码,得到AWS要求的Content-MD5 md5Base64 := base64.StdEncoding.EncodeToString(md5Bytes) resp.HTTPRequest.Header.Set("Content-MD5", md5Base64) url, err := resp.Presign(15 * time.Minute) if err != nil { fmt.Println("error presigning request", err) return } fmt.Printf("curl -XPUT -H \"Content-MD5: %s\" %s --upload-file %s\n\n", md5Base64, url, "testfile.txt") }
额外验证技巧
你可以用openssl命令提前生成正确的Base64编码MD5,和代码输出对比,确保编码正确:
openssl md5 -binary testfile.txt | base64
这个命令的输出应该和代码里的md5Base64完全一致。
关于签名不匹配的补充说明
当你直接用原始十六进制MD5值作为Content-MD5头时,预签名请求会把这个头包含在签名计算里,但实际上传时S3会验证这个头的格式是否符合要求(必须是Base64编码的二进制MD5),同时签名计算的内容和实际请求的有效负载校验不匹配,所以会返回SignatureDoesNotMatch错误。
内容的提问来源于stack exchange,提问作者cvoigt
相关产品推荐
相关产品推荐

