Signature

Signature

If the SDK list contains the language you are working with, you can use the corresponding SDK and ignore the following signing algorithm, which is automatically signed during the call.

Data Assumptions

When generating the Signature in an API request, you need to provide the account's keys, including PublicKey and PrivateKey, Keys can be obtained from the PICPIK User Center (opens in a new tab).

In this example, assume

PublicKey  = 'abcdefg'
PrivateKey = '123456'

You can use the above PublicKey and PrivateKey to debug your code. When you get the same signature result as below (indicating that your code is correct), you can replace it with your own keys and request parameters.

In this example, assume the user's request parameters are as follows:

💡

In practice, all parameters are required except Signature.

{
    "Action"     :  "ListModels",
    "PublicKey"  :  "abcdefg"
}

Constructing the Signature

💡

Constructing the signature using the Sign function in demo codes is recommended if your language is in the list.

If a coding language you prefer is not in the demonstrations, you can contact us for help or generate the signature according to the following rules:

1. Sort the Request Parameters in Ascending Order by Name

{
    "Action"     :  "ListModels",
    "PublicKey"  :  "abcdefg"
}

2. Construct the String to Be Signed

The construction rule of the string to be signed is the concatenation of all request parameters (no need for HTTP escape), and then concatenate the PrivateKey at the end of this signature string.

ActionListModelsPublicKeyabcdefg123456
💡

Note:

  • For bool type, it should be encoded as true / false, lowercase initial letters are required.
  • For floating-point number type, if the decimal part is 0, only the integer part should be retained, such as 42.0 should be kept as 42.
  • For floating-point number type, scientific notation should not be used.
  • For array types, convert each element of the array directly to string concatenation
  • For the map type, each field should be sorted in ascending order by name and then concatenate the name and value of each field.

3. Calculate the Signature

Use SHA1 encoding for the string to be signed to generate the final Signature. According to the above algorithm, in this example, the result is 4a20bc1141494035f6aaaad13224c94c5a8bc3a5.

Demo Codes

func TestGenerateApiKey(t *testing.T) {
	var (
		PublicKey  = "abcdefg"
		PrivateKey = "123456"
		req        = map[string]interface{}{
			"Action":    "ListModels",
			"PublicKey": PublicKey,
		}
	)
	sign := Sign(req, PrivateKey)
	fmt.Println(sign)
}
// Sign generate signature
func Sign(params map[string]interface{}, privateKey string) string {
	str := map2String(params) + privateKey
 
	hashed := sha1.Sum([]byte(str))
	return hex.EncodeToString(hashed[:])
}
 
// simple2String convert map type to string
func map2String(params map[string]interface{}) (str string) {
	for _, k := range extractSortedKeys(params) {
		str += k + any2String(params[k])
	}
	return
}
 
// any2String convert any type to string
func any2String(v interface{}) string {
	switch v := v.(type) {
	case string, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		return simple2String(v)
	case *string, *bool, *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64:
		return point2String(v)
	case float32, float64, *float32, *float64:
		return float2String(v)
	case []interface{}:
		return slice2String(v)
	case map[string]interface{}:
		return map2String(v)
	default:
		return reflectStruct2String(v)
	}
}
 
func point2String(v interface{}) string {
	value := reflect.ValueOf(v)
	if value.Kind() == reflect.Ptr {
		return simple2String(value.Elem().Interface())
	} else {
		return simple2String(v)
	}
}
 
 
// slice2String convert slice type to string
func slice2String(arr []interface{}) (str string) {
	for _, v := range arr {
		switch v := v.(type) {
		case string, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64,
			*string, *bool, *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64, *float32, *float64:
			str += simple2String(v)
		case []interface{}:
			str += slice2String(v)
		case map[string]interface{}:
			str += map2String(v)
		default:
			str += reflectStruct2String(reflect.ValueOf(v))
		}
	}
	return
}
 
// simple2String convert simple type to string
func simple2String(v interface{}) string {
	switch v.(type) {
	case int:
		return decimal.NewFromInt(int64(v.(int))).String()
	case int8:
		return decimal.NewFromInt(int64(v.(int8))).String()
	case int16:
		return decimal.NewFromInt(int64(v.(int16))).String()
	case int32:
		return decimal.NewFromInt(int64(v.(int32))).String()
	case int64:
		return decimal.NewFromInt(v.(int64)).String()
	case uint:
		return decimal.NewFromInt(int64(v.(uint))).String()
	case uint8:
		return decimal.NewFromInt(int64(v.(uint8))).String()
	case uint16:
		return decimal.NewFromInt(int64(v.(uint16))).String()
	case uint32:
		return decimal.NewFromInt(int64(v.(uint32))).String()
	case uint64:
		return decimal.NewFromInt(int64(v.(uint64))).String()
	default:
		return fmt.Sprintf("%v", v)
	}
}
 
func float2String(v interface{}) string {
	switch v.(type) {
	case *float64:
		return decimal.NewFromFloat(*v.(*float64)).String()
	case *float32:
		return decimal.NewFromFloat32(*v.(*float32)).String()
	case float64:
		return decimal.NewFromFloat(v.(float64)).String()
	case float32:
		return decimal.NewFromFloat32(v.(float32)).String()
	}
	return ""
}
 
// reflectStruct2String convert array and slice to string in reflect way
func reflectStruct2String(v interface{}) (str string) {
	rv := reflect.ValueOf(v)
	if rv.Kind() == reflect.Array || rv.Kind() == reflect.Slice {
		for i := 0; i < rv.Len(); i++ {
			str += any2String(rv.Index(i).Interface())
		}
	}else if rv.Kind() == reflect.Struct {
		str += struct2String(v)
	}
	return
}
 
func struct2String(v interface{})string{
	strJson,err := json.Marshal(v)
	if err != nil {
		return ""
	}
	params := make(map[string]interface{}, 0)
	err = json.Unmarshal(strJson, &params)
	if err != nil {
		return ""
	}
	return map2String(params)
}
 
// extractSortedKeys extract all sorted keys from map[string]interface{}
func extractSortedKeys(m map[string]interface{}) []string {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
 
	sort.Strings(keys)
	return keys
}