使用 bash、curl 访问 Azure blob 存储

Posted

技术标签:

【中文标题】使用 bash、curl 访问 Azure blob 存储【英文标题】:Accessing Azure blob storage using bash, curl 【发布时间】:2013-12-04 21:03:44 【问题描述】:

我正在尝试使用 REST API 从 bash 脚本中使用 Azure blob 存储服务。我知道可以使用各种其他工具或语言来完成此操作,但我想将其作为 bash 脚本来完成。

以下脚本尝试列出 Azure 存储容器中的 blob。

此脚本会导致身份验证错误。根据 REST API (reference) 文档,签名字符串和标头看起来是正确的。我怀疑问题可能在于处理签名过程的各个部分。

是否有人成功使用 bash 和 curl 访问 Azure 或其他提供商等云存储资源?

#!/bin/bash

# List the blobs in an Azure storage container.

echo "usage: $0##*/ <storage-account-name> <container-name> <access-key>"

storage_account="$1"
container_name="$2"
access_key="$3"

blob_store_url="blob.core.windows.net"
authorization="SharedKey"

request_method="GET"
request_date=$(TZ=GMT date "+%a, %d %h %Y %H:%M:%S %Z")
storage_service_version="2011-08-18"

# HTTP Request headers
x_ms_date_h="x-ms-date:$request_date"
x_ms_version_h="x-ms-version:$storage_service_version"

# Build the signature string
canonicalized_headers="$x_ms_date_h\n$x_ms_version_h"
canonicalized_resource="/$storage_account/$container_name"

string_to_sign="$request_method\n\n\n\n\n\n\n\n\n\n\n\n$canonicalized_headers\n$canonicalized_resource\ncomp:list\nrestype:container"

# Decode the Base64 encoded access key, convert to Hex.
decoded_hex_key="$(echo -n $access_key | base64 -d -w0 | xxd -p -c256)"

