签名算法

签名算法

如果SDK列表中包含您使用的语言,可使用对应的SDK并忽略以下签名算法,SDK在调用过程中会自动签名。

数据假设

在生成 API 请求中的签名(Signature) 时,需要提供账户密钥,包括 PublicKeyPrivateKey。 密钥可以从 PICPIK用户中心 (opens in a new tab) 获取。

本例中假设

PublicKey  = 'abcdefg'
PrivateKey = '123456'

您可以使用上述的 PublicKeyPrivateKey 调试代码, 如得到与后文一致的签名结果,即表示代码是正确的,可再换为您自己的 PublicKeyPrivateKey 以及业务所需的其它API请求参数。

本例中假设用户请求参数串如下:

💡

实际使用过程中,应包含:Signature外所有已填写的参数

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

构造签名

💡

如果示例代码中包含您使用的语言,建议直接使用其中的Sign函数构造签名。

如果示例代码中没有您使用的语言,可以联系我们补充或者按照以下规则生成签名:

1. 将请求参数按照名称进行升序排列

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

2. 构造被签名参数串

被签名串的构造规则为: 被签名串 = 所有请求参数拼接(无需 HTTP 转义)。并在本签名串的结尾拼接 API 密钥的私钥(PrivateKey)。

ActionListModelsPublicKeyabcdefg123456
💡

注意:

  • 对于 bool 类型,应编码为 true / false,首字母需小写。
  • 对于浮点数类型,如果小数部分为 0,应仅保留整数部分,如 42.0 应保留 42
  • 对于浮点数类型,不能使用科学计数法
  • 对于数组类型,将数组的每个元素直接转为字符串拼接
  • 对于map类型,将每个字段按照名称进行升序排列,然后将每个字段的名称和值拼接

3. 计算签名

使用SHA1编码签名串,生成最终签名,即是请求参数 Signature 的值。

按照上述算法,本例中,计算出的 Signature4a20bc1141494035f6aaaad13224c94c5a8bc3a5

语言编码示例

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[:])
}
 
// map2String 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)
	}
}
 
// simple2String 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 ""
}
 
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)
}
 
// reflectStruct2String convert array or slice or struct 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
}
 
// 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
}