注意:以下文档只适用于TOP接口,请谨慎使用!

文档中心 > 全渠道

奇门sign签名算法

更新时间:2016/10/10 访问次数:26397

1 签名算法

      签名策略:假设 ERP向WMS发起正向接口调用,那么ERP对URL+body信息进行MD5加密(加密算法下面有详细介绍),奇门收到ERP的请求后,会用ERP的 appkey的secret进行sign校验,验证是否报文有被篡改。如果验证通过,奇门转发给WMS之前再做一次签名,用WMS的appkey的 secret进行MD5加密,作为sign参数的值,其他信息不变。这样WMS在收到奇门传过来的报文后,可以用自己WMS的secret进行签名验证, 此次调用的签名验证完成。

 

我们用ERP调用奇门URL演示加密算法:

http://qimenapi.tbsandbox.com/router/qimen/service?method=taobao.qimen.entryorde.rquery&timestamp=2015-04-26 00:00:07&format=xml&app_key=testerp_appkey&v=2.0&sign=abc&sign_method=md5&customerId=stub-cust-code

 

 

    1)、输入参数为

method= taobao.qimen.entryorder.query
       timestamp=2015-04-26 00:00:07
       format=xml
       app_key= testerp_appkey
       v=2.0
       sign_method=md5
       customerId =test

 

    2)、按首字母升序排列

app_key= testerp_appkey

customerId = stub-cust-code

format=xml

method= taobao.qimen.entryorder.query

sign_method=md5
       timestamp=2015-04-26 00:00:07
       v=2.0             

 

    3)、连接字符串

连接参数名与参数值,并在首尾加上secret,此处假设secret=test,如下:
       testapp_keytesterp_appkeycustomerIdstub-cust-codeformatxmlmethodtaobao.qimen.entryorder.querysign_methodmd5timestamp2015-04-26 00:00:07v2.0
bodytest

其中:body用请求中的body内容代替

 

    4)、生成签名 sign

32位大写MD5-> D06D88CB34B2EC0E5C9BAB396C9542B6

 

    5)、拼装URL请求

将所有参数值转换为UTF-8编码,然后拼装,通过浏览器访问该地址,即成功调用一次接口,如下:

http://qimenapi.tbsandbox.com/router/qimen/service?method=taobao.qimen.entryorder.query&timestamp=2015-04-26 00:00:07&format=xml&app_key=testerp_appkey&v=2.0&sign= D06D88CB34B2EC0E5C9BAB396C9542B6 &sign_method=md5&customerId=stub-cust-code

 

 

2 算法JAVA示例

 

package md5;

 

 

import java.io.UnsupportedEncodingException;

import java.net.URLDecoder;

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

import java.util.Arrays;

import java.util.HashMap;

import java.util.Map;

 

/**

 * Created by jiqian.zzp on 2015/7/6.

 */

public class QimenSign {

    public static void main(String[] args) {

        String url = "http://qimenapi.tbsandbox.com/top/router/qimen/service?"

                        + "method=your_method&"

                        + "timestamp=2015-04-26%2000:00:07&"

                        + "format=xml&"

                        + "app_key=your_appkey&"

                        + "v=your_version&"

                        + "sign=your_sign&"

                        + "sign_method=md5&"

                        + "customerId=your_customerId";

        String body = "your_body";

        String secretKey = "your_secretKey";

 

        QimenSign sign = new QimenSign();

        String md5 = sign.sign(url, body, secretKey);

        System.out.println(md5);//6A4B6FCFAFE80280565406E110C27DC8

    }

 

    public String sign(String url, String body, String secretKey) {

 

        Map<String, String> params = getParamsFromUrl(url);

 

        // 1. 第一步,确保参数已经排序

        String[] keys = params.keySet().toArray(new String[0]);

        Arrays.sort(keys);

 

        // 2. 第二步,把所有参数名和参数值拼接在一起(包含body)

        String joinedParams = joinRequestParams(params, body, secretKey, keys);

 

        //your_secretKeyapp_keyyour_appkeycustomerIdyour_customerIdformatxmlmethodyour_methodsign_methodmd5timestamp2015-04-26 00:00:07vyour_versionyour_bodyyour_secretKey

        System.out.println(joinedParams);

 

        // 3. 第三步,使用加密算法进行加密(目前仅支持md5算法)

        String signMethod = params.get("sign_method");

        if (!"md5".equalsIgnoreCase(signMethod)) {

            //TODO

            return null;

        }

        byte[] abstractMesaage = digest(joinedParams);

 

        // 4. 把二进制转换成大写的十六进制

        String sign = byte2Hex(abstractMesaage);

 

        return sign;

 

    }

 

