签名验签使用说明

更新时间:2020-10-19 20:23:53

前提说明

1、2.1.40及以上版本的techWrapper服务一键部署包支持通过签名验签方式进行HTTP请求。

2、加签规则:

请求body体的JSON字符串作为待签名明文信息,并用projectSecret作为密钥通过HmacSHA256哈希算法进行请求签名值X-timevale-signature的计算。

部署说明

techWrapper服务一键部署包使用说明

参考链接:https://qianxiaoxia.yuque.com/docs/share/7c96dfbc-1e53-4de3-b1f6-46e10eda67b4

签名验签服务开启说明

注意事项:

1、签名验签服务为可选服务,如果开发者无需签名验签方式进行HTTP请求,只需按照techWrapper服务一键部署说明配置后,即可启动项目。

2、如果开发者需要通过签名验签方式进行HTTP请求,需接入2.1.40及以上版本的techWrapper服务包,并按照以下说明开启签名验签服务。

techWrapper服务包部署完成后,在启动服务之前,先开启签名验签服务配置。

1、找到systemconfig.properties文件,目录结构如下图所示:

2、编辑systemconfig.properties文件,开启签名验签服务。

首次打开文件后,默认配置项展示图如下:

开启签名验签服务后,配置项展示图如下:

3、以上配置完成后,保存文件,并按照techWrapper部署说明启动服务。

请求头说明

开发者如果使用签名验签方式进行HTTP请求时,需要在请求过程中,添加请求头headers

请求头格式如下:

参数名称

必选

参数说明

示例值

X-timevale-signature

请求签名值


Content-Type

固定类型,application/json


请求签名算法

1、JS示例

//请求数据data
var data = JSON.stringify(request.data);
data = data.substr(1, data.length - 2).replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\r/g, "\r").replace(/\\"/g, "\"");
//获取签名值
var signature = CryptoJS.HmacSHA256(data, "密钥").toString();

2、postman工具,使用Pre-request Script实现签名算法

//请求数据data
var data = JSON.stringify(request.data);
data = data.substr(1, data.length - 2).replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\r/g, "\r").replace(/\\"/g, "\"");
//获取签名值
var signature = CryptoJS.HmacSHA256(data, postman.getEnvironmentVariable("projectSecret")).toString();
postman.setEnvironmentVariable("signature", signature)

3、JAVA开发语言示例

public static void main(String[] args) {
        JSONObject params = new JSONObject();
        params.put("name","宗炼");
        params.put("idNo","34***15");
        params.put("mobile","173***6");
        params.put("title","职位");
        params.put("personArea","19");
        // 请求签名值
        String signature = getSignature(params.toString(), "项目id对应的密钥projectsecret","HmacSHA256","UTF-8");
        System.out.println(params.toString());
        System.out.println("请求签名值 = " + signature);
    }

    /***
     * 获取请求签名值
     * 
     * @param data      加密前数据
     * @param key       密钥
     * @param algorithm HmacMD5 HmacSHA1 HmacSHA256 HmacSHA384 HmacSHA512
     * @param encoding  编码格式
     * @return HMAC加密后16进制字符串
     * @throws Exception
     */
    public static String getSignature(String data, String key, String algorithm, String encoding) {
        Mac mac = null;
        try {
            mac = Mac.getInstance(algorithm);
            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(encoding), algorithm);
            mac.init(secretKey);
            mac.update(data.getBytes(encoding));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            e.printStackTrace();
            return null;
        }
        return byte2hex(mac.doFinal());
    }

    /***
     * 将byte[]转成16进制字符串
     * 
     * @param data
     * 
     * @return 16进制字符串
     */
    public static String byte2hex(byte[] data) {
    StringBuilder hash = new StringBuilder();
    String stmp;
    for (int n = 0; data != null && n < data.length; n++) {
        stmp = Integer.toHexString(data[n] & 0XFF);
        if (stmp.length() == 1)
        hash.append('0');
        hash.append(stmp);
    }
    return hash.toString();
    }

