LoginSignup
5
5

More than 3 years have passed since last update.

Azure StorageでのRest APIの署名のつくりかた(with パケットキャプチャ)

Last updated at Posted at 2020-03-08

本記事は、Azure StorageでのRest APIの署名作成についての調査結果をまとめたものになります。

前提

  • ストレージアカウント等は、本記事中では以下の識別子を使用しています。
項目 本記事での識別子
ストレージアカウント mystorageaccount
コンテナ名 mycontainer
PUT/GET/DELETEするBlob名 sample.txt
  • オペレーションごとの、リクエストヘッダおよびStringToSignに必要なヘッダは以下の通りです。 (〇:必須 -:不要)
Request Header Put Blob Get Blob Detele Blob List Blobs
Content-Length - - -
x-ms-date (or Date)
x-ms-version
x-ms-blob-type - - -
  • リクエストヘッダにはx-ms-dateDateのうち少なくとも一方が存在する必要があります。リクエストヘッダにいずれか一方のみが存在する場合、StringToSignにはリクエストヘッダに存在する方のみを記述し、存在しない方は記述しません。リクエストヘッダに両方が同時に存在する場合、StringToSignにはx-ms-dateのみ記述します。
リクエストヘッダ StringToSignへの記述
x-ms-dateのみ存在 x-ms-dateのみ記述する
Dateのみ存在 Dateのみ記述する
上2つが同時に存在 x-ms-dateのみ記述する
  • 本記事に出てくる「アクセスキー」は、Azureポータルの以下画面の「キー」で確認できます。 ([ホーム] > [ストレージアカウント]でストレージアカウントを選択 > [アクセスキー])

image.png

  • アクセスキーは、Base64エンコードされた文字列です。本記事では、例示のためにVGhpcyBpcyBzYW1wbGUgb2YgQXp1cmUgU3RvcmFnZSBBY2Nlc3MgS2V5IHN0cmluZyBCYXNlNjQgRW5jb2RlZA==というキーを使用していますが、これは実在しないキーです。くれぐれも本物のアクセスキーをソースコードにハードコーディングしないようにご注意下さい。

  • 本記事のサンプルコードはC#で記述していますが、C#特有の文法は使用していないため、C#未経験者でも内容が直観的に理解できるコードになっています。念のため説明しておきますと、string s = "abc";はString型(文字列型)の変数sを宣言し、その値を文字列"abc"で初期化するという意味です。また、byte[] stringToSign_bytes;は、バイト配列型の変数stringToSign_bytesを宣言するという意味です。

署名のつくりかた

おおまかな流れとしては、以下となります。

  1. StringToSignを作成する。
  2. アクセスキー(AccessKey)をキーとして、StringToSignのHMAC-SHA256ハッシュ値を算出する。
  3. 2で算出したHMAC-SHA256ハッシュ値をBase64エンコードした文字列を、リクエストヘッダのAuthoraizationに設定する。

AWS S3のSignature V4の手順と比べると、Azure Storaogeの手順はかなりシンプルで助かります。

以下では、Put/Get/List/Deleteの各オペレーションでの署名の作成手順について解説します。

Put Blob

StringToSignを作成します。

  • StringToSignの末尾(CanonicalResourceの末尾)に改行文字はつけません。
  • HTTPヘッダとStringToSignにおいて、リクエストヘッダ Content-Length, x-ms-blob-typex-ms-dateまたはDatex-ms-versionが必須です。
  • CanonicalHeadersのキー名は小文字です。
  • CanonicalHeadersの並び順は、キー名小文字での辞書順(昇順)です。
StringToSign
string StringToSign =
    // GeneralHeaders
    "PUT\n" +  // Verb
    "\n" +     // Content-Encoding
    "\n" +     // Content-Language
    "4\n" +    // Content-Length (Required)
    "\n" +     // Content-MD5
    "\n" +     // Content-Type
    "\n" +     // Date
    "\n" +     // If-Modified-Since
    "\n" +     // If-Match
    "\n" +     // If-Non-Match
    "\n" +     // If-Unmodified-Since
    "\n" +     // Range
    // CanonicalHeaders
    "x-ms-blob-type:BlockBlob\n" +
    "x-ms-date:Sun, 08 Mar 2020 03:39:02 GMT\n" +
    "x-ms-version:2017-07-29\n" +
    // CanonicalResource
    "/mystorageaccount/mycontainer/sample.txt";

作成したStringToSignと、アクセスキーをインプットとしてして、HMAC-SHA256のハッシュ値を算出します。
算出したハッシュ値(バイト配列)を、Base64エンコードして文字列にします。

(以下のコード例では、例示のためアクセスキーをソースコードにハードコーディングしていますが、これは実在しないキーです。くれぐれも本物のアクセスキーをソースコードに記述しないよう、ご注意下さい)

