entropy283
entropy283

Reputation: 97

Azurite Shared key generation for PUT operation fails with 403

I am trying to make a PUT blob request using azurite. Using the following values.

 account = "devstoreaccount1"
 key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" 
 api_version = "2021-06-08"
 block_type = "BlockBlob"

the string I am trying to sign then becomes this:

string_to_sign = (f"PUT\n"  # HTTP method
                     f"\n"  # Content-Encoding
                     f"\n"  # Content-Language
                     f"0\n"  # Content-Length
                     f"\n"  # Content-MD5
                     f"\n"  # Content-Type
                     f"\n"  # Date
                     f"\n"  # If-Modified-Since
                     f"\n"  # If-Match
                     f"\n"  # If-None-Match
                     f"\n"  # If-Unmodified-Since
                     f"\n"  # Range
                     f"x-ms-date:{date_str}\n"
                     f"x-ms-version:{api_version}\n"
                     f"x-ms-blob-type:{block_type}\n"
                     f"/{account}/{container}/{blob}")

but when making the requests I get a 403 error (server failed to authenticate) everytime. anyone know what I am missing?

the error I get is:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Error>
    <Code>AuthorizationFailure</Code>
    <Message>Server failed to authenticate the request. Make sure the value of the Authorization header is formed correctly including the signature.
RequestId:35ee2117-c03a-417d-84dd-be4a0b4d1163
Time:2024-10-29T16:04:11.193Z</Message>
</Error>

The python code I am using

import hmac
import hashlib
import base64
from datetime import datetime

def main():
    authorization, date, api_version = blobs()

    print(f"Authorization: {authorization}")
    print(f"Date: {date}")
    print(f"API Version: {api_version}")
    input("Press any key to exit...")


def blobs():
    account = "devstoreaccount1"
    key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="  # Replace with your access key
    container = "container" 
    blob = "file.txt" 
    api_version = "2021-06-08"
    block_type = "BlockBlob"

    dt = datetime.utcnow()
    date_str = dt.strftime('%a, %d %b %Y %H:%M:%S GMT')

    string_to_sign = (f"PUT\n"  # HTTP method
                     f"\n"  # Content-Encoding
                     f"\n"  # Content-Language
                     f"0\n"  # Content-Length
                     f"\n"  # Content-MD5
                     f"\n"  # Content-Type
                     f"\n"  # Date
                     f"\n"  # If-Modified-Since
                     f"\n"  # If-Match
                     f"\n"  # If-None-Match
                     f"\n"  # If-Unmodified-Since
                     f"\n"  # Range
                     f"x-ms-date:{date_str}\n"
                     f"x-ms-version:{api_version}\n"
                     f"x-ms-blob-type:{block_type}\n"
                     f"/{account}/{account}/{container}/{blob}")

    signature = sign_this(string_to_sign, key)

    # Updated Authorization format
    authorization = f"SharedKey {account}:{signature}"

    return authorization, date_str, api_version


def sign_this(string_to_sign, key):
    decoded_key = base64.b64decode(key)
    string_to_sign = string_to_sign.encode('utf-8')

    hmac_sha256 = hmac.new(decoded_key, string_to_sign, hashlib.sha256)
    signature = base64.b64encode(hmac_sha256.digest()).decode('utf-8')

    return signature


if __name__ == "__main__":
    main()

I have then been taking the output of this and replacing the it with the value of the Authorization header in postman

Upvotes: 0

Views: 116

Answers (2)

Venkatesan
Venkatesan

Reputation: 10515

Azurite Shared key generation for PUT operation fails with 403

You can use the below code that upload the file to your attached emulator with azurite using Python alone without postman.

Code:

import hmac
import hashlib
import base64
from datetime import datetime
import requests
import os

