# 接入指南

# 基础概念解释

1、公共参数: 公共请求参数是指每个接口都需要使用到的请求参数,与业务无关;

2、业务参数: 根据调用API服务接口的需求所传递的参数;

3、签名算法: 签名算法是指数字签名的算法。数字签名(又称公钥数字签名、电子签章)是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。

4、表单: 在网页中主要负责数据采集功能。一个表单有三个基本组成部分: 表单标签:这里面包含了处理表单数据所用CGI程序的URL以及数据提交到服务器的方法。 表单域:包含了文本框、密码框、隐藏域、多行文本框、复选框、单选框、下拉选择框和文件上传框等。 表单按钮:包括提交按钮、复位按钮和一般按钮;用于将数据传送到服务器上的CGI脚本或者取消输入,还可以用表单按钮来控制其他定义了处理脚本的处理工作。

# API签名算法

认证平台的 API 是基于 HTTP(S) 协议来调用的,开发者可以直接使用我们提供的SDK(包含了请求的封装,签名加密,响应解释等)来调用, 以下主要是针对自行封装 HTTP(S) 请求进行API调用的签名算法进行详细解说。API调用除了必须包含公共参数外,API本身业务级的参数,每个API的业务级参数请参考API文档说明。

# 签名算法原理

为了防止 API 调用过程中被恶意篡改,调用任何一个 API 都需要携带签名,服务端会根据请求参数,对签名进行验证,签名不合法的请求将会被拒绝。目前支持的签名算法:HMAC-SHA256(signMethod=HMAC-SHA256),签名大体过程如下:

  1. 对所有 API 请求参数(包括公共参数和业务参数,但除去sign参数),根据参数名称的ASCII码表的顺序排序,将排序好的参数名和参数值拼接在一起。
  2. 拼接好的字符串和密钥分别按照UTF-8编码,用编码后的密钥字符流结合HmacSHA256算法对编码后的参数字符流进行摘要。
  3. 将摘要后的字符流转换为十六进制大写字符串,即得到签名值。

# JAVA 签名示例代码

P1验签例子:

请求示例:

package cn.unitid.matrix.pkiservice.test;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

public class computeSignatureTest {

    /**密钥*/
    private static String secretKey = "xxx";
    private static String charset = "UTF-8";
    private static final String ALGORITHM = "HmacSHA256";
    private static final Object LOCK = new Object();
    private static Mac macInstance;

    public static void main(String[] args) {

        Map<String, String> paramMap = new HashMap<String, String>(10);

        //公共参数
        paramMap.put("appKey", "xxx");
        paramMap.put("v", "1");
        paramMap.put("method", "sign/verify/p1");
        paramMap.put("format", "JSON");
        paramMap.put("t",getTime());
        paramMap.put("nonce", getNonce());

        //具体接口参数
        paramMap.put("sign", "CERT");
        paramMap.put("signItemValue", "xxx");
        paramMap.put("dataType", "ORIGINAL");
        paramMap.put("data", "xxx");
        paramMap.put("signature", "xxx");
        paramMap.put("signatureAlgorithm", "SHA1withRSA");
        paramMap.put("returnCert", "true");

        //获取待签名内容,排序
        String signContent = getSignatureContent(paramMap);
        System.out.println("待签名内容:" + signContent);

        //计算签名
        String sign = computeSignature(secretKey, signContent, charset);
        System.out.println("签名后:" + sign);
    }

    /**
     * 格式化时间
     */
    private static String getTime() {

        return String.valueOf(System.currentTimeMillis());
    }

    /**
     * 生成随机数
     */
    private static String getNonce(){
        Random random = new Random();
        return String.valueOf(random.nextInt(1000000000));
    }