Signature
    string AccessKey = "VGhpcyBpcyBzYW1wbGUgb2YgQXp1cmUgU3RvcmFnZSBBY2Nlc3MgS2V5IHN0cmluZyBCYXNlNjQgRW5jb2RlZA==";
    byte[] accessKey_bytes = Base64.DecodeFromBase64String(AccessKey);

    byte[] stringToSign_bytes = UTF8.GetBytes(StringToSign);
    byte[] signature_bytes = HMAC_SHA256.ComputeHash(stringToSign_bytes, accessKey_bytes);
    string signature_base64 = Base64.EncodeToBase64String(signature_bytes);

    string Signature = signature_base64;
    // ex) Signature is "QmFzZTY044OH44Kz44O844OJ44GX44G+44GX44GfPw=="

完成したSharedKeyを、HTTP RequestのAuthorizationヘッダにセットします。

SharedKey
    string SharedKey = "SharedKey mystorageaccount:" + Signature;
    // ex) SharedKey is "SharedKey mystorageaccount:QmFzZTY044OH44Kz44O844OJ44GX44G+44GX44GfPw=="
    httpRequestHeaders.Authorization = new AuthorizationHeaderValue(SharedKey);

最終的に、Authorizationヘッダは以下のようになります。

Authorizationヘッダ(例)
Authorization: SharedKey mystorageaccount:QmFzZTY044OH44Kz44O844OJ44GX44G+44GX44GfPw==

Get Blob

  • HTTPヘッダとStringToSignにおいて、リクエストヘッダ x-ms-date(またはDate)、x-ms-versionが必須です。
StringToSign
string StringToSign =
    // GeneralHeaders
    "GET\n" +  // Verb
    "\n" +     // Content-Encoding
    "\n" +     // Content-Language
    "\n" +     // Content-Length
    "\n" +     // Content-MD5
    "\n" +     // Content-Type
    "\n" +     // Date
    "\n" +     // If-Modified-Since
    "\n" +     // If-Match
    "\n" +     // If-Non-Match
    "\n" +     // If-Unmodified-Since
    "\n" +     // Range
    // CanonicalHeaders
    "x-ms-date:Sun, 08 Mar 2020 03:39:02 GMT\n" +
    "x-ms-version:2017-07-29\n" +
    // CanonicalResource
    "/mystorageaccount/mycontainer/sample.txt";

以降の手順はPut Blobと同じです。

List Blobs

  • CanoncalResource末尾にクエリー文字列がつきます。
  • StringToSignの末尾(restype:containerの末尾)に改行文字はつきません。
  • HTTPヘッダとStringToSignにおいて、リクエストヘッダ x-ms-dateまたはDatex-ms-versionが必須です。
  • CanonicalResourceに、クエリー文字列(comp=list&restype=container)をkey:valueの形式でリスト化した文字列が必須です。
  • CanonicalResourceのクエリーの名前(comprestype)は小文字です。
  • CanonicalResourceのクエリーの並び順は、キー名小文字での辞書順(昇順)です。
StringToSign
string StringToSign =
    // GeneralHeaders
    "GET\n" +  // Verb
    "\n" +     // Content-Encoding
    "\n" +     // Content-Language
    "\n" +     // Content-Length
    "\n" +     // Content-MD5
    "\n" +     // Content-Type
    "\n" +     // Date
    "\n" +     // If-Modified-Since
    "\n" +     // If-Match
    "\n" +     // If-Non-Match
    "\n" +     // If-Unmodified-Since
    "\n" +     // Range
    // CanonicalHeaders
    "x-ms-date:Sun, 08 Mar 2020 03:39:02 GMT\n" +
    "x-ms-version:2017-07-29\n" +
    // CanonicalResource
    "/mystorageaccount/mycontainer\n" +
    "comp:list\n" +
    "restype:container";

以降の手順はPut Blobと同様です。

Delete Blob

  • HTTPヘッダとStringToSignにおいて、リクエストヘッダ x-ms-date(またはDate)、x-ms-versionが必須です。
StringtoSign
string StringToSign =
    "DELETE\n" +  // Verb
    // GeneralHeaders
    "\n" +     // Content-Encoding
    "\n" +     // Content-Language
    "\n" +     // Content-Length
    "\n" +     // Content-MD5
    "\n" +     // Content-Type
    "\n" +     // Date
    "\n" +     // If-Modified-Since
    "\n" +     // If-Match
    "\n" +     // If-Non-Match
    "\n" +     // If-Unmodified-Since
    "\n" +     // Range
    // CanonicalHeaders
    "x-ms-date:Sun, 08 Mar 2020 03:39:02 GMT\n" +
    "x-ms-version:2017-07-29\n" +
    // CanonicalResource
    "/mystorageaccount/mycontainer/sample.txt";

以降の手順はPut Blobと同じです。

【参考】実際のパケットキャプチャ

参考情報として、各操作でのパケットキャプチャを乗せておきます。

Put Blob

