基础文档

新手指南

平台概述

产品介绍

基础概念解释

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请求,可以开启参数加解密功能。

  1. 开启参数加解密后,请求参数中新增两个参数
名称 类型 是否必须 描述
encryptionEnable String 是否开启参数加解密
encMethod String 参数加解密方法(AES或者SM4)
  1. 开启参数加解密后,请求URL变为:

    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&encryptionEnable=true&encMethod=AES

    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&encryptionEnable=true&encMethod=AES

  2. SDK开启参数加密后,请求API时,在服务端进行解密后进行相应业务操作,服务端返回加密响应结果,需要对响应结果进行解密。

  3. 加密方法中的加密密钥是根据appSecret生成。

对称加解密工具类

Api 错误码说明

错误码解释

错误码 错误说明
30201000 系统错误
30201001 服务异常,请联系客服
30201002 远程服务错误
30201003 请求参数错误(XX)XX,请参考API文档
30201004 请求参数无效, 请参考API文档
30201005 不支持的签名算法XX
30201006 APP错误
30201007 验签失败
30201008 重复请求
30201009 请求过期
30201010 无法访问当前接口
30201011 请求的IP(XX) 不在允许访问接口的白名单中
30201012 请求超时
30201013 APP已禁用
30201014 服务限流
30201015 你的(XX)不支持密钥算法(XX)
30201016 你的(XX)没有权限使用此密钥算法(XX)
30201017 需要更新的证书(XX)不存在
30201018 需要注销的证书(XX)不存在
30201019 密钥对业务索引大于密钥对索引总数
30201020 当前应用可用密钥对数量为空
30201021 缺少交易账号
30201022 证书操作校验失败:(XX)
30201023 缺少请求参数:appkey
30201024 缺少交易密码
30201025 缺少扩展信息
30201026 App可用密钥对数已用完
30201027 流量达到上限

公共请求参数

公共请求参数详情及示例

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

名称 类型 是否必须 描述
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());
}
}

申请随机数工具类

GetRandom

申请随机数工具类

申请随机数

一、请求说明

二、请求参数(请求方式为 post)

名称 类型 是否必须 描述
appKey String 身份标识,注册后获得
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
sign String 签名结果串,请参看 API 签名机制
nLength String 随机数长度
x-request-id String 请求标识

注意:在postman中,除了x-request-id需要放在Headers中,其余参数均在Body中以application/x-www-form-urlencoded方式提交

代码实例

/**
* @author jcq
* @date 2021年06月07日 2:00 下午
* @Description :获取随机数示例
*/
public class GetRandom {

@Test
public void getRandom() throws Exception {
//返回结果
String result = "";
//请求url
String url = "http://cip.api.ht.demo.smartcert.cn/rest/cip/random";
//请求appKey
String appKey = "w8MuGC9C";
//签名算法
String signMethod = "HMAC-SHA256";
//签名算法版本
String signVersion = "1.0";
//服务方法接口名称
String method = "random";
//返回值类型
String format = "json";
//时间戳 日期格式按照 ISO8601 标准表示,并需要使用 UTC 时间。格式为 yyyy-MM-dd HH:mm:ss
String timestamp = "1623035141160";
//唯一随机数,同样的值,10 分钟内只能被使用一次
String nonce = "ldqxNxOkVB";
//API 版本号,目前版本是1
String version = "1";
//签名结果串,请参看 API 签名机制
String sign = "F97BA592A3C3056EEB64346A09180D044E07CD0F55FAECF05DDEB043305FF20A";
//请求的随机数长度
String nLength = "5000";

Map<String, String> params = new HashMap<String, String>();
params.put("appKey", appKey);
params.put("signMethod", signMethod);
params.put("signVersion", signVersion);
params.put("method", method);
params.put("format", format);
params.put("timestamp", timestamp);
params.put("nonce", nonce);
params.put("version", version);
params.put("sign", sign);
params.put("nLength", nLength);

Map<String,String> headerMap = new HashMap<String, String>();
//在请求头中增加x-request-id参数
headerMap.put("x-request-id", RandomStringUtils.randomAlphanumeric(10));

//发送http post请求,该方法返回的是String类型
result = WebUtils.doPost(url, params, Constants.CHARSET_UTF8, 15000, 60000, headerMap, null);
JSONObject jo = JSONObject.parseObject(result);
String code = jo.getString("code");
assert ("0".equals(code));
JSONObject data = jo.getJSONObject("data");
System.out.println("返回的随机数串:"+data.get("dataHexStr"));
}
}