    private Map<String, String> getParamsFromUrl(String url) {

        Map<String, String> requestParams = new HashMap<String, String>();

        try {

            String fullUrl = URLDecoder.decode(url, "UTF-8");

            String[] urls = fullUrl.split("\\?");

            if (urls.length == 2) {

                String[] paramArray = urls[1].split("&");

                for (String param : paramArray) {

                    String[] params = param.split("=");

                    if (params.length == 2) {

                        requestParams.put(params[0], params[1]);

                    }

                }

            }

        } catch (UnsupportedEncodingException e) {

            // TODO

        }

        return requestParams;

    }

 

    private String byte2Hex(byte[] bytes) {

        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

        int j = bytes.length;

        char str[] = new char[j * 2];

        int k = 0;

        for (byte byte0 : bytes) {

            str[k++] = hexDigits[byte0 >>> 4 & 0xf];

            str[k++] = hexDigits[byte0 & 0xf];

        }

        return new String(str);

    }

 

    private byte[] digest(String message)  {

        try {

            MessageDigest md5Instance = MessageDigest.getInstance("MD5");

            md5Instance.update(message.getBytes("UTF-8"));

            return md5Instance.digest();

        } catch (UnsupportedEncodingException e) {

            //TODO

            return null;

        } catch (NoSuchAlgorithmException e) {

            //TODO

            return null;

        }

    }

 

    private String joinRequestParams(Map<String, String> params, String body, String secretKey, String[] sortedKes) {

        StringBuilder sb = new StringBuilder(secretKey); // 前面加上secretKey

 

        for (String key : sortedKes) {

            if ("sign".equals(key)) {

                continue; // 签名时不计算sign本身

            } else {

                String value = params.get(key);

                if (isNotEmpty(key) && isNotEmpty(value)) {

                    sb.append(key).append(value);

                }

            }

        }

        sb.append(body); // 拼接body

        sb.append(secretKey); // 最后加上secretKey

        return sb.toString();

    }

 

    private boolean isNotEmpty(String s) {

        return null != s && !"".equals(s);

    }

}

 

 

3.算法.net示例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Diagnostics;
using System.Text;
/// <summary>
/// QimenSign 的摘要说明
/// </summary>
public class QimenSign
{
        public QimenSign()
        {
        
        }

    public String sign(String url, String body, String secretKey)
    {
        Dictionary<String, String> param = getParamsFromUrl(url);

        // 1. 第一步,确保参数已经排序
        string[] keys = new string[param.Keys.Count];
        param.Keys.CopyTo(keys,0);
        Array.Sort(keys);

        // 2. 第二步,把所有参数名和参数值拼接在一起(包含body体)
        String joinedParams = joinRequestParams(param, body, secretKey, keys);

        // 3. 第三步,使用加密算法进行加密(目前仅支持md5算法)
        String signMethod = param["sign_method"];
        if (!string.Equals("md5",signMethod)) {
            //TODO
            return null;
        }
        byte[] abstractMesaage = digest(joinedParams);

        // 4. 把二进制转换成大写的十六进制
        String sign = byte2Hex(abstractMesaage);

        return sign;
    }

