数据中枢自建系统数据采集接口注册规范

版本编号 *变化状态 变更内容和范围 变更日期 变更人 批准日期 批准人
1.0.0 A 新增 2023-11-09 高玉坤 2023-11-10 程揭章

*变化状态:A——增加,M——修改,D——删除,N——正式发布

1 引言

1.1 执行摘要

  • 为数据中枢采集各个子系统数据,提供方API接口注册的规范,包括HTTP请求方式,数据体机构要求。

请求流程:

申请流程:

API接口规范:

请求数据:

请求样例

  • 请求方法:POST
  • 请求头:
1
2
3
4
5
{
"data_type_code": "SZZSPT009-01JA01CZRKSJ-001",
"cursor": "xxxxxxx",
"limit": 1000
}

参数说明:

参数 必须 说明
cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用不填
limit 分页,预期请求的数据量,取值范围 1 ~ 1000
data_type_code 原始库数据分类类型(中枢统一分配)

返回结果:

1
2
3
4
5
6
7
8
9
10
11
12
{
"errcode": 0,
"errmsg": "成功",
"data_type_code": "SZZSPT009-01JA01CZRKSJ-001",
"next_cursor": "5",
"encrypt_data": [
"c4cf7fba764b7733ff91c20c6a15f2e344a003802fc74d996bfbb7f2c730c0fa5c11288e1418b55a149d7e22c789f8f3394170c1bb42cf9d82a8d27220be5fc58311277774c6849133842b4ea932bf16",
"c4cf7fba764b7733ff91c20c6a15f2e344a003802fc74d996bfbb7f2c730c0fa821c481492b48dbf4fad6785c3473703394170c1bb42cf9d82a8d27220be5fc58311277774c6849133842b4ea932bf16",
"c4cf7fba764b7733ff91c20c6a15f2e344a003802fc74d996bfbb7f2c730c0fa1f271faea917383edf8a7f8eee69222d394170c1bb42cf9d82a8d27220be5fc58311277774c6849133842b4ea932bf16",
"c4cf7fba764b7733ff91c20c6a15f2e344a003802fc74d996bfbb7f2c730c0fa66eefe3d173a39a8978201f3dfc5c4c1394170c1bb42cf9d82a8d27220be5fc58311277774c6849133842b4ea932bf16"
]
}

返回结果参数说明:

参数 说明
errmsg 返回码
errmsg 对返回码的文本描述内容
data_type_code 原始库数据分类类型(中枢统一分配)
next_cursor 分页游标,下次请求时填写以获取之后分页的记录。如果该字段返回空则表示已没有更多数据
data 数据列表
encrypt_data 数据加密 (SM4加密的数据: 原始数据的格式应为{key:value}的json格式)

附录:

全局返回码说明如下:

错误码 错误说明 排查方法
-1 系统繁忙 服务器暂不可用,建议稍候重试。建议重试次数不超过3次。
0 请求成功 接口调用成功
41001 缺少sign参数 确认参数
90305 参数错误,errmsg中有提示具体哪个参数有问题 确认参数

SM4国密算法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

package com.crypto.gm;

import java.math.BigInteger;

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

