azure-blob-storageazuriteazure-emulator

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


Solution

  • 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