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

不使用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)
}

问题分析与修复方案

核心问题

  1. 密钥处理错误:原代码将Base64解码后的账户密钥转成字符串传入HMAC计算,这会破坏原始字节数据(部分字节对应不可打印字符,转字符串时会丢失或变形),导致签名计算错误。
  2. 函数参数类型不匹配:原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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 17:52:23