三、响应参数

字段名称 描述
code 此次请求的结果 0-此次请求成功 其他-此次请求异常
message 此次请求结果的描述,success-成功 其他-异常参见异常码
requestId 此次请求的全局唯一标识
data 此次请求响应的负载,接口的响应信息主体
success 请求结果,true为成功,其余失败
字段名称 描述
incorrect 随机数结果响应码
dataHexStr 随机数-byte数组的字符串

四、成功示例

JSON示例

{
"code": 0,
"message": "success",
"requestId": "11112222",
"data": {
"incorrect": "0",
"dataHexStr": "e306abc2d3e2f110a1a970a6005cd1a583f590b545fe0d053da239acae4498f929ef389778504760bee9421f82e36d8fbf98b79a4b5a2948e7d796b2124cd604e6fb75ccdc6e53a3e61c64096446653ff5b591d13e505acffd31c768c19ce6294093623f6963da0865b027dfcff25c259c1aa84b72afe46f6eb2d96fde792e5f22cd05bdfb1be0ceeaa2114a8116782b331e9d10eca29b0dcc1e3b0a61cbf420bd6700996269e5cc0bcda9524d7d70bcbb5eaa4ce7f3c583cb50a5a694649ad102ccd932e4e9bd8dbfb93fbf96bf7ca7398d6905531c588cc816788e989a24c528223921b8c348e5e20e21b9d8c05db0364a3a2d51296ecc40a41d85e12a14122c767cae8b2f966db485047e207e2d4b3f12d7c5b86d2f6054925c78b671c2c64cec5f8effd2437e5a73ab198e2cc99c653aeb829304869b4c70b9bbb2f0be0f8b05a73828eb19005ce1fbe1daa7f059bdfcbbe5a123543810e449ab4625e73031f4f9200d4733f6253615b6cd307588e094361bd43d11d6b15a9bfc34af32f0480069182d123ab5876d21cfc37b11776d6cf5fdd2f5f881de488f7615c76d3620fc4781f76e838834e1e447300fc57983570e7e77e035fa6ffb4e43d5045c37cbc853a250e113f446a14e5e18f2cc1947e1bb585117b281def7b8913276fd67a2ea53521189d6d9b75c8bd894aa937164a8337a239b7765c9ece9c54005f0e6be2daa69872c9187ead95ce5548c325e86cb3b9c6386cac77232c30ae72e27c07f5a82e3f2125f1a21ffb74518575e09de6551b4bb0f67e03fdb90b0630974a209fa935c06afbcb3046beb8b3625d90fa11b728c8b5c8da43a543167c49a9732c22aa8a96c196eb3209dd0c218aac33189b6da716523c5b49195600c6c7fd924d6845594425bca8865aec9ddbac0670b52d24c3d901e838117e3087993e05cabb30ea468978835b5a8998de8304a56ce3e99ad18874e1ad3c21cbb72c6baad948d418be2f0aad3481f14050c1e5cf25392900f4e770ca3442b9fb0a8647440abdb1d7c36c67bbe79e8f5b9233ebfc32b1775791cc4ecc1cc11b4f86d20c4dd83d84f696406ef47f582329bcddebd2a799a27fc0684717822d692b3b482e734ee0364ffc08f8d1332516117945f424f8d6ca9e952028d69a700f17ca866290ea89f0bffa4b79f10591ed6cf10b745652be2283f723df43b84b92268287bb8d7c83471eb7ec7279f52954966acf08da04ed61d94e7664104cf50cb2f4bc4cd86980c9d7e177aac6924c61127eea7bc711a247620c966e6e0e6b61d7b0a1b23072ab7e995b582d3bd13a5ea91248eade6562ed2ab6855ef31ab02ad5a2ca9b9b21e878888f203cc6def8a4e038eea24487c59d177ce049516d8eecc5e8470f08b6d15161a7c1689847bb25e000f15ce4eda8e6e77a5749f1a9d5782e9afc1ea33e6ba3e7bb548cc5638bc5be7f1fd8989d8b96c65ef5d983d76c0039e83c646d76c17730a251800bba52cae0622a30dcfa64ed38b57a8cdbd6ad0c90f7af9238b998eacc6e07ce271b2eb7e5cd29ed0d4f5bf7a4e43f5235118cf2f1396ab79d47ac641a6b75dff093fc5df8086954b457907a9cacf16b9b55c2da451557ebd53b82acb43aa16fbc77ae9837c02c886ef0522be1dc49256c402fc680382ebd9ce376b179361f9ac5d686d088ebc88f8e91792f0242023b46e9371e4f0ecbe7413b4fa2f90b6ca097a07c8cc1f0abbb3c26ddc19c857388a534e67811d5b94856b6a8f6d1a0a42de7b77565b706759e6162f0993217b7474d258db1fbe3c643c9e20a59c6d208d59d1e6d861afe7f1a72bc6262115842da4c96571155d15f337b74afb31eb6fcc67ce49f13991112518622dc4f127c2ee626773eebd4dd75e0c0e05148f255794dc03a3aa019b2a29cda0f278a7abe5f564b9bfbb9bd1d6941948ecb8565f7a6e6d611eee43a93948920c9c5c768e7bb6ebe3bf69089163dab14e9a71d1f09c557cac5f0f3173d75f76aa6a8c5062c356b82ab927754989459073b40f6245a73bb2152c3f670c3a602ac5b0aa226da930ce35964e3bd3f2439f3d04240a632e8469f705184ba7c6d1af2f135b20d45654053cb6d6fce06c130f980989fe2dbe88e8066915f019db4a57bf15c820343a036dfdbede941a1048bc5dd2d74bda5b156c82141691e76efb0e97441e0b39a3347eb7ed289875ac4ae67aa8ef8a3d32ec012638e098d0a123011e250e53060c131e7dfe9a65eda9d4351f5573a5f21d4033f1679b3a2b4c9a82bbda4e8582f37c709c84bec6fc347713ba4c4aaa81db3a7f07f2529a862b4851b21415e4d7bb847afc7942207b37164ebb1420b2fded77a5359176855a54ab22b56ba313bc58f1949cde7932bf5068115299e3d3caf088c0e57d5cbbc52756820ecfb7d650b9bdae9f7cd96929ba7a2ca4f647297eeb70308c8ebac59c2a6c00760217dbafc1975f8d487bbf77c72aa8376e43e3096b2a55d76f2e614bc129b0ec760c96735861cab2609e29857cadad88de926ea79721192cfcb7754a0259f63afae330cf84b34fcd61259c568409bd051834a8313bb64b39613a9f2c87dfa7e9d16d8507da571069a2149997af9c33ecd7ea21e1afbecbab11fe4583623078d0d77f2a5f75cb3f9de91651e8029322bf06bd769cfb01824d7830471e27d5b793177390c7a3c2461061fa539f477475521577af4ef0bc2b1901602d683bed62ab3da62de9dca3b56dd532d1e0039568a4f828b7f50520f50c5402f9e089a87fcde1f8ba54210807b8d352682db9a24fff51a3df5ac7d1e3c58cef78f9fdcdccd5574f80eff1413220d59789ca4a1bac1dc6fe75a71667112ed1e132d12235c0843b14b1ec06cf0d4d765e66a677d3934a6316674743f0b5c7e8fadb98a74dd5af5cb4d8e5a4e9813caaa87199657267b83b08f4cbe66c025f5d3e619b83982bbd0a2057e4eefa8a28f49c702756f4d88706b976c0c250cb2c3d14802ff22b7584ff4ded6ddc200c1af7051b22bf284239af5036a3650403b4cdc9ee13853035ef52b559e68aaf711315d04bcf2b334b8238b64b74316504e45d0d15f819634ef2f1d504d879ac23fd6a90c1747d0ae1c13c1d968c7a43ad49ed7354339698e1ba0cadbdf5e153c8bd31fa6e6b5cfb7b8ca376bfaed9e448598df5e0ece9337c28d14386e7be92562870695b1ced25b60ad9f9508478804a1b396f293602ae96ee8f207034548bf8a9f45b138926fb3124f1a1d7f813c2d7c55e1fd0d9ff1763f71bd188dbed16bee1c0f1a8417ac4d0b0a5ad1825ac4d230d7e2c33e13e8febe1cc67d66d5d3c1f8c6c7344ead71e392a0c2156bc5d3597340d1c851279cc80b50ef763bb3cbd4e6c88ca44b27877ad973443a32dad82cf2a4e5b75e993987824d21b5e654f8b5c1db505f05e499332aea01e49eb9fb1643adcbb68cc784f3cf881703fe7744d28f5f4d5b3cb3b09065fa7023b8a5c32861982ef4180ac65a7805b160de97cdd2363bb10f60143c6644e66029babaeeb6462570eba1c39deb5202933a3ecd7cae5745f31754a3f88a1ecd7744ee2b7b36dbe3b174903e4d48252677011a88ed78b6ae2d638daa9638b2b0275a2064bd8534e85679e9a97f596bccd7a079f227aed846e4112fd6140240af76300aaeb9aeb181f44a9d05e0387320f8251cdfa5e9e778dd8acdf9759a7708d61b29c2fadd48a635631a54e52e2ba64be6474c7e1c028d9a62e5b73bfdf164ed6b7b74cbbf450b628ff3e404c71f420e6fb5dd897a8a8d4d76864bfd4ce6e0deea95cf1e0c11740ac944d4cf0c3741ab7c325b9c54c8bae40980095d1a2cd1017b05d222c5df935b475b98e4f10fa3474175c87e78a0de9d4b5e1ba41fd4c6a86843fbd257da4c57a00e5d70f618ab260cd935e2a39b04730570c3184891e6cb088076a4228861619d6ff96e813da3e4513ade6f20833ed101f292a4de34bb1d1e9073c713f80cf11d32e7729a893d8f05aef8856391948d7bd638625cd1c3f68d55bfd92d8f637c7978adf33dd5ad5b256c4109f1922470c82f9bde6b69dafb9bd0525f9683e5a02a136c4fd20a53ee0052685cd5b5f52d778d90b4094002b4aaee1c3f0bfb4846ff012fd8f4ec1ae54d111d81237d3b0881ef8923703d23266b0dd4832d10e261f45caa487bca8a6ebb792a1545ac055119f382ba3c23ce79d3f2ad9724fa54804e43ed9979b730e058b347cafae158c87a60d7dae78d57b42ce541dd216cadf77f0979404a58a33083bf2c0f1e99f99a4c133155ac9a806eb597ed06c6770b1b554713c2919f020dd33f1501df1ea3e484e2724f9bc85b1f50f2013cdefb1e55d3af0f2f20d3a9f72c99bd3e90afdebf4fb37afb376be9afe01107f74a7fa2cf312beab620d188336b4ec4f3fe2559d4ddc5b13033bd95f69eab73e5879e5fc61302c850d047e329f9dfd4fbf58db979f0324dc3ac6eb61dd00b0f55fbe139b71ed01ec9e7fce71a7bc2ace3c299ea557b9741bfbf29a50c6de2f1de887898c5466b79e533d9e9b4b825d033b5aa0088838e3a0bdb0f86dd373079342ba58871ec11ebb7c08eb5e0b5650ce778007146700dba94d4aba4a760a127e32f7a4334706be9fffe8df006e62ddd360d1643fcfb4ece549fc806f675c555771584ae02b895b90d7a180e87b71aaa89605fd92be8ee8f4a5db2e8522b73edb2cfab0ce8be07ebb907fd6eb40d9c34c730b3a7fd65ca6d62a0c82136430b0e2835597e00a6fe874cd76786f4e78f8017f89b08cdd139d1ef24670dab2eacfa70a6bdd41b27b4137c39288ea3bc2080ef93063d53defab1feeb0fc1fd269cbc64abd9a1f6cda8645bbb380ca1fdcfa4955e02de38521b7ae600182094ff7005f411b099d8b24f092939d680317c92578b8958262e614fa8c61f8aa3cf9164115de3a0efe428155ee3e564fd2c6a6c53a7e31e213b63b31055c259391630b1a5c86236e6e29fe554c3fd42159cff92a2db508bb89f170fdc891fdc994646a5f539268f0e3bc77c07132fd38c45c0ad7119f47978bb3e822a6934232f596c15fc4cf330259dc11012e44f4389b9a5017403424c56abfb0853dcab8f7beda4a7a3cfc1f9f044c4095cbb6b81bb83af1f17b9ee0e22ffbc00580f22d1572d2b0060c4fe38a43355fc85a5e148120f249752bbb9df84bf29b9c579acf249866e3c0fbf1c9259ea2db3a0125da9da6fa8dbc6c9efd0963507f5e8da6d1747a40fc2adf7d0066b505ecf5868a7464c890723be80d9649d96a2441afce649afe4f3397c685aebfe8b0d80df01dc4c0bc74eb225b05335807b410ca721dfdfc7c48e3cb770de105365dd41410054e01006cf7f0bd32e9395d487606d472e8139a209e73000e458e2ba764e0aa7bbac7f9c27c7d9b78f8ea4666a46efa023550c89351cba9f4b121c48e5a63e44da31a88c13516a56d7ebe58ee0797bfd4e7ec1da17b4051218483abf75a9e775cb272543bad7bd0dcf8f024e8bbd08d44831f970b6ff12d68c799b92af7e86a1e10ddd8064bd1544398181ffca91b2f80dd62b2daa7415792318069525515be1c228de47a70d2ab55625d1dfa3a0c0f0e47d691d92ff4f4dcec0fb1016930dd75997740f5a4de547a24a35a549786644401eb7116137edd7732a6e4a9bbe0785ad1d0cd57d2ae774a360f589f7771b2cf04da5265e4828b07b5334223c5a5c3907d88198691c5ac160c949f1685c2102d189c27dd51adbf25dbde552cd479f9f1a81ce9eb86ec215f996c34174a86b932365eba59c955d8ddc9c2a9e3869885eb035eb6f4ce50095c83b171a3b2c5678744acbbf82ca9e4313f95194dd36b10ff82ff68907a10c279237232e21c6b30261acc1e9d9741bd943c3db2d29fe13d434d0bdeb61b4b71d359fb619df9889fe0d502fb6f3163f86f81a43cbdebabeae1fa96f6de6107d3bc2f367ec2fca30ed784bea65a144f649fd54d72bf872d45962351f886c67fe23d4a4829e4dde25a0419dea68d405ecef5237a616c5e1535333dc203ae49a8998f2ce0a2b9a8455cb2af9159faa0d641b564e6b5bc4cea2220ca5a455c3eff6fa14ad7e49a9d46c101288df9da1fac9517586c728c8a0957d4d7a84f1927fb7096eb132a06a2863c640987b326cb9f14ca1010271ba3d9d55ecd4ebe73a2a7ba44dcd4d91f41674423ba3d0d1f096f476c4e44d68dfd33d5df8995da796743f5b5f1d02c2b3cb4feda057dcac6dc046d13a86f8b6bf546a50dd3b37b4252f1dca95bd46414abbe54f8f3ddd1643adff8c786fc1b2183722db21325c66680ce4613c7c51ae9ac8b9641ee3a50046c1a8405f07dab7f74e1eb2b6aa9be837dff2298503dfa5ba8f8854d37127ff485d4f93e20b3523935e1bcea06c9deb97f86dd1347173d411cf71c06b150ff48ebda6b55f573b867273a8df5daf4f92fb4f42714beac5c140042e42ef33eaf57e2d3e82529d082068c0a8164969ecb8c9b48a65cf09bb973b42e3ad7b18a3f3c3956e1f95beb4574fbe506e6f6e1777e768b3162724a6ea1837b7be973a69056faa02512ef5ffdf86d3bc53d66992137c2ff62d1348b1b02b23d9a9f99f90ec830e54925af322c38d4434ebde8f779eac4f2e208571242a21e51510ab39ad9fabc534bda4ea176c03ffb0cd15b02d7532a838698c9a0a6ae73ebcc8d268e7c163961a44eb155e8829be6eb770482ef2cb0ee9fcac01152f8e634535f27c36c8cedf19a9204c82051f4a7ccbf6d4ad4b9e2380517f7b3f9778a08320cfc48418ed55f7729acd94e6451a3054f97ec1f3de93c39adbf98ffc52c3ef9f49476da94b38c7f8efbf23bda1c36c1e273319f82627cd3f1e8c2c59fc91fd3cb72fe44da357205acc02da6301c4348c4b7f5e556513dbbf76e5fc2698aec9e17dd282cdd2ae294649e56d0ccf814f858f7405d0819c90a0cfe719ea7568b4bb668ae1be2c568500a37cf7d7658d9e19215b258ef89de772d459fab85f4ced255ceeca66ff382d9e8269b87d98536d87e029baae3fda8ee8d18ff2850b9ed1ff91070c8d0bd9ab4bace21d7"
},
"success": true
}

