我一直在测试各种Azure Blob web服务的使用情况(List、Get、Put、Delete(。我在shell脚本上使用cURL的测试成功了。然而,我尝试在Java上模拟这个功能是行不通的。确切地说,Java上生成的签名值与OpenSSL的签名值不匹配。
以下是运行良好的脚本:
#!/bin/bash
# List the blobs in an Azure storage container.
echo "usage: ${0##*/} <storage-account-name> <container-name> <access-key>"
storage_account="ABC"
container_name="XYZ"
access_key="abc=="
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="2015-02-21"
# 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}nnnnnnnnnnnn${canonicalized_headers}n${canonicalized_resource}ncomp:listnrestype:container"
# Decode the Base64 encoded access key, convert to Hex.
decoded_hex_key="$(printf $access_key | base64 -d -w0 | xxd -p -c256)"
# Create the HMAC signature for the Authorization header
signature=$(printf "$string_to_sign" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$decoded_hex_key" -binary | base64 -w0)
authorization_header="Authorization: $authorization $storage_account:$signature"
# -v or --trace to enable tracing
curl -v
-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" -o Azure_ListBlob_Output.dat
这是Java函数,它产生的输出与前者不匹配:
public static String computeHMac256(final String base64Key, final String stringToSign) {
//Signature=Base64(HMAC-SHA256(UTF8(StringToSign), Base64.decode(<your_azure_storage_account_shared_key>)))
try {
byte[] key = Base64.getDecoder().decode(base64Key);
Mac hmacSHA256 = Mac.getInstance("HmacSHA256");
hmacSHA256.init(new SecretKeySpec(key, "HmacSHA256"));
byte[] utf8Bytes = stringToSign.getBytes(StandardCharsets.UTF_8);
byte[] output = hmacSHA256.doFinal(utf8Bytes);
return Base64.getEncoder().encodeToString(output);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
这是stringToSign值,提供给Java函数:
"GET\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"x-ms-date:Tue, 30 Mar 2021 10:19:17 GMT\n" +
"x-ms-version:2015-02-21\n" +
"/ABC/XYZ\n" +
"comp:list\n" +
"restype:container";
当Java生成的签名传递到请求时,Azure会报告以下内容:
HTTP/1.1 403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
从这里获取的java函数有什么不正确的地方吗?请协助。
修正变量stringToSign的以下值,修复了问题:
"GETn" +
"n" +
"n" +
"n" +
"n" +
"n" +
"n" +
"n" +
"n" +
"n" +
"n" +
"n" +
"x-ms-date:Tue, 30 Mar 2021 10:19:17 GMTn" +
"x-ms-version:2015-02-21n" +
"/ABC/XYZn" +
"comp:listn" +
"restype:container
注意换行符从\\n更改为\n
现在,签名值匹配。感谢@GauravMantri伸出援手。