    /**
     * 将参数按key值排序
     */
    public static String getSignatureContent(Map<String, String> paramMap) {

        Collection<String> keySet = paramMap.keySet();
        //签名内容
        StringBuilder content = new StringBuilder();
        //所有的键值
        List<String> keys = new ArrayList<String>(keySet);
        //排序
        Collections.sort(keys);

        //循环赋值
        for (String key : keys) {
            String value = paramMap.get(key);
            if (isNotEmpty(key) && isNotEmpty(value)) {
                content.append(key).append(value);
            }
        }

        return content.toString();
    }

    /**
     *字符串非空校验
     */
    private static boolean isNotEmpty(String str) {
        return str != null && str.length() != 0;
    }

    /**
     * 计算签名值
     */

    private static String computeSignature(String key, String data, String charset) {
        try {
            byte[] signData = sign(key.getBytes(charset), data.getBytes(charset));
            return byte2hex(signData);
        } catch (UnsupportedEncodingException ex) {
            throw new RuntimeException("不支持的算法: " + charset, ex);
        }
    }

    /**
     * 把字节流转换为十六进制表示方式。
     */
    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();
    }

    /**
     * 使用HMAC加密
     */
    private static byte[] sign(byte[] key, byte[] data) {
        try {
            //因为Mac类的getInstance()方法的调用时一个同步方法,可能被阻塞,所以使用原型模式来提高可靠性
            if (macInstance == null) {
                synchronized (LOCK) {
                    if (macInstance == null) {
                        macInstance = Mac.getInstance(ALGORITHM);
                    }
                }
            }

            Mac mac;
            try {
                mac = (Mac) macInstance.clone();
            } catch (CloneNotSupportedException e) {
                //如果不可复制,创建一个新的Mac对象
                mac = Mac.getInstance(ALGORITHM);
            }

            mac.init(new SecretKeySpec(key, ALGORITHM));
            return mac.doFinal(data);
        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException("不支持的算法: " + ALGORITHM, ex);
        } catch (InvalidKeyException ex) {
            throw new RuntimeException("非法key: " + key, ex);
        }
    }
}


详细示例代码请参见 SDK 源代码。

# 调用示例

1、设置参数值

 method = "sign/verify/p1"
 
 data = 签名数据
 
 signature = MEUCIH2eIxHO3UVWDkY8Du3h+ptUx387aOgtpIOlu1ET3jxiAiEAhLUL+ofXwHHUVXEix+kcKSBfCGFyi42EVMlmJcXdrWU=
 
 dataType = ORIGINAL

 format = "JSON"
 
 nonce = V2Yx5zNt1r
 
 signatureAlgorithm = SHA1withRSA
 
 t = 1668496549088
 
 signItemValue = MIICszCCAligAw****

 v = 1
 
 returnCert = true

 signType = CERT

 appKey = ODRp4fQmiQiVytrk

2、排序

 appKey = ODRp4fQmiQiVytrk
 
 data = 签名数据
 
 dataType = ORIGINAL

 format = "JSON"

 method = "sign/verify/p1"
 
 nonce = V2Yx5zNt1r

 returnCert = true

 signItemValue = MIICszCCAligAw****

 signType = CERT

 signature = MEUCIH2eIxHO3UVWDkY8Du3h+ptUx387aOgtpIOlu1ET3jxiAiEAhLUL+ofXwHHUVXEix+kcKSBfCGFyi42EVMlmJcXdrWU=

 signatureAlgorithm = SHA1withRSA
 
 t = 1668496549088

 v = 1

3、拼接参数名与参数值

