1 Star 0 Fork 665

我不是超人 / easyopen

forked from tanghc / easyopen 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
签名算法.md 6.13 KB
一键复制 编辑 原始数据 按行查看 历史
tanghc 提交于 2018-07-20 11:00 . update

签名算法

签名算法描述如下: 1.将请求参数按参数名升序排序; 2.按请求参数名及参数值相互连接组成一个字符串:...; 3.将应用密钥分别添加到以上请求参数串的头部和尾部:<请求参数字符串>; 4.对该字符串进行MD5(全部大写),MD5后的字符串即是这些请求参数对应的签名; 5.该签名值使用sign参数一起和其它请求参数一起发送给服务开放平台。

参数示例

{
    "name": "file.upload",
    "version": "",
    "app_key": "admin",
    "data": "%7B%22goods_name%22%3A%22iphoneX%22%7D",
    "timestamp": "2018-07-17 16:34:34",
    "format": "json",
    "sign": "966E54AE152F0D60840E65A15376D924"
}

请求示例

import java.io.IOException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Test;

import com.alibaba.fastjson.JSON;

import junit.framework.TestCase;

public class PostTest extends TestCase {

    @Test
    public void testPost() throws IOException {
        String appKey = "test";
        String secret = "123456";
        // 业务参数
        Map<String, String> jsonMap = new HashMap<String, String>();
        jsonMap.put("goodsName", "iphoneX");

        String json = JSON.toJSONString(jsonMap);
        json = URLEncoder.encode(json, "utf-8");

        // 系统参数
        Map<String, Object> param = new HashMap<String, Object>();
        param.put("name", "goods.get");
        param.put("app_key", appKey);
        param.put("data", json);
        param.put("timestamp", getTime());
        param.put("version", "");
        param.put("access_token", "");

        String sign = buildSign(param, secret);

        param.put("sign", sign);

        /*
        // 最终请求数据
        {
            "sign": "2AE534A15AACE112EE43B9CCF6BD4383",
            "timestamp": "2018-03-21 12:57:30",
            "name": "goods.get",
            "data": "%7B%22goodsName%22%3A%22iphoneX%22%7D",
            "app_key": "test",
            "version": ""
        }
        */
        System.out.println("=====请求数据=====");
        String postJson = JSON.toJSONString(param);
        System.out.println(postJson);
        // contentType:application/json
        // postJson放到请求体中
        // String resp = HttpUtil.post(postJson); // 发送请求
        /*
        响应结果:
        {
            "code":"0",
            "data":{
                "pageIndex":1,
                "pageSize":10,
                "rows":[
                    {
	                    "goods_name":"iPhoneX",
	                    "id":1,
	                    "price":8000
                    },
                    {
	                    "goods_name":"三星",
	                    "id":2,
	                    "price":7000
                    }
	           ],
	           "total":100
            }
        }
        */
    }

    /**
     * 构建签名
     *
     * @param paramsMap
     *            参数
     * @param secret
     *            密钥
     * @return
     * @throws IOException
     */
    public static String buildSign(Map<String, ?> paramsMap, String secret) throws IOException {
        Set<String> keySet = paramsMap.keySet();
        List<String> paramNames = new ArrayList<String>(keySet);

        Collections.sort(paramNames);

        StringBuilder paramNameValue = new StringBuilder();

        for (String paramName : paramNames) {
            paramNameValue.append(paramName).append(paramsMap.get(paramName));
        }

        String source = secret + paramNameValue.toString() + secret;

        return md5(source);
    }

    /**
     * 生成md5,全部大写
     *
     * @param message
     * @return
     */
    public static String md5(String message) {
        try {
            // 1 创建一个提供信息摘要算法的对象,初始化为md5算法对象
            MessageDigest md = MessageDigest.getInstance("MD5");

            // 2 将消息变成byte数组
            byte[] input = message.getBytes();

            // 3 计算后获得字节数组,这就是那128位了
            byte[] buff = md.digest(input);

            // 4 把数组每一字节(一个字节占八位)换成16进制连成md5字符串
            return byte2hex(buff);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 二进制转十六进制字符串
     *
     * @param bytes
     * @return
     */
    private static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }

    public String getTime() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    }
}

基于公私钥+AES加密传输:

0.事先把公钥放在客户端,私钥放在服务端 1.客户端生成一个随机码randomKey 2.将randomKey通过公钥RSA加密str = rsa_encode(randomKey,publicKey) 3.将str发送到服务端 4.服务端通过私钥解开str,得到randomKey:randomKey = rsa_decode(str, privateKey) 5.服务端使用randomKey通过AES对称加密一个值(假设值为"0")返回给客户端,resp = aes_encode("0", randomKey) 6.客户端用自己的randomKey去aes解密resp,如果得到的是"0",则整个握手交互完成,后续都用这个randomKey进行aes加解密传输

注: 黑客是可以拿到公钥的,但是黑客无法知道客户端生成的随机码randomKey,一旦str发生改变则握手失败. 整个过程的重点就是将randomKey安全的送到服务器,后期都用randomKey进行对称加密传输,对称加密黑客无法破解. 此流程参照HTTPS,只不过服务器无法将公钥正确的送到客户端(浏览器),因此需要CA机构介入.

Java
1
https://gitee.com/LicV587/easyopen.git
git@gitee.com:LicV587/easyopen.git
LicV587
easyopen
easyopen
master

搜索帮助