All Products
Search
Document Center

Object Storage Service:Example of uploading signature Header in OSS using C# language

Last Updated:Mar 20, 2026

This example shows how to upload an object to Object Storage Service (OSS) using the PutObject API from C# with a manually computed HMAC-SHA1 Authorization header, without using the OSS SDK.

For production use, use the OSS SDK, which handles signing automatically. This example is for cases where you need direct REST API access or want to understand the signing mechanism.

Prerequisites

Before you begin, ensure that you have:

  • An Alibaba Cloud account with an AccessKey ID and AccessKeySecret

  • An OSS bucket

  • Visual Studio 2017 with .NET Framework 4.0

How it works

The signing flow has three stages:

  1. Build the StringToSign — Construct a canonical string from the HTTP method, headers, and resource path.

  2. Compute the signature — Sign the StringToSign with your AccessKeySecret using HMAC-SHA1, then Base64-encode the result.

  3. Send the request — Include the signature in the Authorization header and send the PUT request to OSS.

Stage 1: Build the StringToSign

The StringToSign for a PutObject request follows this format:

PUT\n
\n
{Content-Type}\n
{Date (GMT)}\n
/{BucketName}/{ObjectName}
FieldRequiredDescriptionExample
HTTP verbYesThe HTTP method in uppercasePUT
Content-MD5NoMD5 hash of the request body. Leave blank if not provided — the field must still appear as an empty line.*(empty)*
Content-TypeYesMIME type of the objectapplication/json
DateYesCurrent time in GMT (RFC 1123 format). OSS rejects requests where the timestamp differs from server time by more than 15 minutes.Thu, 19 Mar 2026 14:00:00 GMT
Canonicalized resourceYes/BucketName/ObjectName/mybucket/mytest/1.txt

The StringToSign for this example is:

string str = varb + "\n\n" + content_type + "\n" + timeGmt + "\n/" + BucketName + "/" + objectName;
The \n\n represents the HTTP verb line ending followed by an empty Content-MD5 field. Both newlines are required — omitting either one produces a wrong signature and a 403 error.

Stage 2: Compute the signature

Sign the StringToSign using HMAC-SHA1 with your AccessKeySecret, then Base64-encode the result:

public static string HmacSha1Sign(string secret, string strOrgData)
{
    var hmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(secret));
    var dataBuffer = Encoding.UTF8.GetBytes(strOrgData);
    var hashBytes = hmacsha1.ComputeHash(dataBuffer);
    return Convert.ToBase64String(hashBytes);
}

The resulting Authorization header has this format:

Authorization: OSS {AccessKey}:{Signature}

For example:

Authorization: OSS <your-access-key>:<base64-encoded-hmac-sha1-signature>

Stage 3: Send the request

The following example sends a PutObject request with a computed Authorization header. It was tested with Visual Studio 2017 and .NET Framework 4.0.

Replace the placeholder values before running:

PlaceholderDescriptionExample
AccessKeyYour Alibaba Cloud AccessKey IDLTAI5tXxx
AccessKeySecretYour Alibaba Cloud AccessKeySecret*(your secret)*
EndpointOSS endpoint for the bucket's regionoss-cn-hangzhou.aliyuncs.com
BucketNameName of your OSS bucketmy-bucket
objectNamePath and name of the object to createmytest/1.txt
using System;
using System.Security.Cryptography;
using System.Text;
using System.Net;
using System.IO;
using System.Reflection;


namespace Aliyun.OSS.Samples
{
    class Program
    {



        //Your AccessKey
        public static string AccessKey = "xx";
        //Your AccessKeySecret
        public static string AccessKeySecret = "xx";
        //bucket endpoint
        public static string Endpoint = "oss-cn-hangzhou.aliyuncs.com";
        public static string BucketName = "xx";
        public static string objectName = "mytest/1.txt";
        public static string HmacSha1Sign(string secret, string strOrgData)
        {
            var hmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(secret));
            var dataBuffer = Encoding.UTF8.GetBytes(strOrgData);
            var hashBytes = hmacsha1.ComputeHash(dataBuffer);
            return Convert.ToBase64String(hashBytes);
        }


        public static string HttpRequest(string url, string data, string sign, string contentType, string time1)
        {


            byte[] datas = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(data);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "PUT";
            request.Timeout = 150000;
            request.Headers.Add("Authorization", "OSS " + AccessKey + ":" + sign);
            CallPrivateMethod(request, "SetSpecialHeaders", "Date", time1);
            request.ContentType = contentType;
            Stream requestStream = null;
            string responseStr = null;
            try
            {
                if (datas != null)
                {
                    request.ContentLength = datas.Length;
                    requestStream = request.GetRequestStream();
                    requestStream.Write(datas, 0, datas.Length);
                    requestStream.Close();
                }
                else
                {
                    request.ContentLength = 0;
                }
                HttpWebResponse response = request.GetResponse() as HttpWebResponse;
                responseStr = response.Headers.GetValues("x-oss-request-id")[0];
                //responseStr = sr.ReadToEnd();
            }
            catch (Exception)
            {
                Console.WriteLine("error");
            }
            finally
            {
                request = null;
                requestStream = null;
            }
            return responseStr;
        }


        public static string ToGMTString()
        {
            return DateTime.Now.ToUniversalTime().ToString("r");
        }
        public static void CallPrivateMethod(object instance, string name, params object[] param)
        {
            BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
            Type type = instance.GetType();
            MethodInfo method = type.GetMethod(name, flag);
            method.Invoke(instance, param);
        }


        static void Main(string[] args)
        {


            string varb = "PUT";
            string content_type = "application/json";
            string timeGmt = ToGMTString();
            string str = varb + "\n\n" + content_type + "\n" + timeGmt + "\n/" + BucketName + "/" + objectName;
            string signature = HmacSha1Sign(AccessKeySecret, str);
            string url = "http://" + BucketName + "." + Endpoint + "/" + objectName;
            string data = "{ \"key\":\"this is a oss's test\"}";
            string result = HttpRequest(url, data, signature, content_type, timeGmt);
            Console.WriteLine("requestId:  " + result);




        }
    }
}

A successful upload prints the x-oss-request-id value from the response. If the call fails, the console prints error — check the Authorization header construction and confirm the Date header is within 15 minutes of OSS server time.

The Date header is set via reflection using CallPrivateMethod because .NET's HttpWebRequest does not expose the Date header through its public API.

What's next