English
English
简体中文
Contact Us
Register
Log In
layoutIndex

Sign Requests

Last Updated on : 2022-09-13 07:43:25download

When you make requests to Tuya’s OpenAPI services, you must provide a signature to verify your identity and ensure data security. This topic describes how to generate a signature in a request.

Signature algorithm

HMAC-SHA256 is used in OpenAPI services to create a message digest. Different signature algorithms are applied to token management API requests and general API requests, as described in the following table.

API type Token management API General business API
Scope of application Requests that are made to get or refresh tokens Requests that are made to manage business rather than tokens.
Signature algorithm str = client_id + t + nonce + stringToSign
sign = HMAC-SHA256(str, secret).toUpperCase()
str = client_id + access_token + t + nonce + stringToSign
sign = HMAC-SHA256(str, secret).toUpperCase()
Field description 1. `client_id`: the key of an authentication key pair.
2. `t`: the 13-digit standard timestamp.
3. `nonce`: the universally unique identifier (UUID) generated for each API request. Combined with the timestamp, the UUID ensures the uniqueness of API requests. This field is optional.
4. `stringToSign`: the string-to-sign. For more information, see [String-to-sign (stringToSign)](#String-to-sign).
5. `secret`: the value of an authentication key pair.
6. `access_token`: the access token. The field value is obtained from the authentication key pair.
Remarks Concatenate the value of `client_id`, the 13-digit standard timestamp (`t`) of the specified request, `nonce`, and `stringToSign` to create a string named as `str`. Use HMAC-SHA256 to create a hash digest value based on the string `str` and the value of `secret`. Capitalize all letters of the hash digest value to get a signature.

Different from token management API requests, in the signature algorithm for general API requests, access_token is additionally concatenated to generate the string str.

Create a string-to-sign (stringToSign)

Structure

stringToSign is created in the following structure:

String stringToSign=
HTTPMethod + "\n" +
Content-SHA256 + "\n" +
Headers + "\n" +
URL

This structure consists of the following four parts:

  • HTTPMethod
  • Content-SHA256
  • Headers
  • URL

You can concatenate the strings of these four parts with line-feed characters (\n) to create a string-to-sign named as stringToSign.

HTTPMethod

HTTPMethod represents an API method, such as GET and POST.

Content-SHA256

Content-SHA256 represents the SHA256 value of a request body. SHA256 is calculated only when the body is not a form.

Calculation:

String content-SHA256 = SHA256(bodyStream.getbytes("UTF-8")); // `bodyStream` is a byte array.

An empty body is still encrypted into e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.

Headers

Headers represents the string in which all request headers involved in the signature calculation are concatenated with line-feed characters (\n). Each request header is a key-value pair.

The following request headers are described as an example:

client_id: 1KAD46OrT9HafiKd****
sign: C5EFD19AD45E33A060C0BE47AEF65D975D54B2D70CBAA7A1ACA1A7D0E5C0****
sign_method: HMAC-SHA256
t: 1588925778000
access_token: 3f4eda2bdec17232f67c0b188af3****
nonce: 5138cc3a9033d69856923fd07b49****
...

To improve data security, add two custom fields area_id and request_id to the signature calculation.

area_id: 29a33e8796834b1****
request_id: 8afdb70ab2ed11eb85290242ac13****

Therefore, the request headers are organized in the following structure:

client_id: 1KAD46OrT9HafiKd****
sign: C5EFD19AD45E33A060C0BE47AEF65D975D54B2D70CBAA7A1ACA1A7D0E5C0****
sign_method: HMAC-SHA256
t: 1588925778000
access_token: 3f4eda2bdec17232f67c0b188af3****
nonce: 5138cc3a9033d69856923fd07b49****
...

// Two request headers are added.
Signature-Headers: area_id:request_id
area_id: 29a33e8796834b1**** // Subject to your customization.
request_id: 8afdb70ab2ed11eb85290242ac13****	// Subject to your customization.

All request header fields involved are concatenated with colons (:). As shown in the preceding code block, area_id:request_id is generated as the value of Signature-Headers.

Then, concatenate all key-value pairs included in Signature-Headers to generate the value of Headers in the following structure:

String Headers =
HeaderKey1 + ":" + HeaderValue1 + "\n" +
HeaderKey2 + ":" + HeaderValue2 + "\n" +
...
HeaderKeyN + ":" + HeaderValueN + "\n"

In this example, the following value of Headers is obtained:

String Headers =
area_id + ":" + 29a33e8796834b1**** + "\n" +
request_id + ":" + 8afdb70ab2ed11eb85290242ac13**** + "\n"

URL

A URL represents the request path into which a request path and form parameters are concatenated.

Sort form parameters by keys in alphabetically ascending order and concatenated in the following way. If query parameters or form parameters are empty, set the URL to the request path that is not suffixed with a question mark (?).

String url =
Path +
"?" +
Key1 + "=" + Value1 +
"&" + Key2 + "=" + Value2 +
"&" + Key3 +
...
"&" + KeyN + "=" + ValueN

Example:

String url = /v1.0/iot-03/devices/87707085bcddc23a5fa3/logs?end_time=1657263936000&event_types=1&start_time=1657160836000

Sort parameters in alphabetically ascending order. If multiple parameters have the same initial letter, sort these parameters by their second letter alphabetically. This way, letters are sequentially compared among different parameters until the parameters can be sorted as expected. Take the following parameters as an example:

start_time=1657160836000
end_time=1657263936000
event_types=1

They are sorted in the following alphabetical order:

end_time=1657263936000
event_types=1
start_time=1657160836000

In these parameters:

  • end_time and event_types have the same initial letter, and the second letter n of end_time comes earlier in the alphabet than the second letter v of event_types. Therefore, end_time is followed by event_types.
  • The first letter of end_time and event_types comes earlier in the alphabet than the first letter of start_time. As a result, end_time and event_types are followed by start_time.

Example

Token management API

In the following example, make an API request to get a token. Set grant_type to 1 and leave the body parameter empty.

GET:/v1.0/token?grant_type=1

The following request header fields are included.

Parameter Value
method GET
client_id 1KAD46OrT9HafiKdsXeg
secret 4OHBOnWOqaEC1mWXOpVL3yV50s0qGSRC
t 1588925778000
sign_method HMAC-SHA256
nonce 5138cc3a9033d69856923fd07b491173
Signature-Headers area_id:call_id
area_id (Customized and used in signature calculation) 29a33e8796834b1efa6
call_id (Customized and used in signature calculation) 8afdb70ab2ed11eb85290242ac130003

Create a string-to-sign (stringToSign)

Generate the string-to-sign stringToSign in the following structure:

GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003

/v1.0/token?grant_type=1

The structure is described as follows:

Sign Requests

Why does a blank line exist in stringToSign? This depends on the structure of stringToSign as follows:

String stringToSign=
HTTPMethod + "\n" +
Content-SHA256 + "\n" +
Headers + "\n" +
URL

stringToSign consists of HTTPMethod, Content-SHA256, Headers, and URL, concatenated with a line-feed character (\n) in between.

The following example shows how to calculate Headers:

String Headers =
HeaderKey1 + ":" + HeaderValue1 + "\n" +
HeaderKey2 + ":" + HeaderValue2 + "\n" +
…
HeaderKeyN + ":" + HeaderValueN + "\n"

Therefore, the Headers string is followed by two line-feed characters (\n) that result in a blank line.

Create a string that includes the string-to-sign

After stringToSign is generated, you can use it to create the string str:

1KAD46OrT9HafiKdsXeg15889257780005138cc3a9033d69856923fd07b491173GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003

/v1.0/token?grant_type=1

The structure is described as follows:
Sign Requests

Use HMAC-SHA256 to create a message digest

Hash digest value:

HMAC-SHA256(1KAD46OrT9HafiKdsXeg15889257780005138cc3a9033d69856923fd07b491173GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003

/v1.0/token?grant_type=1,4OHBOnWOqaEC1mWXOpVL3yV50s0qGSRC)

Create a hash digest value and encode the hash digest value into a new string:

9e48a3e93b302eeecc803c7241985d0a34eb944f40fb573c7b5c2a82158af13e

Capitalize all letters of the new string:

9E48A3E93B302EEECC803C7241985D0A34EB944F40FB573C7B5C2A82158AF13E

General business API

In the following example, make an API request to get a list of users. Set schema to apps and leave the body parameter empty.

Parameter Value
URL /v2.0/apps/schema/users
method GET
client_id 1KAD46OrT9HafiKdsXeg
secret 4OHBOnWOqaEC1mWXOpVL3yV50s0qGSRC
t 1588925778000
access_token 3f4eda2bdec17232f67c0b188af3eec1
sign_method HMAC-SHA256
nonce 5138cc3a9033d69856923fd07b491173
Signature-Headers area_id:call_id
area_id (Customized and used in signature calculation) 29a33e8796834b1efa6
call_id (Customized and used in signature calculation) 8afdb70ab2ed11eb85290242ac130003
page_no 1
page_size 50

Create a string-to-sign (stringToSign)

Generate the string-to-sign stringToSign in the following structure:

GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003

/v2.0/apps/schema/users?page_no=1&page_size=50

The structure is described as follows:

Sign Requests

Create a string that includes the string-to-sign

After stringToSign is generated, you can use it to create the string str:

1KAD46OrT9HafiKdsXeg3f4eda2bdec17232f67c0b188af3eec115889257780005138cc3a9033d69856923fd07b491173GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003

/v2.0/apps/schema/users?page_no=1&page_size=50

The structure is described as follows:
Sign Requests

Use HMAC-SHA256 to create a message digest

Hash digest value:

HMAC-SHA256(1KAD46OrT9HafiKdsXeg3f4eda2bdec17232f67c0b188af3eec115889257780005138cc3a9033d69856923fd07b491173GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003

/v2.0/apps/schema/users?page_no=1&page_size=50,4OHBOnWOqaEC1mWXOpVL3yV50s0qGSRC)

Create a hash digest value and encode the hash digest value into a new string:

ae4481c692aa80b25f3a7e12c3a5fd9bbf6251539dd78e565a1a72a508a88784

Capitalize all letters of the new string:

AE4481C692AA80B25F3A7E12C3A5FD9BBF6251539DD78E565A1A72A508A88784

Implement the HMAC-SHA256 authentication scheme

Example for Java

Download the sample code.

Example for Go

Download the sample code.

Example for Node.js

Download the sample code.

Example for JavaScript

/**
Run the code online with this jsfiddle. Dependent upon an open source js library calledhttp://code.google.com/p/crypto-js/.
**/

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/enc-base64.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/hmac-sha256.min.js"></script>

<script>
  var hash = CryptoJS.HmacSHA256("Message", "secret");
  var hashInBase64 = hash.toString().toUpperCase();
  document.write(hashInBase64);
</script>

Example for PHP

/**
PHP has built in methods for hash_hmac (PHP 5) and base64_encode (PHP 4, PHP 5) resulting in no outside dependencies. Say what you want about PHP but they have the cleanest code for this example.
**/

$s = strtoupper(hash_hmac("sha256", "Message", 'secret'));
echo var_dump($s);

Example for C#

using System;
using System.Security.Cryptography;

namespace Test
{
  public class MyHmac
  {
    public static string Encrypt(string message, string secret)
            {
                secret = secret ?? "";
                var encoding = new System.Text.UTF8Encoding();
                byte[] keyByte = encoding.GetBytes(secret);
                byte[] messageBytes = encoding.GetBytes(message);
                using (var hmacsha256 = new HMACSHA256(keyByte))
                {
                    byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
                    StringBuilder builder = new StringBuilder();
                    for (int i = 0; i < hashmessage.Length; i++)
                    {
                        builder.Append(hashmessage[i].ToString("x2"));
                    }
                    return builder.ToString().ToUpper();
                }
            }
  }
}

FAQs

How do I verify the encrypted signature?

During local development, you can test API requests with Postman to verify the encrypted signature. For more information, see Verify Signature Result.