基础文档

新手指南

平台概述

产品介绍

基础概念解释

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 签名示例代码

签名例子:

请求示例:

import javax.crypto.Mac;

import javax.crypto.spec.SecretKeySpec;

import java.io.UnsupportedEncodingException;

import java.security.InvalidKeyException;

import java.security.NoSuchAlgorithmException;

import java.text.DateFormat;

import java.text.SimpleDateFormat;

import java.util.*;

/**

* 获取参数签名

* @author 壹证通

*/

public class GetSignature {

/**密钥*/

private static String secretKey = "XXX";

private static String charset = "UTF-8";

private static final String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss";

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("signMethod", "HMAC-SHA256");

paramMap.put("signVersion", "1");

paramMap.put("method", "XXX");

paramMap.put("format", "JSON");

paramMap.put("timestamp", getTime());

paramMap.put("version", "1");

paramMap.put("nonce", getNonce());

//具体接口参数,如姓名实名认证

paramMap.put("realname", "XXX");

paramMap.put("idcard", "XXX");

//获取待签名内容,排序

String signContent = getSignatureContent(paramMap);

System.out.println("待签名内容:" + signContent);

//计算签名

String sign = computeSignature(secretKey, signContent, charset);

System.out.println("签名后:" + sign);

}

/**

* 格式化时间

*/

private static String getTime() {

TimeZone tz = TimeZone.getTimeZone("UTC");

DateFormat df = new SimpleDateFormat(TIMESTAMP_FORMAT);

df.setTimeZone(tz);

return df.format(new Date());

}

/**

* 生成随机数

*/

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、设置参数值

2、排序

3、拼接参数名与参数值

appKey1111111formatJSONidcard111111111111111111methodrealid.idcard.verifynonce1111111 realname 张三 signMethodHMAC-SHA256signVersion1timestamp2018-02-07 02:50:21version1

4、生成签名 假设 secretKey 为 111111,则签名结果为:

E41E6FDA4D24B27AE78281F6D71D790F55097CD558BB377A3F9343F07ADED112

API 调用协议

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

组装 HTTP(S) 请求

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

HTTP请求http://222.190.151.234:2016/api?appKey=1111111&format=JSON&method=realid.idcard.verify&signMethod=HMAC-SHA256&signVersion=1&version=1&realname=张三&idcard=111111111111111111&nonce=随机数&timestamp=2018-02-07 02:50:21&sign=E41E6FDA4D24B27AE78281F6D71D790F55097CD558BB377A3F9343F07ADED112

HTTPS请求https://222.190.151.234:2016/api?appKey=1111111&format=JSON&method=realid.idcard.verify&signMethod=HMAC-SHA256&signVersion=1&version=1&realname=张三&idcard=111111111111111111&nonce=1111111&timestamp=2018-02-07 02:50:21&sign=E41E6FDA4D24B27AE78281F6D71D790F55097CD558BB377A3F9343F07ADED112

注意事项

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 发起请求。

公共参数

公共请求参数是指每个接口都需要使用到的请求参数

名称 类型 是否必须 描述
appKey String 身份标识,注册后获得
sign String 签名结果串,请参看 API 签名机制
signMethod String 签名算法,默认HMAC-SHA256
signVersion String 签名算法版本,目前是1
method String 服务方法/API 接口名称
format String 返回值的类型,默认JSON
timestamp String 时间戳,日期格式按照 ISO8601 标准表示,并需要使用 UTC 时间。格式为 yyyy-MM-dd HH:mm:ss
nonce String 唯一随机数,同样的值,10 分钟内只能被使用一次
version String API 版本号,目前版本是1

请求示例:

http或https://222.190.151.234:2016/具体接口地址? &appKey=XXX &sign=XXX
&signMethod=HMAC-SHA256 &signVersion=1 &method=XXX &format=JSON
&timestamp=2018-02-07 02:50:21 &nonce=随机数 &version=1
&<[具体接口特有的请求参数]>

响应参数

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

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

成功示例

JSON示例

{
"code": 0,
"requestId": "dsd24...",
"data": {
......
},
"message": "比对成功"
}

失败示例

JSON示例

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

Api 错误码说明

错误码解释

返回码 系统错误
10001 系统错误
10002 服务异常,请联系客服
10003 远程服务错误
10005 请求参数(XX)XX,请参考 API 文档(如:请求参数(name)不合法,请参考 API 文档)
10006 签名算法不支持
10008 App 不存在或状态异常
10009 App 签名错误
10010 请求重复
10011 请求过期
10012 没有访问此接口权限
10013 请求 IP(XX)不在允许访问接口白名单(如:请求 IP(127.0.0.1)不在允许访问接口白名单)
10014 请求超时
10015 API 超过最大可访问次数
10016 App 已被禁用
10017 服务限流
10018 余额不足
10019 账户已冻结
10020 请求数据过大
返回码 业务错误描述
30100 主题项不规范
30101 证书解析异常
30102 证书已失效
30103 添加操作失败
30104 更新操作失败
30105 删除操作失败
30106 无此证书
30107 证书类型未找到
30108 证书下载失败
30201 CA 中心参数校验异常
30202 CA 中心数据转换异常
30301 调用 RA 服务,发生的基础异常
30302 调用 RA 服务,签发异常
30303 调用 RA 服务,注销异常
30304 调用 RA 服务,延期异常
30304 调用 RA 服务,重发异常
30304 调用 RA 服务,更新异常
30304 调用 RA 服务,证书名称重复

公共请求参数

公共请求参数详情及示例

公共请求参数是指每个接口都需要使用到的请求参数