    private Dictionary<string, string> getParamsFromUrl(String url) {
        Dictionary<string, string> requestParams = new Dictionary<string, string>();
        try {
            String fullUrl = System.Web.HttpUtility.UrlDecode(url, System.Text.Encoding.UTF8);
            String[] urls = fullUrl.Split('?');
            if (urls.Length == 2) {
                String[] paramArray = urls[1].Split('&');
                foreach (String param in paramArray) {
                    String[] values = param.Split('=');
                    if (values.Length == 2) {
                        requestParams.Add(values[0], values[1]);
                    }
                }
            }
        } catch (Exception e) {
            // TODO
            return null;
        }
        return requestParams;
    }
    private String byte2Hex(byte[] bytes) {
        char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        int j = bytes.Length;
        char[] str = new char[j * 2];
        int k = 0;
        foreach (byte byte0 in bytes) {
            str[k++] = hexDigits[byte0 >> 4 & 0xf];
            str[k++] = hexDigits[byte0 & 0xf];
        }
        return new String(str);
    }

    private byte[] digest(String message)
    {
        try
        {
            //获取加密服务  
            System.Security.Cryptography.MD5CryptoServiceProvider md5CSP = new System.Security.Cryptography.MD5CryptoServiceProvider();  
         
            //获取要加密的字段,并转化为Byte[]数组  
            byte[] testEncrypt = System.Text.Encoding.UTF8.GetBytes(message);  
  
            //加密Byte[]数组  
            byte[] resultEncrypt = md5CSP.ComputeHash(testEncrypt);
            return resultEncrypt;
        }
        catch (Exception e)
        {
            //TODO
            return null;
        }
    }

    private String joinRequestParams(Dictionary<String, String> param, String body, String secretKey, String[] sortedKes) {
        StringBuilder sb = new StringBuilder(secretKey); // 前面加上secretKey

        foreach (String key in sortedKes) {
            if ("sign".Equals(key)) {
                continue; // 签名时不计算sign本身
            } else {
                String value = param[key];
                if (isNotEmpty(key) && isNotEmpty(value)) {
                    sb.Append(key).Append(value);
                }
            }
        }
        sb.Append(body); // 拼接body体
        sb.Append(secretKey); // 最后加上secretKey
        return sb.ToString();
    }

    private bool isNotEmpty(String s) {
        return null != s && !"".Equals(s);
    }

}

 

4.签名算法Python版本

以下算法基于python2.x


#! /usr/bin/python
# -*- coding: utf-8 -*-
import md5

    def getTotalUrl_with_body(method_args,xml_body):#method_args为参数列表的dict
        request_args = {}
        url_head = 'http://qimenapi.tbsandbox.com/top/router/qimen/service?'
        url_secret ='sandbox642d8c408d84760fa0045ea79'  #修改配置
        url_str = '' + url_secret
        sign_list = []
        url_list =[]
        request_args = method_args
        for key,value in request_args.items():
            sign_list.append(key+value)
            url_list.append(key + '=' + value.encode('UTF-8'))
        sign_list.sort()
        for item in sign_list:
            url_str += item
        url_str = url_str + xml_body + url_secret
        sign_md5 = md5.new()
        sign_md5.update(url_str)
        sign = sign_md5.hexdigest().upper()  #签名生成,若仅需要签名,url生产部分可以忽略
        for item in url_list:
            url_head += '%s&' %(urllib.quote(item))
        final_url = url_head + 'sign=' + sign  #最终url生成
        return final_url

 

 

 

FAQ

1.Q:签名时注意timestamp字段的处理。

   A:timestamp字段做签名的时候,要用encode之前的,验证签名的时候,要用decode之后的。也就是,计算签名的时候都使用timestamp的原始数据。如:签名的时候用encode之前的timestamp2015-08-19 12:04:24,传URL的时候要encode,用stamptime2015-04-26%2000:00:07,校验签名的时候还是用timestamp2015-08-19 12:04:24。

 

2.Q:什么是沙箱appkey,以及沙箱的secret怎么获取?

   A:登陆开放平台open.taobao.com,进入控制台,找到对接奇门要使用的应用,应用设置里有沙箱环境的参数。

 

 3.Q:如何验证签名算法,有没有工具

   A:有,在应用管理的奇门联调配置工具里(http://cloud.tmall.com/my/qimen/center/signatureBuilder.htm?spm=0.0.0.0.rEEw3E),奇门接入工具-签名工具。

 

 

 

 

 

 

 

FAQ

关于此文档暂时还没有FAQ
返回
顶部