Request
PUT /mycontainer/sample.txt HTTP/1.1
Authorization: SharedKey mystorageaccount:QmFzZTY044OH44Kz44O844OJ44GX44G+44GX44GfPw==
x-ms-date: Sun, 08 Mar 2020 04:46:28 GMT
x-ms-version: 2017-07-29
x-ms-blob-type: BlockBlob
Host: mystorageaccount.blob.core.windows.net
Content-Length: 4
Expect: 100-continue
Connection: Keep-Alive

hoge

HTTPレスポンスのContent-MD5には、リクエストのメッセージボディ(hoge)のMD5ハッシュ値をBase64エンコードした文字列(6nA+eqHv2gBk6qUH2eirfg==)がセットされます。

Response
HTTP/1.1 201 Created
Content-Length: 0
Content-MD5: 6nA+eqHv2gBk6qUH2eirfg==
Last-Modified: Sun, 08 Mar 2020 04:46:27 GMT
ETag: "0x8D7C31BA9E7D293"
Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: d738ae8f-101e-0058-4404-f51de0000000
x-ms-version: 2017-07-29
x-ms-request-server-encrypted: true
Date: Sun, 08 Mar 2020 04:46:26 GMT

Get Blob

Request
GET /mycontainer/sample.txt HTTP/1.1
x-ms-date: Sun, 08 Mar 2020 04:46:28 GMT
x-ms-version: 2017-07-29
Authorization: SharedKey mystorageaccount:QmFzZTY044OH44Kz44O844OJ44GX44G+44GX44GfPw==
Host: mystorageaccount.blob.core.windows.net

HTTPレスポンスのContent-MD5には、レスポンスのメッセージボディ(hoge)のMD5ハッシュ値をBase 64エンコードした文字列(6nA+eqHv2gBk6qUH2eirfg==)がセットされます。

Response
HTTP/1.1 200 OK
Content-Length: 4
Content-Type: application/octet-stream
Content-MD5: 6nA+eqHv2gBk6qUH2eirfg==
Last-Modified: Sun, 08 Mar 2020 04:46:27 GMT
Accept-Ranges: bytes
ETag: "0x8D7C31BA9E7D293"
Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: d738aeb5-101e-0058-6304-f51de0000000
x-ms-version: 2017-07-29
x-ms-lease-status: unlocked
x-ms-lease-state: available
x-ms-blob-type: BlockBlob
x-ms-server-encrypted: true
Date: Sun, 08 Mar 2020 04:46:27 GMT

hoge

List Blobs

Request
GET /mycontainer?restype=container&comp=list HTTP/1.1
x-ms-date: Sun, 08 Mar 2020 04:46:28 GMT
x-ms-version: 2017-07-29
Authorization: SharedKey mystorageaccount:QmFzZTY044OH44Kz44O844OJ44GX44G+44GX44GfPw==
Host: mystorageaccount.blob.core.windows.net
Response(ResponseHeader)
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/xml
Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: d738aee2-101e-0058-0b04-f51de0000000
x-ms-version: 2017-07-29
Date: Sun, 08 Mar 2020 04:46:27 GMT
Response(MessageBody)
<?xml version="1.0" encoding="utf-8"?>
<EnumerationResults ServiceEndpoint="http://mystorageaccount.blob.core.windows.net/" ContainerName="mycontainer">
  <Blobs>
    <Blob>
      <Name>sample.txt</Name>
      <Properties>
        <Last-Modified>Sun, 08 Mar 2020 04:46:27 GMT</Last-Modified>
        <Etag>0x8D7C31BA9E7D293</Etag>
        <Content-Length>4</Content-Length>
        <Content-Type>application/octet-stream</Content-Type>
        <Content-Encoding />
        <Content-Language />
        <Content-MD5>6nA+eqHv2gBk6qUH2eirfg==</Content-MD5>
        <Cache-Control />
        <Content-Disposition />
        <BlobType>BlockBlob</BlobType>
        <AccessTier>Hot</AccessTier>
        <AccessTierInferred>true</AccessTierInferred>
        <LeaseStatus>unlocked</LeaseStatus>
        <LeaseState>available</LeaseState>
        <ServerEncrypted>true</ServerEncrypted>
      </Properties>
    </Blob>
  </Blobs>
  <NextMarker />
</EnumerationResults>

Delete Blob

Request
DELETE /mycontainer/sample.txt HTTP/1.1
x-ms-date: Sun, 08 Mar 2020 04:46:28 GMT
x-ms-version: 2017-07-29
Authorization: SharedKey mystorageaccount:QmFzZTY044OH44Kz44O844OJ44GX44G+44GX44GfPw==
Host: mystorageaccount.blob.core.windows.net
Content-Length: 0
Response
HTTP/1.1 202 Accepted
Content-Length: 0
Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: d738af03-101e-0058-2904-f51de0000000
x-ms-version: 2017-07-29
x-ms-delete-type-permanent: true
Date: Sun, 08 Mar 2020 04:46:27 GMT

参考

Blob service REST API (公式)
Microsoft Azure~REST API 操作やってみた~ …  PHPでの実装例です。とても分かりやすくて、何度も参考にさせていただきました。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5