名称 类型 是否必须 描述
appKey String 身份标识,注册后获得
sign String 签名结果串,请参看 API 签名机制
signMethod String 签名算法,默认HMAC-SHA256
signVersion String 签名算法版本,目前是1
method String 服务方法/API 接口名称
format String 返回值的类型,默认JSON
timestamp String 时间戳,日期格式按照 ISO8601 标准表示,并需要使用 UTC 时间。格式为yyyy-MM-dd HH:mm:ss
nonce String 唯一随机数,同样的值,10 分钟内只能被使用一次
version String API 版本号,目前版本是 1

证书业务辅助工具类

证书 SDK 相关操作的辅助工具类,以供开发者参考.

P10Util

P10 操作工具类

根据主题项生成 P10



import cn.unitid.ca.sdk.dto.cert.BaseCertificate;
import cn.unitid.ca.sdk.utils.P10Util;
import org.junit.Test;

/**
* PkCS10生成测试
*/
public class PkCS10Test {

/**
* 根据主题项生成P10
*
* @throws Exception Exception
*/
@Test
public void testGenSM2PKCS10() throws Exception {
P10Util.P10Info p10Info = P10Util.p10Structure("CN=诸葛亮,OU=蜀汉,O=刘备CA,ST=西蜀,L=成都,C=CN");

System.out.println("P10 BASE64 字符串形式:" + p10Info.getP10());
System.out.println("KCS#10 certification request 形式:" + p10Info.getPkCs10());
System.out.println("公钥:" + p10Info.getSm2PublicKey());
System.out.println("私钥:" + p10Info.getSm2PrivateKey());
}

/**
* 根据证书生成P10
*
* @throws Exception Exception
*/
@Test
public void testGenSM2PKCS10ByCert() throws Exception {
//构造一个证书对象
BaseCertificate certificate = new BaseCertificate();
//设置证书基本信息
certificate.setCommonName("诸葛亮");
certificate.setOrganization("刘备CA");
certificate.setOrganizationUnit("蜀汉");
certificate.setCountry("CN");
certificate.setProvince("西蜀");
certificate.setCity("成都");

P10Util.P10Info p10Info = P10Util.p10Structure(certificate);

System.out.println("P10 BASE64 字符串形式:" + p10Info.getP10());
System.out.println("KCS#10 certification request 形式:" + p10Info.getPkCs10());
System.out.println("公钥:" + p10Info.getSm2PublicKey());
System.out.println("私钥:" + p10Info.getSm2PrivateKey());
}

/**
* 根据主题项生成RSA P10
*
* @throws Exception Exception
*/
@Test
public void testRsaP10() throws Exception {
String subject = "CN=诸葛亮,OU=蜀汉,O=刘备CA,ST=西蜀,L=成都,C=CN";
String pkcs10 = P10UtilWithRSA.genP10(subject);
System.out.println(pkcs10);
}

}

AdapteBaseCertificate

证书信息读取工具类

根据证书base64字符串读取证书信息


import cn.unitid.ca.sdk.dto.cert.AdapteBaseCertificate;
import org.junit.Test;

import java.util.HashMap;

/**
* @author YRJ
* @date 2019/5/6
*/
public class CertReaderTest {

@Test
public void adaptCert_Test() throws Exception{
String base64Cert = "MIICqzCCAlKgAwIBAgIIIBkFBgAAgJcwCgYIKoEcz1UBg3UwdjEcMBoGA1UEAwwTU21hcnRDQV9UZXN0X1NNMl9DQTEVMBMGA1UECwwMU21hcnRDQV9UZXN0MRAwDgYDVQQKDAdTbWFydENBMQ8wDQYDVQQHDAbljZfkuqwxDzANBgNVBAgMBuaxn+iLjzELMAkGA1UEBhMCQ04wHhcNMTkwNTA2MDY0NzEyWhcNMjAwNTA2MDY0NzE0WjCBwDFQME4GA1UELQxHYXBpX2NhX1RFU1RfVE9fUEhfUkFfMl9VU0wxM1NTMV9hcGlfY2FfYzVlMjcwYmI1OTMwNDg4MGJiZDM5NTRlOTNjN2JiNjIxCzAJBgNVBAYTAkNOMQ8wDQYDVQQIDAbmsZ/oi48xDzANBgNVBAcMBuWNl+S6rDEQMA4GA1UECgwHU21hcnRDQTESMBAGA1UECwwJ5aO56K+B6YCaMRcwFQYDVQQDDA7mtZnmsZ/opb/muZZAMTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABIZVFqTa9ar4WScUO6D2Ww/qCwyglNGVi5wkh89DdQXvsY2RkHqlfdhg4NifbPMg9kP4LVst0HVUsHIzaHevwSijfzB9MAsGA1UdDwQEAwIGwDAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBQ6Lpzol3uyBwOEjSc2S6Td1HpdLjAfBgNVHSMEGDAWgBRc87oljMJlDOxn777djWunXq/jrDAgBggqgRzQFAQBAwQUExI2NjQ2NDg4NDk0MTM2NDY0NjcwCgYIKoEcz1UBg3UDRwAwRAIgW+cwWylLKsuAhqx3dNt/UHriw2oq/QspDIsW9v+qHb8CICyie8vv1ZdaY4OsjJdAhd1/fuNn/sT0RYUIz03psQ6q";
AdapteBaseCertificate baseCertificate = new AdapteBaseCertificate();
baseCertificate.convert(base64Cert);

// 获取证书主题项集合
HashMap<String, String> subjectExtensionMap = baseCertificate.getSubjectExtension(base64Cert);
// 获取证书ID
String certId = subjectExtensionMap.get("UniqueIdentifier");
System.out.println(certId);
// 获取证书算法
System.out.println(baseCertificate.getAlgorithm());
}
}