public class SMHelper {
private static final String DEFAULT_ENCODING = "UTF-8";

/**
* SM4加密
* @param key UTF8编码后的长度必须为128位,即16个英文字符
* @param text 原文
* @return
*/
public static String sm4Encrypt(String key, String text) {
try {
byte[] data = SM4Util.encryptEbc(key.getBytes(DEFAULT_ENCODING), text.getBytes(DEFAULT_ENCODING));
return ByteUtils.toHexString(data);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* SM4解密
* @param key 与SM4加密时使用的值一致
* @param cipherText SM4加密后的文本
* @return
*/
public static String sm4Decrypt(String key, String cipherText) {
try {
byte[] data = SM4Util.decryptEbc(key.getBytes(DEFAULT_ENCODING), ByteUtils.fromHexString(cipherText));
return new String(data, DEFAULT_ENCODING);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* SM3生成哈希值
* @param text 原文
* @return
*/
public static String sm3Hash(String text) {
try {
return ByteUtils.toHexString(SM3Util.hash(text.getBytes(DEFAULT_ENCODING)));
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* SM3检验哈希值
* @param text 原文
* @param hash 哈希值
* @return
*/
public static boolean sm3verify(String text, String hash) {
return hash.equals(sm3Hash(text));
}

/**
* SM2获取私钥与公钥
* @return
*/
public static SM2Keys sm2GengenerateKeys() {
AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPair();

ECPrivateKeyParameters priKey = (ECPrivateKeyParameters)keyPair.getPrivate();
String priKeyStr = ByteUtils.toHexString(priKey.getD().toByteArray());

ECPublicKeyParameters pubKey = (ECPublicKeyParameters)keyPair.getPublic();
String pubKeyStr = ByteUtils.toHexString(pubKey.getQ().getEncoded(false));

return new SM2Keys(priKeyStr, pubKeyStr);
}

/**
* SM2使用公钥加密
* @param publicKey 公钥
* @param text 原文
* @return
*/
public static String sm2Encrypt(String publicKey, String text) {
try {
ECDomainParameters domainParams = SM2Util.getDomainParams();
ECPoint point = domainParams.getCurve().decodePoint(ByteUtils.fromHexString(publicKey));
ECPublicKeyParameters ecpKey = new ECPublicKeyParameters(point, domainParams);
byte[] encData = SM2Util.encrypt(ecpKey, text.getBytes(DEFAULT_ENCODING));
return ByteUtils.toHexString(encData);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* SM2使用私钥解密
* @param privateKey 私钥
* @param text SM2加密后的文本
* @return
*/
public static String sm2Decrypt(String privateKey, String cipherText) {
try {
//16进制转为BigInteger
BigInteger biKey = new BigInteger(privateKey, 16);
ECPrivateKeyParameters ecpKey = new ECPrivateKeyParameters(biKey, SM2Util.getDomainParams());
byte[] decData = SM2Util.decrypt(ecpKey, ByteUtils.fromHexString(cipherText));
return new String(decData, DEFAULT_ENCODING);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* SM2使用私钥签名
* @param privateKey 私钥
* @param text 原文
* @return
*/
public static String sm2Sign(String privateKey, String text) {
try {
//16进制转为BigInteger
BigInteger biKey = new BigInteger(privateKey, 16);
ECPrivateKeyParameters ecpKey = new ECPrivateKeyParameters(biKey, SM2Util.getDomainParams());
byte[] signData = SM2Util.sign(ecpKey, null, text.getBytes(DEFAULT_ENCODING));
return ByteUtils.toHexString(signData);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* SM2使用公钥校验签名
* @param publicKey 公钥
* @param text 原文
* @param sign 签名
* @return
*/
public static boolean sm2Verify(String publicKey, String text, String sign) {
try {
ECDomainParameters domainParams = SM2Util.getDomainParams();
ECPoint point = domainParams.getCurve().decodePoint(ByteUtils.fromHexString(publicKey));
ECPublicKeyParameters ecpKey = new ECPublicKeyParameters(point, domainParams);
return SM2Util.verify(ecpKey, null, text.getBytes(DEFAULT_ENCODING), ByteUtils.fromHexString(sign));
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* 存储公钥与私钥信息
*
*/
public static class SM2Keys {
private String privateKey;
private String publicKey;
public SM2Keys(String privateKey, String publicKey) {
this.privateKey = privateKey;
this.publicKey = publicKey;
}
public String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

package com.crypto.gm;

import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class SM4Util {
static {
//提供器添加到JDK中
Security.addProvider(new BouncyCastleProvider());
}
private static final String ALGORITHM_NAME = "SM4";
private static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
private static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
private static final int DEFAULT_KEY_SIZE = 128;

/**
* 生成随机密钥
* @return
* @throws GeneralSecurityException
*/
public static byte[] generateKey() throws GeneralSecurityException {
KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
kg.init(DEFAULT_KEY_SIZE, new SecureRandom());
return kg.generateKey().getEncoded();
}
/**
* EBC加密
* @param key
* @param data
* @return
* @throws GeneralSecurityException
*/
public static byte[] encryptEbc(byte[] key, byte[] data) throws GeneralSecurityException {
Cipher cipher = generateCipher(Cipher.ENCRYPT_MODE, key, null);
return cipher.doFinal(data);
}

/**
* EBC解密
* @param key
* @param cipherData
* @return
* @throws GeneralSecurityException
*/
public static byte[] decryptEbc(byte[] key, byte[] cipherData) throws GeneralSecurityException {
Cipher cipher = generateCipher(Cipher.DECRYPT_MODE, key, null);
return cipher.doFinal(cipherData);
}

/**
* CBC加密
* @param key
* @param iv
* @param data
* @return
* @throws GeneralSecurityException
*/
public static byte[] encryptCbc(byte[] key, byte[] iv, byte[] data) throws GeneralSecurityException {
Cipher cipher = generateCipher(Cipher.ENCRYPT_MODE, key, iv);
return cipher.doFinal(data);
}

/**
* CBC解密
* @param key
* @param iv
* @param cipherData
* @return
* @throws GeneralSecurityException
*/
public static byte[] decryptCbc(byte[] key, byte[] iv, byte[] cipherData) throws GeneralSecurityException {
Cipher cipher = generateCipher(Cipher.DECRYPT_MODE, key, iv);
return cipher.doFinal(cipherData);
}

/**
* 获取Cipher
* @param mode
* @param key
* @param iv
* @return
* @throws GeneralSecurityException
*/
private static Cipher generateCipher(int mode, byte[] key, byte[] iv) throws GeneralSecurityException {
String algorithmName = iv == null ? ALGORITHM_NAME_ECB_PADDING : ALGORITHM_NAME_CBC_PADDING;
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
if (iv != null) {
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(mode, sm4Key, ivParameterSpec);
} else {
cipher.init(mode, sm4Key);
}
return cipher;
}
}