Search code examples
azureazure-storageazure-storage-files

Call Create File on Azure File Service REST Api


I am trying to create a file in Azure File Service: https://learn.microsoft.com/en-us/rest/api/storageservices/create-file

I've seen and read multiple threads about this issue, but I can't figure out what might be the problem in my case... Most likely I am missing some small thing in the request which I cannot find.

The error I am getting: The MAC signature found in the HTTP request (...) is not the same as any computed signature.

The signature string I've used:

PUT
\n
\n
\n
\n
text/plain // Content-Type
\n
\n
\n
\n
\n
\n
x-ms-content-length:1000
x-ms-date:Tue, 20 Apr 2021 19:23:30 GMT
x-ms-type:file
x-ms-version:2015-02-21
/account/share/test

Authorization header:

SharedKey account:XXXXXXXXXX

I've used the HMACSHA256 for hashing the authorization header and signature string, it all works when I use other endpoint (List Shares: https://learn.microsoft.com/en-us/rest/api/storageservices/list-shares), but I can't make it work for the file. I've seen this topic AZURE File Service - Upload PDF through REST API and I believe I am using very similar request, but with no success...

I appreciate any help : )

Edit: I am not sure if I correctly set the content headers. For example, does the x-ms-content-length should be placed in the CanonicalizedHeaders string?

Edit2: With regard to what Ivan Yang wrote, I made the code to work, but only when my CanonicalizedHeaders are built like that:

        CanonicalizedHeaders := 'x-ms-content-length:1200'
                + LF + 'x-ms-date:' + UTCDateTimeText
                + LF + 'x-ms-file-attributes:Archive' + LF + 'x-ms-file-creation-time:Now' + LF + 'x-ms-file-last-write-time:Now' + LF + 'x-ms-file-permission:Inherit'
                + LF + 'x-ms-type:file' + LF + 'x-ms-version:2019-02-02';

If I have them in different order, then it crashes:

     CanonicalizedHeaders := 'x-ms-date:' + UTCDateTimeText + LF +
                                'x-ms-content-length:1200' + LF +
                                'x-ms-version:2019-02-02' + LF +
                                'x-ms-file-attributes:Archive' + LF +
                                'x-ms-file-creation-time:Now' + LF +
                                'x-ms-file-last-write-time:Now' + LF +
                                'x-ms-file-permission:Inherit' + LF +
                                'x-ms-type:file';

How come does this make a difference?


Solution

  • Your signature string is incorrect(you're missing some "\n"). I'm using the x-ms-version:2019-02-02 instead of the older one x-ms-version:2015-02-21, and the correct signature string should look like this one:

               "PUT\n"
                + "\n" // content encoding
                + "\n" // content language
                + "\n" // content length
                + "\n" // content md5
                + content_type  + "\n" // content type
                + "\n" // date
                + "\n" // if modified since
                + "\n" // if match
                + "\n" // if none match
                + "\n" // if unmodified since
                + "\n" // range
                + "x-ms-content-length:" + content_length
                + "\nx-ms-date:" + dt.ToString("R")
                + "\nx-ms-file-attributes:Archive" + "\nx-ms-file-creation-time:Now" + "\nx-ms-file-last-write-time:Now" + "\nx-ms-file-permission:Inherit"
                + "\nx-ms-type:file" + "\nx-ms-version:" + apiversion + "\n" // headers
                + "/{0}/{1}/{2}", Account, FileShare, FileName);
    

    Here is the c# code using create file api:

    using System;
    using System.Globalization;
    using System.Net;
    using System.Security.Cryptography;
    
    namespace ConsoleApp25
    {
        class Program
        {
            static void Main(string[] args)
            {
                string Account = "storage_account_name";
                string Key = "storage_account_key";
                string FileShare = "file_share_name";
                string FileName = "test555.txt";
                string apiversion = "2019-02-02";
                int content_length = 1200;
                string content_type = "text/plain";
    
                DateTime dt = DateTime.UtcNow;
                string StringToSign = String.Format("PUT\n"
                    + "\n" // content encoding
                    + "\n" // content language
                    + "\n" // content length
                    + "\n" // content md5
                    + content_type  + "\n" // content type
                    + "\n" // date
                    + "\n" // if modified since
                    + "\n" // if match
                    + "\n" // if none match
                    + "\n" // if unmodified since
                    + "\n" // range
                    + "x-ms-content-length:" + content_length
                    + "\nx-ms-date:" + dt.ToString("R")
                    + "\nx-ms-file-attributes:Archive" + "\nx-ms-file-creation-time:Now" + "\nx-ms-file-last-write-time:Now" + "\nx-ms-file-permission:Inherit"
                    + "\nx-ms-type:file" + "\nx-ms-version:" + apiversion + "\n" // headers
                    + "/{0}/{1}/{2}", Account, FileShare, FileName);
    
                string auth = SignThis(StringToSign, Key, Account);
                string method = "PUT";
                string urlPath = string.Format("https://{0}.file.core.windows.net/{1}/{2}", Account, FileShare, FileName);
                Uri uri = new Uri(urlPath);
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
                request.Method = method;
                request.ContentLength = 0;
    
                request.Headers.Add("x-ms-content-length", $"{content_length}");
                request.Headers.Add("Content-Type", content_type);
                request.Headers.Add("x-ms-type", "file");
                request.Headers.Add("x-ms-date", dt.ToString("R"));
                request.Headers.Add("x-ms-version", apiversion);
                request.Headers.Add("x-ms-file-attributes", "Archive"); //note it is case-sensitive.
                request.Headers.Add("x-ms-file-permission", "Inherit");
                request.Headers.Add("x-ms-file-creation-time", "Now");
                request.Headers.Add("x-ms-file-last-write-time", "Now");
                request.Headers.Add("Authorization", auth);
               
    
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    //read the response code
                    Console.WriteLine("the response is:" + response.StatusCode);
                }
    
                Console.WriteLine("**completed**");
                Console.ReadLine();
            }
    
            private static String SignThis(String StringToSign, string Key, string Account)
            {
                String signature = string.Empty;
                byte[] unicodeKey = Convert.FromBase64String(Key);
                using (HMACSHA256 hmacSha256 = new HMACSHA256(unicodeKey))
                {
                    Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(StringToSign);
                    signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
                }
    
                String authorizationHeader = String.Format(
                      CultureInfo.InvariantCulture,
                      "{0} {1}:{2}",
                      "SharedKey",
                      Account,
                      signature);
    
                return authorizationHeader;
            }
    
        }
    }