This topic describes how to calculate signatures in .NET on the server, configure upload callback, and use form upload to upload data to OSS.

Prerequisites

  • The domain name of the application server is accessible over the Internet.
  • The application server has Visual Studio 2012 or later installed.
  • The application server has .Net Framework 4.5 or later installed.

Step 1: Configure the application server

  1. Download the application server source code that is in .NET.
  2. The Windows system is used in the example. Decompress the source code to the C:\callback-server-dotnet directory.
  3. Go to the directory. Open the TinyHttpServer.cs file that contains the source code. Modify the following snippet:
    // Enter your AccessKey ID.
    public static string accessKeyId = "<yourAccessKeyId>";
    // Enter your AccessKey secret.
    public static string accessKeySecret = "<yourAccessKeySecret>";
    // Set host to a value that is in the format of https://bucketname.endpoint.
    public static string host = "https://bucketname.oss-cn-hangzhou.aliyuncs.com";
    // Set the URL of the server to which an upload callback request is sent. Replace the IP address and port number with your actual information.
    public static string callbackUrl = "http://192.168.0.1:8888";
    // Specify the prefix for the name of the object you want to upload.
    public static string uploadDir = "user-dir-prefix/";
    • accessKeyId: Enter your AccessKey ID.
    • accessKeySecret: Enter your AccessKey secret.
    • host: The format is https://bucketname.endpoint. Example: https://bucket-name.oss-cn-hangzhou.aliyuncs.com. For more information about endpoints, see the "Endpoint" section of the Terms topic.
    • callbackUrl: Set the URL of the server to which an upload callback request is sent. This URL is used to communicate between the application server and OSS. After an object is uploaded, OSS sends the object upload information to the application server by using this URL. In this example, set the URL to String callbackUrl ="http://10.10.10.10:1234";.
    • uploadDir: Specify the prefix of the object to prevent overwriting an existing object with the same name. You can also leave this parameter unspecified.
  4. Open the aliyun-oss-net-callback-server.sln file by using Visual Studio. Click Enable to generate executable file aliyun-oss-net-callback-server.exe.

Step 2: Configure the client

  1. Download the client source code.
  2. Decompress the package to a directory. The D:\aliyun\aliyun-oss-appserver-js directory is used in the example.
  3. Go to the directory. Open the upload.js file. Find the following code:
    // serverUrl specifies the URL of the application server used to obtain information such as the signature and policy. Replace the IP address and port number with your actual information.
    serverUrl = 'http://192.168.0.1:8888'
  4. Set severUrl to the URL of the application server. In this example, specify serverUrl = 'http://10.10.10.10:1234'.

Step 3: Modify CORS configurations

When you use form upload to upload data from the client to OSS, the client includes the Origin header in the request and sends the request to OSS by using the browser. OSS verifies the request message that includes the Origin header for cross-origin resource sharing (CORS) verification. To use the POST method, configure CORS rules for a bucket.

  1. Log on to the OSS console.
  2. In the left-side navigation pane, click Buckets. On the Buckets page, click the name of the desired bucket.
  3. In the left-side navigation tree, choose Content Security > Cross-Origin Resource Sharing (CORS).
  4. On the Cross-Origin Resource Sharing (CORS) page, click Create Rule and configure the parameters as shown in the following figure.
    Important To ensure data security, we recommend that you specify the actual domain name from which you want OSS to allow requests in the Sources field. For more information, see Configure CORS.

Step 4: Send an upload callback request

  1. Start the application server.
    In the command line of the application server, go to the directory where the aliyun-oss-net-callback-server.exe executable file is generated. Run the command aliyun-oss-net-callback-server.exe ${ip} ${port} to start the application server.
    cd C:\Users\Desktop\callback-server-dotnet\aliyun-oss-net-callback-server\bin\Debug\  
    aliyun-oss-net-callback-server.exe 10.10.10.10 1234
    Note
    • The actual directory applies. When you use Visual Studio to generate the aliyun-oss-net-callback-server.exe executable file, you can view the directory.
    • Change ${ip} and ${port} to the IP address and port number of the application server. Example: aliyun-oss-net-callback-server.exe 10.10.10.10 1234.
  2. Start the client.
    1. On the PC, open the index.html file in the directory that contains the client source code.
      Important The index.html file may be incompatible with Internet Explorer 10 or earlier. If you encounter any problems when you use Internet Explorer 10 or earlier, you must perform debugging.
    2. Click Select File. Select the file of a specified type. Click Upload.
      After the object is uploaded, the content returned by the callback server is displayed.