五、失败示例

JSON示例

{
"code": 30201005,
"message": "签名算法HMAC-SHA2561不支持",
"requestId": "11112222",
"data": null,
"success": false
}

对称加解密工具类

AES加解密工具类

AES加解密工具类

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;


public class AesUtils {

private static final String KEY_ALGORITHM = "AES";
static Charset charset = Charset.forName("UTF-8");
private static final String SHA1PRNG ="SHA1PRNG";
private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";

/**
* AES对称-加密操作
*
* @param secretKey 秘钥
* @param data 需要进行加密的原文
* @return 数据密文,加密后的数据,进行了Base64的编码
* @throws Exception
*/
public static String encrypt(String data, SecretKey secretKey) throws Exception {
byte[] result = aes(data.getBytes(), Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder().encodeToString(result);
}

public static String decryptByPassword(String keyStr, String data) throws Exception {
// 转换密钥
Key key = new SecretKeySpec(keyStr.getBytes(), KEY_ALGORITHM);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 解密
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(Base64.getDecoder().decode(data));
return new String(result);
}
/**
* AES对称-解密操作
*
* @param secretKey 秘钥
* @param data 需要解密的数据(数据必须是通过AES进行加密后,对加密数据Base64编码的数据)
* @return 返回解密后的原文
* @throws Exception
*/
public static String decrypt(String data, SecretKey secretKey) throws Exception {
byte[] result = aes(Base64.getDecoder().decode(data), Cipher.DECRYPT_MODE, secretKey);
return new String(result, charset);
}

/**
* 生成密钥
*
* @return
* @throws NoSuchAlgorithmException
*/
public static SecretKey generateKey(String password) throws NoSuchAlgorithmException {
KeyGenerator secretGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
SecureRandom secureRandom = SecureRandom.getInstance(SHA1PRNG);
secureRandom.setSeed(password.getBytes());
secretGenerator.init(128, secureRandom);
SecretKey secretKey = secretGenerator.generateKey();
return secretKey;
}

private static byte[] aes(byte[] contentArray, int mode, SecretKey secretKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(mode, secretKey);
return cipher.doFinal(contentArray);
}
}

SM4加解密工具类

SM4加解密工具类

import cn.unitid.easypki.crypto.CipherException;
import cn.unitid.easypki.crypto.SM4SymmetricCipher;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

/**
* 描述: SM4加解密
*
* @author whx on 2022/10/08
*/
public class SM4Utils {

private static final String KEY_ALGORITHM = "AES";
public static final String ECB = "ECB";
static Charset charset = Charset.forName("UTF-8");


/**
* SM4加密
*
* @param data 需要进行加密的原文
* @param secretKey 密钥
* @return 数据密文,加密后的数据,进行了Base64的编码
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws CipherException
*/
public static String encrypt(String data,SecretKey secretKey) throws NoSuchAlgorithmException, InvalidKeyException, CipherException{
byte[] result = sm4(data.getBytes(), Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder().encodeToString(result);
}

/**
* SM4解密
*
* @param data 需要进行加密的原文
* @param secretKey 密钥
* @return 返回解密后的原文
* @throws InvalidKeyException
* @throws CipherException
* @throws NoSuchAlgorithmException
*/
public static String decrypt(String data, SecretKey secretKey) throws InvalidKeyException, CipherException, NoSuchAlgorithmException {
byte[] result = sm4(Base64.getDecoder().decode(data), Cipher.DECRYPT_MODE, secretKey);
return new String(result, charset);
}


/**
* 生成密钥
*
* @return
* @throws NoSuchAlgorithmException
*/
public static SecretKey generateKey(String password) throws NoSuchAlgorithmException {
KeyGenerator secretGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
secretGenerator.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = secretGenerator.generateKey();
return secretKey;
}

private static byte[] sm4(byte[] contentArray, int mode, SecretKey secretKey) throws NoSuchAlgorithmException, InvalidKeyException, CipherException {
SM4SymmetricCipher cipher = new SM4SymmetricCipher(ECB);
cipher.init(mode, secretKey.getEncoded());
cipher.update(contentArray);
byte[] bytes = cipher.doFinal();
return bytes;
}
}