不使用SDK为Blob容器创建服务SAS令牌时签名无效问题排查
手动生成基于访问策略的Blob容器服务SAS令牌失败问题
我正尝试遵循Azure官方文档,不使用Azure SDK为Blob容器创建服务SAS令牌。该SAS令牌基于访问策略生成(因此无需指定权限、起始时间、过期时间),但生成的签名始终无效。我已尝试文档中指定的所有版本(从2020-12-06到2024-11-04),但问题仍未解决。
以下是我使用的代码:
package main import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "fmt" "net/url" ) func ComputeHmac256(message string, secret string) string { key := []byte(secret) h := hmac.New(sha256.New, key) h.Write([]byte(message)) return base64.StdEncoding.EncodeToString(h.Sum(nil)) } const accountKey = "xxxxxxx" var signedPermissions = "" var signedStart = "" var signedExpiry = "" var canonicalizedResource = "/blob/storamela/cont1" var signedIdentifier = "allperms" var signedIP = "" var signedProtocol = "https" var signedVersion = "2021-08-06" var signedResource = "c" var signedSnapshotTime = "" var signedEncryptionScope = "" var rscc = "" var rscd = "" var rsce = "" var rscl = "" var rsct = "binary" func main() { message := fmt.Sprintf( "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, signedProtocol, signedVersion, signedResource, signedSnapshotTime, signedEncryptionScope, rscc, rscd, rsce, rscl, rsct, ) secret,_ := base64.StdEncoding.DecodeString(accountKey) // Also tried secret := accountKey sig := ComputeHmac256(message, string(secret)) escapedSig := url.QueryEscape(sig) signedString := fmt.Sprintf("si=%s&spr=%s&sv=%s&sr=%s&sig=%s", signedIdentifier, signedProtocol, signedVersion, signedResource, escapedSig) fmt.Println(signedString) }
问题分析与修复方案
核心问题
- 密钥处理错误:原代码将Base64解码后的账户密钥转成字符串传入HMAC计算,这会破坏原始字节数据(部分字节对应不可打印字符,转字符串时会丢失或变形),导致签名计算错误。
- 函数参数类型不匹配:原
ComputeHmac256函数接收字符串类型的密钥,无法正确处理原始字节数组。
修复后的代码
package main import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "fmt" "net/url" ) // 修改函数参数,直接接收[]byte类型的密钥 func ComputeHmac256(message string, secret []byte) string { h := hmac.New(sha256.New, secret) h.Write([]byte(message)) return base64.StdEncoding.EncodeToString(h.Sum(nil)) } const accountKey = "xxxxxxx" var signedPermissions = "" var signedStart = "" var signedExpiry = "" var canonicalizedResource = "/blob/storamela/cont1" var signedIdentifier = "allperms" var signedIP = "" var signedProtocol = "https" var signedVersion = "2021-08-06" var signedResource = "c" var signedSnapshotTime = "" var signedEncryptionScope = "" var rscc = "" var rscd = "" var rsce = "" var rscl = "" var rsct = "binary" func main() { message := fmt.Sprintf( "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, signedProtocol, signedVersion, signedResource, signedSnapshotTime, signedEncryptionScope, rscc, rscd, rsce, rscl, rsct, ) secret, _ := base64.StdEncoding.DecodeString(accountKey) // 直接传入解码后的字节数组,不再转字符串 sig := ComputeHmac256(message, secret) escapedSig := url.QueryEscape(sig) signedString := fmt.Sprintf("si=%s&spr=%s&sv=%s&sr=%s&sig=%s", signedIdentifier, signedProtocol, signedVersion, signedResource, escapedSig) fmt.Println(signedString) }
额外注意事项
- 确认
canonicalizedResource格式严格为/blob/存储账户名/容器名,确保账户名和容器名与实际资源一致。 - 签名字符串中的空字段必须保留换行(原代码的格式化逻辑已满足此要求)。
内容的提问来源于stack exchange,提问作者redhottsili
相关产品推荐
相关产品推荐