Core code analysis of the application server

The source code of the application server is used to implement the signature-based upload and upload callback.
  • During signature-based upload, the application server responds to the GET message that is sent from the client. An example of the snippet:
    private static string GetPolicyToken()
    {
      //expireTime
      var expireDateTime = DateTime.Now.AddSeconds(expireTime);
    
      // example of policy
      //{
      //  "expiration": "2020-05-01T12:00:00.000Z",
      //  "conditions": [
      //    ["content-length-range", 0, 1048576000]
      //    ["starts-with", "$key", "user-dir-prefix/"]
      //  ]
      //}
    
      //policy
      var config = new PolicyConfig();
      config.expiration = FormatIso8601Date(expireDateTime);
      config.conditions = new List<List<Object>>();
      config.conditions.Add(new List<Object>());
      config.conditions[0].Add("content-length-range");
      config.conditions[0].Add(0);
      config.conditions[0].Add(1048576000);
      config.conditions.Add(new List<Object>());
      config.conditions[1].Add("starts-with");
      config.conditions[1].Add("$key");
      config.conditions[1].Add(uploadDir);
    
      var policy = JsonConvert.SerializeObject(config);
      var policy_base64 = EncodeBase64("utf-8", policy);
      var signature = ComputeSignature(accessKeySecret, policy_base64);
    
      //callback
      var callback = new CallbackParam();
      callback.callbackUrl = callbackUrl;
      callback.callbackBody = "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}";
      callback.callbackBodyType = "application/x-www-form-urlencoded";
    
      var callback_string = JsonConvert.SerializeObject(callback);
      var callback_string_base64 = EncodeBase64("utf-8", callback_string);
    
      var policyToken = new PolicyToken();
    
      policyToken.accessid = accessKeyId;
      policyToken.host = host;
      policyToken.policy = policy_base64;
      policyToken.signature = signature;
      policyToken.expire = ToUnixTime(expireDateTime);
      policyToken.callback = callback_string_base64;
      policyToken.dir = uploadDir;
    
      return JsonConvert.SerializeObject(policyToken);
    }
    
    public void DoGet()
    {
      Console.WriteLine("DoGet request: {0}", this.httpURL);
      var content = GetPolicyToken();
      this.swResponse.WriteLine("HTTP/1.0 200 OK");
      this.swResponse.WriteLine("Content-Type: application/json"); 
      this.swResponse.WriteLine("Access-Control-Allow-Origin: *");
      this.swResponse.WriteLine("Access-Control-Allow-Method: GET, POST");
      this.swResponse.WriteLine($"Content-Length: {content.Length.ToString()}");
      this.swResponse.WriteLine("Connection: close");
      this.swResponse.WriteLine("");
      this.swResponse.WriteLine(content);
    }
  • During upload callback, the application server responds to the POST message that is sent from OSS. An example of the snippet:
    public bool VerifySignature()
    {
      // Get the Authorization Base64 from Request
      if (this.httpHeadersDict["authorization"] != null)
      {
        this.strAuthorizationRequestBase64 = this.httpHeadersDict["authorization"].ToString();
      } else if (this.httpHeadersDict["Authorization"] != null) {
        this.strAuthorizationRequestBase64 = this.httpHeadersDict["Authorization"].ToString();
      }
      if (this.strAuthorizationRequestBase64 == "")
      {
        Console.WriteLine("authorization property in the http request header is null. ");
        return false;
      }
    
      // Decode the Authorization from Request
      this.byteAuthorizationRequest = Convert.FromBase64String(this.strAuthorizationRequestBase64);
    
      // Decode the URL of PublicKey
      this.strPublicKeyURLBase64 = this.httpHeadersDict["x-oss-pub-key-url"].ToString();
      var bytePublicKeyURL = Convert.FromBase64String(this.strPublicKeyURLBase64);
      var strAsciiPublickeyURL = System.Text.Encoding.ASCII.GetString(bytePublicKeyURL);
    
      // Get PublicKey from the URL
      ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(validateServerCertificate);
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(strAsciiPublickeyURL);
      HttpWebResponse response = (HttpWebResponse)request.GetResponse();
      StreamReader srPublicKey = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
      this.strPublicKeyBase64 = srPublicKey.ReadToEnd();
      response.Close();
      srPublicKey.Close();
      this.strPublicKeyContentBase64 = this.strPublicKeyBase64.Replace("-----BEGIN PUBLIC KEY-----\n", "").Replace("-----END PUBLIC KEY-----", "").Replace("\n", "");
      this.strPublicKeyContentXML = this.RSAPublicKeyString2XML(this.strPublicKeyContentBase64);
    
      // Generate the New Authorization String according to the HttpRequest
      String[] arrURL;
      if (this.httpURL.Contains('?'))
      {
        arrURL = this.httpURL.Split('?') ;
        this.strAuthSourceForMD5 = String.Format("{0}?{1}\n{2}", System.Web.HttpUtility.UrlDecode(arrURL[0]), arrURL[1], this.httpBody);
      }
      else
      {
        this.strAuthSourceForMD5 = String.Format("{0}\n{1}", System.Web.HttpUtility.UrlDecode(this.httpURL), this.httpBody);
      }
    
      // MD5 hash bytes from the New Authorization String 
      var byteAuthMD5 = byteMD5Encrypt32(this.strAuthSourceForMD5);
    
      // Verify Signature
      System.Security.Cryptography.RSACryptoServiceProvider RSA = new System.Security.Cryptography.RSACryptoServiceProvider();
      try
      {
        RSA.FromXmlString(this.strPublicKeyContentXML);
      }
      catch (System.ArgumentNullException e)
      {
        throw new ArgumentNullException(String.Format("VerifySignature Failed : RSADeformatter.VerifySignature get null argument : {0} .", e));
      }
      catch (System.Security.Cryptography.CryptographicException e)
      {
        throw new System.Security.Cryptography.CryptographicException(String.Format("VerifySignature Failed : RSA.FromXmlString Exception : {0} .", e));
      }
      System.Security.Cryptography.RSAPKCS1SignatureDeformatter RSADeformatter = new System.Security.Cryptography.RSAPKCS1SignatureDeformatter(RSA);
      RSADeformatter.SetHashAlgorithm("MD5");
    
      var bVerifyResult = false;
      try
      {
        bVerifyResult = RSADeformatter.VerifySignature(byteAuthMD5, this.byteAuthorizationRequest);
      }
      catch (System.ArgumentNullException e)
      {
        throw new ArgumentNullException(String.Format("VerifySignature Failed : RSADeformatter.VerifySignature get null argument : {0} .", e));
      }
      catch (System.Security.Cryptography.CryptographicUnexpectedOperationException e)
      {
        throw new System.Security.Cryptography.CryptographicUnexpectedOperationException(String.Format("VerifySignature Failed : RSADeformatter.VerifySignature Exception : {0} .", e));
      }
    
      return bVerifyResult;
    }
    
    public void DoPost()
    {
      this.GetPostBody();
    
      // Verify Signature
      try
      {
        if (this.VerifySignature())
        {
          Console.WriteLine("\nVerifySignature Successful . \n");
    
          // do something according to callback_body ... 
    
          this.HttpResponseSuccess();
        }
        else
        {
          Console.WriteLine("\nVerifySignature Failed . \n");
          this.HttpResponseFailure();
        }
      }
      catch
      {
        Console.WriteLine("\nVerifySignature Failed . \n");
        this.HttpResponseFailure();
      }
    }