appKeyODRp4fQmiQiVytrkdata签名数据dataTypeORIGINALformatJSONmethodsign/verify/p1nonceV2Yx5zNt1rreturnCerttruesignItemValueMIICszCCAligAwIBAgINAO2Cjz/tEqZSVvI/eDAKBggqgRzPVQGDdTBJMQswCQYDVQQGEwJDTjESMBAGA1UECAwJ5rGf6IuP55yBMRIwEAYDVQQHDAnljZfkuqzluIIxEjAQBgNVBAMMCXRlc3RTTTJDQTAeFw0yMjAzMTgwNTQwMTlaFw0yMzAzMTgwNTQwMTlaMIGjMQswCQYDVQQGEwJDTjEPMA0GA1UECAwG5rGf6IuPMQ8wDQYDVQQHDAbljZfkuqwxFTATBgNVBAoMDOaVsOWtl+iupOivgTERMA8GA1UECwwI5pm65oWnQ0ExMDAuBgNVBC0MJzFmZWM0ODA3MjlhYTQ0MjJiODk0YzhiMTM2YjFkNDU0MjAyMjAzNzEWMBQGA1UEAwwNd3d3LnF3ZmRjLmNvbTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABFjoFlPaMPGM7OAKEYpwwsJWKjr5w0qy32hLc/z4r065nPWJyZl+VkP/8EF0mRSrvjJz3tlR9OMiaDjjHwxW86KjgckwgcYwCQYDVR0TBAIwADAdBgNVHQ4EFgQU0HDEQ8QjtsYHokinKQvAK7z+FoEwSwYDVR0lAQH/BEEwPwYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUHAwgGCisGAQQBgjcKAwMGCWCGSAGG+EIEATAOBgNVHQ8BAf8EBAMCBsAwHAYDVR0RBBUwE4IRd3d3Lnh1amkudGVzdC5jb20wHwYDVR0jBBgwFoAUE3GRQ7iygyrxFixllDZ6SqWNJicwCgYIKoEcz1UBg3UDSQAwRgIhAIi2sGHKO+KkMX9MxC2c+u/p82t4SxqEjbDk96zluqmfAiEA00lg6iFIc03pofZbsA5uiA6vznSLKaru1A+nGqO78Uw=signTypeCERTsignatureMEUCIH2eIxHO3UVWDkY8Du3h+ptUx387aOgtpIOlu1ET3jxiAiEAhLUL+ofXwHHUVXEix+kcKSBfCGFyi42EVMlmJcXdrWU=signatureAlgorithmSHA1withRSAt1668496549088v1

4、生成签名

假设 secretKey 为 111111,则签名结果为:

F384EB51EFF959BF0AA7BA2C7F4759BD9D0F0D6ADE95E24F235CE7B4945DE1B2

# API调用协议

接口支持HTTP,HTTPS GET/POST请求,所有接口需在请求中加入公共参数,请求及返回结果都使用 UTF-8 字符集进行编码。

组装 HTTP(S) 请求

将所有参数名和参数值采用UTF-8进行 URL 编码(参数顺序可随意,但必须要包括签名参数),然后通过GETPOST发起请求,如:

https://api.spiderid.cn/openapi/svs/v1/sign/verify/p1?method=sign%2Fverify%2Fp1&t=1668497130259&v=1&sign=858BB6FAECEC3B52CBEDD9391743DB8A5E68A7EF75650C0F45F61406C2FD88E2&appKey=ODRp4fQmiQiVytrk&nonce=AqqqG1qWmt&format=JSON

# 注意事项

1、所有的请求和响应数据编码皆为UTF-8格式,URL 里的所有参数名和参数值请做 URL 编码。如果请求的 Content-Type 是 application/x-www-form-urlencoded,则 HTTP Body 体里的所有参数值也做 URL 编码;如果是 multipart/form-data 格式,每个表单字段的参数值无需编码, 但每个表单字段的 charset 部分需要指定为UTF-8。

2、参数名与参数值拼装起来的 URL 长度小于 1024 个字符时,可以用 GET 发起请求;参数类型含 byte[] 类型或拼装好的请求 URL 过长时,必须用 POST 发起请求。所有 API 都可以用 POST 发起请求,某些 API 只支持 POST 请求

3、POST请求请务必将业务参数放入请求Body中

# 公共参数

公共请求参数是指每个接口都需要使用到的请求参数,务必以url参数形式传入