注:如果开发者使用文件流方式进行签署请求时,可参考下面提供的代码示例进行请求

(以平台自身摘要签署-文件流方式请求为例)

文件流方式进行签署请求时,注意事项:

1、请求body体可以直接在入参中就规定好顺序,例如入参参数顺序:file,sealId,signPos,signType,或者入参定义完成之后,按照字母顺序重新进行排序

2、转换入参信息,将常规map的入参转为url参数

public static void main(String[] args) {
        String url = "http://localhost:8080/tech-sdkwrapper/timevale/sign/selfStreamSign";
        
        JSONObject params = new JSONObject();
        params.put("file","D:\\01-模板合同文件\\test-templte.pdf");
        params.put("sealId","0");
        params.put("signPos","{" +
                "\"posX\":100," +
                "\"posY\":0," +
                "\"addSignTime\":\"true\"," +
                "\"posPage\":\"1-10\"," +
                "\"posType\":\"1\"," +
                "\"key\":\"关键字\"" +
                "}");
        params.put("signType","Key");
        InputStream inputStream = null;
        String result = "";

        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpPost httpPost = new HttpPost(url);
            RequestConfig defaultRequestConfig = RequestConfig.custom().
                    setConnectTimeout( 5000 )
                    .setConnectionRequestTimeout( 5000 )
                    .setSocketTimeout( 15000 )
                    .build();
            httpPost.setConfig( defaultRequestConfig );
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setMode( HttpMultipartMode.BROWSER_COMPATIBLE );
            //字符编码
            builder.setCharset(Charset.forName("UTF-8"));
            Iterator iter = params.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry) iter.next();
                if (entry.getKey().toString().equalsIgnoreCase("file") ) {
                    String filePath = params.get("file").toString();
                    inputStream = new FileInputStream(filePath);
                    String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
                    builder.addBinaryBody("file", inputStream, ContentType.MULTIPART_FORM_DATA, fileName);// 设置流参数
                    inputStream = new FileInputStream(filePath);
                    byte[] bytes = new byte[0];
                    try {
                        bytes = read(inputStream);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    String fileHash = new String(Base64.encodeBase64(bytes));
                    System.out.println("fileHash:"+fileHash);

                    params.put("file",fileHash);
                } else {
                    builder.addTextBody(entry.getKey().toString(), entry.getValue().toString(), ContentType.create("text/plain", Consts.UTF_8));
                }
            }
            //转换入参信息,将map的入参转为url参数
            String signParamsStr = parameterText(params,"&",  "sign_type","sign", "key", "signature");
            System.out.println("signParamsStr:"+signParamsStr);
            String sign = getSignature(signParamsStr, "项目id对应的密钥projectsecret","HmacSHA256","UTF-8");
            httpPost.addHeader("X-timevale-signature", sign);
            
            System.out.println("params:"+ params);
            System.out.println("[url]:" + url);
            System.out.println("[request]:" + params);

            //HttpEntity
            HttpEntity entity = builder.build();
            httpPost.setEntity(entity);

            CloseableHttpResponse resp = null; //返回结果
            resp = httpClient.execute( httpPost );
            StatusLine line = resp.getStatusLine();

            // 结果
            HttpEntity httpEntity = resp.getEntity();
            if (httpEntity != null) {
                result = EntityUtils.toString( httpEntity, Charset.forName( "UTF-8" ) );
                System.out.println( "返回响应码: " + line.getStatusCode()+"  "+"返回结果: " + result );
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.err.println("result" + result);
    }

    public static byte[] read(InputStream inStream) throws IOException, NoSuchAlgorithmException {
        byte[] md5Bytes = null;
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        byte[] buffer = new byte[1024];
        int len = 0;
        while((len=inStream.read(buffer))!=-1){
            md5.update(buffer, 0, len);
        }
        md5Bytes = md5.digest();
        inStream.close();
        return md5Bytes;
    }

    /***
     * 获取请求签名值
     * 
     * @param data
     *            加密前数据
     * @param key
     *            密钥
     * @param algorithm
     *            HmacMD5 HmacSHA1 HmacSHA256 HmacSHA384 HmacSHA512
     * @param encoding
     *            编码格式
     * @return HMAC加密后16进制字符串
     * @throws Exception
     */
    public static String getSignature(String data, String key, String algorithm, String encoding) {
        Mac mac = null;
        try {
            mac = Mac.getInstance(algorithm);
            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(encoding), algorithm);
            mac.init(secretKey);
            mac.update(data.getBytes(encoding));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            System.out.println("获取Signature签名信息异常:" + e.getMessage());
            return null;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            System.out.println("获取Signature签名信息异常:" + e.getMessage());
            return null;
        } catch (InvalidKeyException e) {
            e.printStackTrace();
            System.out.println("获取Signature签名信息异常:" + e.getMessage());
            return null;
        }
        return byte2hex(mac.doFinal());
    }

    /***
     * 将byte[]转成16进制字符串
     * 
     * @param data
     * 
     * @return 16进制字符串
     */
    public static String byte2hex(byte[] data) {
        StringBuilder hash = new StringBuilder();
        String stmp;
        for (int n = 0; data != null && n < data.length; n++) {
            stmp = Integer.toHexString(data[n] & 0XFF);
            if (stmp.length() == 1)
                hash.append('0');
            hash.append(stmp);
        }
        return hash.toString();
    }
    /**
     * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串
     *
     * @param parameters 参数
     * @param separator 分隔符
     * @param ignoreKey 需要忽略添加的key
     * @return 去掉空值与签名参数后的新签名,拼接后字符串
     */
    public static String parameterText(Map parameters, String separator, String... ignoreKey) {
        if (parameters == null) {
            return "";
        }
        StringBuffer sb = new StringBuffer();
        if (null != ignoreKey) {
            Arrays.sort(ignoreKey);
        }
        // TODO 2016/11/11 10:14 author: egan 已经排序好处理

            for (Map.Entry<String, Object> entry :
                    (Set<Map.Entry<String, Object>>) parameters.entrySet()) {
                Object v = entry.getValue();
                if (null == v
                        || "".equals(v.toString().trim())
                        || (null != ignoreKey
                        && Arrays.binarySearch(ignoreKey, entry.getKey()) >= 0)) {
                    continue;
                }
                sb.append(entry.getKey()).append("=").append(v.toString().trim()).append(separator);
            }
            if (sb.length() > 0 && !"".equals(separator)) {
                sb.deleteCharAt(sb.length() - 1);
            }
            System.out.println("排序前:"+ sb.toString());
            return sb.toString();
        

    }

4、PHP开发语言示例

//对于请求数据data 转为json字符串
$pa=json_encode($param);
$signature =  getSignature($pa,PROJECT_SECRET(密钥));
/计算请求签名值
function getSignature($message, $projectSecret) {
    $signature = hash_hmac('sha256', $message, $projectSecret, FALSE);
    return $signature;
}

5、.NET获取示例

// 请求参数-JSON字符串
  string data = jsonObject.ToString();
            // 请求签名值
  string signature =GetSignature(data, SceneConfig.PROJECTSECRET);
具体方法:
/// <summary>
        ///  HmacSHA256 加密
        /// </summary>
        /// <param name="secret">projectSecret</param>
        /// <param name="data">请求的JSON参数</param>
        /// <returns></returns>
        public static string GetSignature(string data, string secret)
        {
            byte[] keyByte = Encoding.UTF8.GetBytes(secret);
            byte[] messageBytes = Encoding.UTF8.GetBytes(data);
            using (var hmacsha256 = new HMACSHA256(keyByte))
            {
                byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
                StringBuilder sb = new StringBuilder();
                foreach (byte test in hashmessage)
                {
                    sb.Append(test.ToString("x2"));
                }
                return sb.ToString();
            }
        }
    }
我要纠错