def main():
    file_path = <path of your file> 
    with open(file_path, 'rb') as file:
        blob_content = file.read()
    content_length = str(len(blob_content))
    
    authorization, date, api_version = generate_authorization(content_length, file_path)

    account = "devstoreaccount1"
    container = <container name>
    blob = os.path.basename(file_path) 
    url = f"http://127.0.0.1:10000/{account}/{container}/{blob}"  

    headers = {
        "Authorization": authorization,
        "x-ms-date": date,
        "x-ms-version": api_version,
        "x-ms-blob-type": "BlockBlob",  # Specify blob type as BlockBlob
        "Content-Length": content_length,
        "Content-Type": "application/octet-stream"  
    }

    print("Request Headers:")
    for key, value in headers.items():
        print(f"{key}: {value}")

    response = requests.put(url, headers=headers, data=blob_content)

    print(f"\nStatus Code: {response.status_code}")
    print(f"Response: {response.text}")

    print("\nResponse Headers:")
    for key, value in response.headers.items():
        print(f"{key}: {value}")


def generate_authorization(content_length, file_path):
    account = "devstoreaccount1"
    key = "xxxxxxx" 
    container = "source-container"
    blob = os.path.basename(file_path)  
    api_version = "2021-06-08"

    dt = datetime.utcnow()
    date_str = dt.strftime('%a, %d %b %Y %H:%M:%S GMT')

    string_to_sign = (f"PUT\n"  # HTTP method
                      f"\n"  # Content-Encoding
                      f"\n"  # Content-Language
                      f"{content_length}\n"  # Content-Length
                      f"\n"  # Content-MD5
                      f"application/octet-stream\n"  # Content-Type
                      f"\n"  # Date
                      f"\n"  # If-Modified-Since
                      f"\n"  # If-Match
                      f"\n"  # If-None-Match
                      f"\n"  # If-Unmodified-Since
                      f"\n"  # Range
                      f"x-ms-blob-type:BlockBlob\n"
                      f"x-ms-date:{date_str}\n"
                      f"x-ms-version:{api_version}\n"
                      f"/{account}/{account}/{container}/{blob}")  # Update path format

    signature = sign_this(string_to_sign, key)
    authorization = f"SharedKey {account}:{signature}"

    return authorization, date_str, api_version

def sign_this(string_to_sign, key):
    decoded_key = base64.b64decode(key)
    string_to_sign = string_to_sign.encode('utf-8')
    
    hmac_sha256 = hmac.new(decoded_key, string_to_sign, hashlib.sha256)
    signature = base64.b64encode(hmac_sha256.digest()).decode('utf-8')

    return signature

if __name__ == "__main__":
    main()

Output:

Request Headers:
Authorization: SharedKey devstoreaccount1:IDUuFS55xxxxxxx
x-ms-date: Wed, 30 Oct 2024 04:20:06 GMT
x-ms-version: 2021-06-08
x-ms-blob-type: BlockBlob
Content-Length: 88731
Content-Type: application/octet-stream

Status Code: 201
Response: 

Response Headers:
Server: Azurite-Blob/3.31.0
etag: "0xxx49A0"
last-modified: Wed, 30 Oct 2024 04:20:06 GMT
content-md5: 0wxxx0Z/aA==
x-ms-request-id: 4fc5dxxxx8-88aa-a3baba7b621c
x-ms-version: 2024-08-04
date: Wed, 30 Oct 2024 04:20:06 GMT
x-ms-request-server-encrypted: true
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 0

enter image description here

Storage explorer: enter image description here

Upvotes: 1

Gaurav Mantri
Gaurav Mantri

Reputation: 136366

Based on the documentation here, you will need to add storage account name twice when creating a string to sign for storage emulator resource.

enter image description here

Please try something like the following:

string_to_sign = (f"PUT\n"  # HTTP method
                     f"\n"  # Content-Encoding
                     f"\n"  # Content-Language
                     f"0\n"  # Content-Length
                     f"\n"  # Content-MD5
                     f"\n"  # Content-Type
                     f"\n"  # Date
                     f"\n"  # If-Modified-Since
                     f"\n"  # If-Match
                     f"\n"  # If-None-Match
                     f"\n"  # If-Unmodified-Since
                     f"\n"  # Range
                     f"x-ms-date:{date_str}\n"
                     f"x-ms-version:{api_version}\n"
                     f"x-ms-blob-type:{block_type}\n"
                     f"/{account}/{account}/{container}/{blob}")

Upvotes: 0

Related Questions