名称 类型 是否必须 描述
appKey String 身份标识
sign String 签名结果串,请参看API签名机制
method String 服务方法/API接口名称
format String 返回值的类型,默认JSON
t String 时间戳
nonce String 唯一随机数,同样的值,10分钟内只能被使用一次
v String API 版本号,目前版本是1

请求示例:

GET:

https://api.spiderid.cn/openapi/svs/v1/sign/verify/p1?
&appKey=XXX
&sign=XXX
&method=XXX
&format=JSON
&t=时间戳
&nonce=随机数
&v=1
&<[具体接口特有的请求参数]>

POST:

https://api.spiderid.cn/openapi/svs/v1/sign/verify/p1?
&appKey=XXX
&sign=XXX
&method=XXX
&format=JSON
&t=时间戳
&nonce=随机数
&v=1
<[具体接口特有的请求参数]>

# 响应参数

调用 API 服务后返回数据采用统一格式,code0 ,请求成功,其他为失败,这时没有data结果信息

名称 类型 描述
code Integer 状态码,0请求成功,其他为失败,具体见 API错误码说明
message String 状态码的描述
data Object 结果信息,code为0时出现,具体看各个接口说明

# 成功示例

JSON示例

{
    "code": 0,
    "data": {
        "original": "签名数据",
        "cert": "MIICszCCAligAw******LKaru1A+nGqO78Uw=",
        "message": "success",
        "verification": 0
    },
    "requestId": "f100637335f7c9e77c0001f2d50c",
    "message": "success"
}

# 失败示例

JSON示例

{
    "code": 10008,
    "message": "App不存在或状态异常"
}

# API错误码说明

# 错误码解释

返回码 系统错误 备注
0 成功
10001 系统错误
10002 服务异常,请联系客服
10003 系统繁忙,请稍候再试
10005 租户不存在
10006 租户密钥错误
10007 该APP不属于此租户
10008 此租户已被禁用
10010 请求重复
10011 请求过期
10012 请求方法不存在
10021 App不存在
10022 App状态异常
10023 App被限流
10024 App签名错误
10031 暂不支持此方法
10032 没有访问此接口权限
10100 参数校验异常 (如:请求参数(name)不合法,请参考API文档)
10101 加密卡引擎加密失败
10102 加密卡引擎暂不支持此算法
10103 加密卡引擎解密失败
10104 加密卡签名验证失败
10105 加密卡暂不支持此摘要算法
10106 加密卡签名操作失败
10107 加密卡导入sm2加密密钥对异常
10108 通过生成随机数方式生成密钥对异常
10109 加密卡引擎生成密钥对失败
10110 加密卡异常
10111 文件引擎加密失败
10112 文件引擎暂不支持此算法
10113 文件引擎解密失败
10114 文件签名验证失败
10115 文件暂不支持此摘要算法
10116 文件签名操作失败
10117 文件导入sm2加密密钥对异常
10119 文件加密引擎生成密钥对失败
10120 文件引擎生成摘要失败
10121 修改密钥对数据失败
10122 不支持的加密引擎类型
10123 删除密钥对失败
10124 同步指定索引位置的SM2密钥信息失败
10125 同步指定索引位置的RSA密钥信息失败
10126 同步指定索引位置的SM4密钥信息失败
10127 生成密钥对失败
10201 证书内容不合法
10202 证书密钥信息不合法
10203 暂不支持该种算法证书
10204 签名操作失败
10205 签名验证失败
10206 证书注销列表(CRL)内容不合法
10207 证书增强密钥用法读取失败
10208 RSA加密操作失败
10209 RSA解密操作失败
10210 SM2加密操作失败
10211 SM2解密操作失败
10212 SM4加密操作失败
10213 SM4解密操作失败
10300 时间戳异常
10301 该证书不符合时间戳证书规范
10302 密钥对不存在
10400 证书不存在
10401 服务方法不支持RSA算法
10402 服务方法不支持SM2算法
最后更新于: 2/22/2023, 1:24:04 PM