# Create the HMAC signature for the Authorization header
signature=$(echo -n "$string_to_sign" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$decoded_hex_key" | sed 's/^.*= //' | base64 -w0)

authorization_header="Authorization: $authorization $storage_account:$signature"

curl \
  -H "$x_ms_date_h" \
  -H "$x_ms_version_h" \
  -H "$authorization_header" \
  "https://$storage_account.$blob_store_url/$container_name?restype=container&comp=list"

更新 - 存储服务错误和脚本生成的相应签名字符串。

以下是存储服务针对 AuthenticationFailed 错误返回的内容。

<?xml version="1.0" encoding="utf-8"?>
<Error>
  <Code>AuthenticationFailed</Code>
  <Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:27e6337e-52f3-4e85-98c7-2fabaacd9ebc
Time:2013-11-21T22:10:11.7029042Z</Message>
  <AuthenticationErrorDetail>The MAC signature found in the HTTP request
'OGYxYjk1MTFkYmNkMCgzN2YzODQwNzcyNiIyYTQxZDg0OWFjNGJiZDlmNWY5YzM1ZWQzMWViMGFjYTAyZDY4NAo='
is not the same as any computed signature. Server used following string to sign:
'GET

x-ms-date:Thu, 21 Nov 2013 22:10:11 GMT
x-ms-version:2011-08-18
/storage_account_name/storage_container
comp:list
restype:container'
  </AuthenticationErrorDetail>
</Error>

接下来是脚本生成的string_to_sign

GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Thu, 21 Nov 2013 22:10:11 GMT\nx-ms-version:2011-08-18\n/storage_account_name/storage_container\ncomp:list\nrestype:container

【问题讨论】:

【参考方案1】:

使用Fiddler(或您平台上的等效项)拦截对 Windows Azure 存储的调用。失败时,这将向您显示存储服务用于验证调用的字符串,您可以将其与您使用的字符串进行比较。

【讨论】:

感谢您的出色建议。由于我正在运行的环境,这样做很困难。如果我的调试仍然没有结果,我会坚持这个建议。【参考方案2】:

查看 REST API 文档和您上面的代码,我认为您构建 canonicalized_resource 字符串的方式存在问题。您缺少该字符串中的查询参数。你的canonicalized_resource 字符串应该是:

canonicalized_resource="/$storage_account/$container_name\ncomp:list\nrestype:container"

【讨论】:

在分配string_to_sign 变量期间正在附加查询参数。我运行脚本时从存储服务返回的身份验证错误包括 Azure 用于创建其授权签名的字符串。当我打印 string_to_sign 变量时,它看起来与 Azure 在错误中返回的字符串完全一样。 对不起,我的错!之前没有看到那个代码。您能否分享您创建的string_to_sign 的输出和Windows Azure 返回的输出。我很确定那里一定有一些小问题。 另一件事是你如何解码你的帐户密钥。来自 C# 世界,我们使用Convert.FromBase64String() 来获取以字节为单位的帐户密钥。这可能是身份验证失败的另一个原因。 我将string_to_sign 的输出添加到原始问题中。我还包含了存储服务作为响应生成的错误 XML。 我最怀疑解码帐户密钥、创建签名、然后重新编码的过程的各个部分。我想我可能会使用另一种语言(可能是 Java)编写一个简单的签名生成器,然后并排运行签名生成以进行比较。【参考方案3】:

看起来 openssl dgst 没有为您生成正确的 HMAC。 我用 C 编写了一个简单的程序,它执行以下操作:

    从命令行获取 base64 编码的密钥并将其解码为二进制。 从标准输入读取字符串以进行签名。 使用 libcrypto HMAC() 例程生成签名。 base64 编码签名并将结果打印到标准输出。

然后,我将脚本中的 openssl dgst 管道替换为对我的程序的调用,它成功了。

请注意,您从 Azure 获得的输出是 XML 包装和 base-64 编码的,因此您需要为其提供某种解析/转换代码。

【讨论】:

请注意 - 我使用 wazproxy 作为参考,您可能想查看一下。在分析任何流量/协议时,Wireshark 也是您最好的朋友。【参考方案4】:

使用 printf 代替 echo(它适用于我)

例如:

签名=printf "$string_to_sign" | openssl dgst -sha256 -mac HMAC -macopt hexkey:$HEXKEY -binary | base64 -w0

【讨论】:

【参考方案5】:

我能够让它工作。 这段代码有两个问题,第一,正如 Patrick Park 所指出的,将echo -n 替换为printf。第二个是用 openssl 上的 -binary 选项替换 sed 魔法。

对比原文:

signature=$(echo -n "$string_to_sign" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$decoded_hex_key" -binary | sed 's/^.*= //' | base64 -w0)

与固定:

signature=$(printf "$string_to_sign" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$decoded_hex_key" -binary |  base64 -w0)

需要回显更改,因为echo -n 不会将\n 转换为实际的换行符。

-binary 更改是必要的,因为即使您剥离了坏部分,openssl 仍然以 ascii-encoded-hex 格式输出签名,而不是二进制格式。所以在传递给base64 之后,结果是十六进制表示的 b64 编码版本,而不是原始值。

【讨论】:

也许你可以在this 上帮助我? 我对此进行了一些修改并添加了对下载的支持。我贴在这里:gist.github.com/jrwren/ff46f4ba177f042ccdc48c080c198f60 decoded_hex_key="$(echo -n $access_key | base64 -d -w0 | xxd -p -c256)" 这个命令给了我 base64: invalid input。有没有办法解决这个问题?仍然遇到同样的错误

以上是关于使用 bash、curl 访问 Azure blob 存储的主要内容,如果未能解决你的问题,请参考以下文章

我如何使用curl请求使用CLI通过yaml文件创建Azure管道?

静默安装Azure CLI

有没有办法在 Azure 数据工厂中使用 cURL 命令?

git-bash 中的 curl 命令

Azure CLI + 使用 oAuth 令牌

azure管道:访问机密变量