星火印¶
星火印是星火链网主链上的一项基础性公共服务,基于BID标识体系,面向骨干节点用户及可信企业用户提供便捷的电子数据存证和核验服务。
简介¶
欢迎使用星火·链网 星火印API接口文档。
1、本文档用于指导可信用户使用星火印完成数据上链。
2、本文档的接口采用HTTPS加密方式请求。
请求¶
访问星火·链网 星火印API需要使用https协议,并进行数字签名。
路径¶
API地址:
测试网络环境:https://test-stamp.bitfactory.cn/api
生产环境:https://stamp.bitfactory.cn/api
请求路径=API地址+接口名称,比如hash存证接口的名称是/evidence/hash,则请求路径为:
https://test-stamp.bitfactory.cn/api/evidence/hash
方法¶
所有的请求遵循RESTFUL方法
参数¶
通用请求头 headers¶
字段名 |
描述 |
---|---|
request_id |
请求号,由接入客户创建唯一字符串,长度不超过32位 |
access_key |
访问识别码,当您在星火印成功上传SM2公钥后会获得一个access_key |
nonce |
请求时间, 必须以 Unix Time 的格式发送, nonce与服务器时间不得超过正负900秒,否则请求将视为无效 |
signature |
使用你的SM2私钥进行签名后的字符串,具体签名的方法后面会进一步描述 |
例如:
"request_id": "2XiTgZ2oVrBgGqKQ1ruCKh"
"access_key": "2y7cg8kmoGDrDBXJLaizoD"
"nonce": "1464594744"
"signature": "cdVtQ52evi4YDIuygRRiGhosn5XZyDH63LhNMk10I0LFBAamfuEBb6A2vlynVYll2ASzC/yolU/pbEAZ0zxdtg=="
通用应答参数¶
字段名 |
描述 |
---|---|
code |
状态码 |
data |
返回数据 |
message |
错误描述 |
例如:
{
"data": {
"attestationId": "rBgGqKQ1ruCKhXiTgZ2oVr",
},
"message": "string",
"code": "string"
}
错误码¶
错误码 |
描述 |
---|---|
200 |
操作成功 |
500 |
系统内部错误 |
401 |
暂未登录或token已经过期 |
403 |
没有相关权限 |
1001 |
参数检验失败 |
1002 |
账户余额不足 |
1003 |
存证不存在 |
1004 |
存证失败 |
1005 |
存证处理中 |
1006 |
提取码错误 |
1007 |
存证文件已经过期 |
1008 |
请输入最大6个字符的标签 |
1009 |
请输入最大20个字符的文件名称 |
1010 |
请输入正确格式的文件hash值 |
1011 |
该文件已经做过存证 |
1012 |
请勿提交相同文件hash |
1013 |
存证失败,已存在相同文件hash的存证数据 |
1014 |
存证失败,请勿提交相同文件 |
1015 |
存证失败,文件有安全问题 |
2001 |
存证文件不存在 |
2002 |
上传文件失败 |
3001 |
用户不存在 |
3002 |
验证码错误 |
4001 |
业务流水不唯一 |
4002 |
权限校验失败 |
接口¶
初始化请求参数¶
// 构建请求
HttpRequest httpRequest = HttpUtil.createPost(uri + apiName);
// 构建请求头
Map<String ,String> headers = new HashMap<>();
headers.put("request_id", requestId);
headers.put("access_key", accessKey);
headers.put("nonce",nonce);
headers.put("signature",signatureData);
httpRequest.addHeaders(headers);
文件存证(分两步):¶
上传文件 - /file/upload¶
客户可以通过该接口上传文件并获取文件id,文档类文件最大限制150M,图片类文件最大10M,音频和视频类文件最大550M,文件未存证时数据会在存储7天后删除。
form-data¶
参数名 |
描述 |
是否可选 |
---|---|---|
file |
文件 |
必选 |
type |
doc:文档pic:图片audio:音频video:视频 |
必选 |
返回的data¶
字段名 |
描述 |
---|---|
fileKey |
文件id |
以java为例:
// 构建请求参数
httpRequest.form("file",new File("/tmp/背景图.png"));
httpRequest.form("type","pic");
HttpResponse httpResponse = httpRequest.execute();
String result = httpResponse.body();
返回结果示例: a.接口调用成功,则返回JSON数据示例为::
{
"code": "200",
"data": {
"fileKey": "1544567382363930624"
},
"message": "操作成功"
}
b.接口调用失败,则返回JSON数据示例为::
{
"code": "1001",
"message": "fileLabel文件标签不能为空"
}
文件存证 - /evidence/file¶
用户进行文件存证
请求参数¶
参数名 |
描述 |
是否可选 |
---|---|---|
fileLabel |
文件标签 |
必选 |
files |
文件id列表 |
必选 |
files[0] |
文件id |
必选 |
返回的data¶
调用文件接口成功后会返回文件id对应的存证id
字段名 |
描述 |
---|---|
list |
bean对象列表 |
bean.id |
文件id |
bean.attestationId |
存证id |
以java为例:
// 构建请求参数
List<Long> list = new ArrayList<>();
list.add(1529663660129480704L);
EvidenceFileParam evidenceFileParam = new EvidenceFileParam();
evidenceFileParam.setFileLabel("标签");
evidenceFileParam.setFiles(list);
httpRequest.body(JSONUtil.toJsonStr(evidenceFileParam));
HttpResponse httpResponse = httpRequest.execute();
String result = httpResponse.body();
返回结果示例: a.接口调用成功,则返回JSON数据示例为::
{
"code":"200",
"data":[
{
"attestationId":"did:bid:ef23cydtVMQit888kfwqrZAJCccet2qQM",
"id":"1544567382363930624"
}
],
"message":"操作成功"
}
b.接口调用失败,则返回JSON数据示例为::
{
"code": "500",
"message": "文件类型不支持"
}
hash存证(sha256) - /evidence/hash¶
用户进行hash存证。
请求参数¶
参数名 |
描述 |
是否可选 |
---|---|---|
fileLabel |
文件标签 |
必选 |
list |
HashInfo对象列表 |
必选 |
HashInfo.filename |
文件名 |
必选 |
HashInfo.fileHash |
文件hash |
必选 |
返回的data¶
调用hash存证接口成功后会返回存证id列表
字段名 |
描述 |
---|---|
list |
bean对象列表 |
bean.hash |
文件hash |
bean.attestationId |
存证id |
以java为例:
// 构建请求参数
List<EvidenceHashParam.HashInfo> list = new ArrayList<>();
EvidenceHashParam.HashInfo hashInfo1 = new EvidenceHashParam.HashInfo();
hashInfo1.setFilename("test1");
hashInfo1.setFileHash("98df1f1dfb3b1a123c1517912dc70447aa61c6be532ac99de973abb6219e1653");
list.add(hashInfo1);
EvidenceHashParam evidenceHashParam = new EvidenceHashParam();
evidenceHashParam.setFileLabel("标签");
evidenceHashParam.setList(list);
httpRequest.body(JSONUtil.toJsonStr(evidenceHashParam));
HttpResponse httpResponse = httpRequest.execute();
String result = httpResponse.body();
返回结果示例: a.接口调用成功,则返回JSON数据示例为::
{
"code":"200",
"data":[
{
"attestationId":"did:bid:efaE9e45apUbuA87y7Y6zjMTaGfHt7WX",
"hash":"98df1f1dfb3b1a123c1517912dc70447aa61c6be532ac99de973abb6219e1653"
}
],
"message":"操作成功"
}
b.接口调用失败,则返回JSON数据示例为::
{
"code":"1010",
"message":"请输入正确格式的文件hash值"
}
存证列表 - /evidence/list¶
获取存证列表
请求参数¶
参数名 |
描述 |
是否可选 |
---|---|---|
evidenceType |
存证类型 1.文件存证 2.hash存证 |
非必选 |
evidenceChannel |
存证方式 1.自助 2.API |
非必选 |
state |
3.待支付4.上链中5.存证成功6.存证失败 |
非必选 |
startTime |
开始时间 |
非必选 |
endTime |
结束时间 |
非必选 |
pageNumber |
当前页码 |
非必选 |
pageSize |
每页显示数量 最大50 |
非必选 |
filename |
文件名称 |
非必选 |
返回的data¶
调用存证获取列表接口成功后会返回存证列表
字段名 |
描述 |
---|---|
totalPage |
当前页 |
pageSize |
每页显示数量 |
pageNum |
总页数 |
rows |
存证数据对象info |
info.evidenceChannel |
存证方式 1.自助 2.API |
info.attestationId |
存证id |
info.auditTime |
审核时间 |
info.auditResult |
审核结果 |
info.fileHash |
文件hash |
info.userId |
用户id |
info.fileLabel |
文件标签 |
info.filename |
文件名 |
info.fileSize |
文件大小 |
info.createTime |
创建时间 |
info.upChainTime |
上链时间 |
info.evidenceType |
存证类型 1:文件存证, 2:hash存证 |
info.state |
1.待审核 2.待复审 3.待支付 4.上链中 5.存证成功 6.存证失败 |
info.username |
用户名称 |
以java为例:
// 构建请求参数
Map<String ,Object> body = new HashMap<>();
body.put("evidenceType",1);
httpRequest.body(JSONUtil.toJsonStr(body));
HttpResponse httpResponse = httpRequest.execute();
String result = httpResponse.body();
返回结果示例: a.接口调用成功,则返回JSON数据示例为::
{
"code":"200",
"data":{
"totalPage":"1",
"pageSize":"10",
"rows":[
{
"evidenceChannel":2,
"attestationId":"did:bid:efaE9e45apUbuA87y7Y6zjMTaGfHt7WX",
"fileHash":"98df1f1dfb3b1a123c1517912dc70447aa61c6be532ac99de973abb6219e1653",
"userId":"did:bid:zfGUkdqhxEamsPvpqAH2iRHk1ifhcW61",
"fileLabel":"标签",
"filename":"test1",
"createTime":"2022-07-07 11:10:19",
"evidenceType":2,
"upChainTime":"2022-07-07 11:10:59",
"state":4,
"username":"陈诚"
},
{
"evidenceChannel":2,
"attestationId":"did:bid:ef23cydtVMQit888kfwqrZAJCccet2qQM",
"fileHash":"46d1f4f65279641891c13eb1cfba0f4a93cdd1c9e5d7cca31cd1860dbe7ca463",
"userId":"did:bid:zfGUkdqhxEamsPvpqAH2iRHk1ifhcW61",
"fileLabel":"标签",
"filename":"背景图.png",
"fileSize":"1306418",
"createTime":"2022-07-07 11:08:51",
"evidenceType":1,
"state":2,
"username":"陈诚"
}
],
"pageNum":"1",
"total":"2"
},
"message":"操作成功"
}
b.接口调用失败,则返回JSON数据示例为::
{
"code": "500",
"message": "系统错误"
}
存证详情 - /evidence/detail¶
查询存证详情。
请求参数¶
参数名 |
描述 |
是否可选 |
---|---|---|
attestationId |
存证id |
必选 |
返回的data¶
调用存证详情成功后会返回详情数据
字段名 |
描述 |
---|---|
attestationId |
存证id |
evidenceShareCode |
证据提取码 |
pdfFileKey |
pdf文件id |
fileHash |
存证文件hash |
dataExpireTime |
存证文件过期时间 |
attestationType |
存证类型 1.文件 2.hash |
dataExpireFlag |
存证文件是否已过期 |
userId |
用户id |
fileLabel |
文件标签 |
auditTime |
审核时间 |
auditResult |
审核结果 |
filename |
文件名 |
createTime |
创建时间 |
upChainTime |
上链时间 |
attestationChannel |
数据来源 1.自助 2.API |
dataFileKey |
存证文件的文件id |
username |
用户名称 |
checkBean |
链信息 |
checkBean.blockHash |
交易hash |
checkBean.fileName |
文件名称 |
checkBean.evidenceTime |
存证时间 |
checkBean.flag |
是否上链 |
checkBean.attestationId |
存证id |
checkBean.confirmTime |
出块时间 |
checkBean.confirmHash |
区块hash |
checkBean.ledgerSeq |
区块高度 |
checkBean.hash |
文件hash |
以java为例:
// 构建请求参数
Map<String ,Object> body = new HashMap<>();
body.put("attestationId","did:bid:efsRrRCTEmA7ZWodWFPkjMW2u5Y4hikv");
httpRequest.body(JSONUtil.toJsonStr(body));
HttpResponse httpResponse = httpRequest.execute();
String result = httpResponse.body();
返回结果示例: a.接口调用成功,则返回JSON数据示例为::
{
"code":"200",
"data":{
"checkBean":{
"blockHash":"ec879f484d5aed9d598c3d615ea70f8246272b3d4c5796dcedc3e67a402d0905",
"fileName":"test1",
"evidenceTime":"2022-07-07 11:10:59",
"flag":true,
"attestationId":"did:bid:efaE9e45apUbuA87y7Y6zjMTaGfHt7WX",
"confirmTime":"2022-07-07 11:11:01",
"confirmHash":"106f9a90a4ac78a45acdfe203a353562f3779ff1c6f3fc35d8914dd6a7ec06da",
"ledgerSeq":"1113290",
"hash":"98df1f1dfb3b1a123c1517912dc70447aa61c6be532ac99de973abb6219e1653"
},
"attestationId":"did:bid:efaE9e45apUbuA87y7Y6zjMTaGfHt7WX",
"evidenceShareCode":"KD8TCISG",
"pdfFileKey":"1544881909048279040",
"fileHash":"98df1f1dfb3b1a123c1517912dc70447aa61c6be532ac99de973abb6219e1653",
"attestationType":2,
"dataExpireFlag":false,
"userId":"did:bid:zfGUkdqhxEamsPvpqAH2iRHk1ifhcW61",
"fileLabel":"标签",
"filename":"test1",
"createTime":"2022-07-07 11:10:19",
"attestationChannel":2,
"upChainTime":"2022-07-07 11:10:59",
"id":"1544881469589377024",
"username":"陈诚"
},
"message":"操作成功"
}
b.接口调用失败,则返回JSON数据示例为::
{
"code": "500",
"message": "系统错误"
}
下载存证或pdf文件 - /file/download/{fileKey}¶
存证原文件或pdf下载
Path¶
参数名 |
描述 |
是否可选 |
---|---|---|
fileKey |
文件id |
必选 |
返回的文件¶
该接口会返回存证文件以及文件名,文件就是http返回结果的body,文件名存放在http的header中,header的名称是Content-Disposition,header值形如:
form-data; name=Content-Disposition; filename=5Yhus2mVSMnQRXobRJCYgt.zip
以java为例:
String apiName = "/file/download/1529707935276466176";
HttpRequest httpRequest = createRequestGet(apiName);
HttpResponse httpResponse = httpRequest.execute();
String header = httpResponse.header("Content-Disposition");
Pattern pattern = Pattern.compile(".*filename=\"(.*)\".*");
Matcher matcher = pattern.matcher(header);
String fileName = "";
if (matcher.matches()) {
fileName = matcher.group(1);
}
byte[] bytes = httpResponse.bodyBytes();
IoUtil.write(new FileOutputStream("/tmp/" + fileName),true,bytes);
返回结果示例: a.接口调用成功,则返回文件流::
byte[]
b.接口调用失败,则返回JSON数据示例为::
{
"code": "2001",
"message": "文件不存在"
}
签名¶
假定待签名数据头为:
"request_id": "2XiTgZ2oVrBgGqKQ1ruCKh",
"access_key": "2y7cg8kmoGDrDBXJLaizoD",
"nonce": 1464594744
签名过程用Java代码描述如下:
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.SM2;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.util.Base64;
public class SignatureUtil {
//签名
public static String sign(byte[] data, String privateKey) {
SM2 sm2 = new SM2(privateKey,null);
sm2.setMode(SM2Engine.Mode.C1C2C3);
sm2.usePlainEncoding();
//签名使用Base64编码后得到的值即为请求头中signature字段的值
return Base64.getEncoder().encodeToString(sm2.sign(data));
}
//验签
public static boolean verify(byte[] data, String publicKey, String sign) {
SM2 sm2 = new SM2(null, publicKey);
sm2.setMode(SM2Engine.Mode.C1C2C3);
sm2.usePlainEncoding();
return sm2.verify(data,Base64.getDecoder().decode(sign));
}
//测试用例
public static void main(String[] args) {
KeyPair keyPair = SecureUtil.generateKeyPair("SM2");
String publicKey = HexUtil.encodeHexStr(((BCECPublicKey)keyPair.getPublic()).getQ().getEncoded(false));
//RSA私钥文件路径
String privateKey = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(keyPair.getPrivate()));
System.out.println(publicKey);
System.out.println(privateKey);
long requestId = IdUtil.getSnowflakeNextId();//2XiTgZ2oVrBgGqKQ1ruCKh
String accessKey = "2y7cg8kmoGDrDBXJLaizoD";
long nonce = System.currentTimeMillis()/1000;//1464594744
String data = requestId + accessKey + nonce;
//签名
String sign = sign(data.getBytes(StandardCharsets.UTF_8),privateKey);
System.out.println(sign);
//验签
boolean verify = verify(data.getBytes(StandardCharsets.UTF_8),publicKey,sign);
System.out.println(verify);
}
}
Note
签名所用的方法是SM2,签名数据字符串转换成bytes时要用UTF-8编码格式
Java¶
如果使用maven,可以加入如下依赖:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.22</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.71</version>
</dependency>
示例程序¶
java:
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import org.junit.Test;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ApiRequestTest {
static class EvidenceHashParam {
private String fileLabel;
private List<HashInfo> list;
static class HashInfo {
private String filename;
private String fileHash;
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getFileHash() {
return fileHash;
}
public void setFileHash(String fileHash) {
this.fileHash = fileHash;
}
}
public String getFileLabel() {
return fileLabel;
}
public void setFileLabel(String fileLabel) {
this.fileLabel = fileLabel;
}
public List<HashInfo> getList() {
return list;
}
public void setList(List<HashInfo> list) {
this.list = list;
}
}
static class EvidenceFileParam {
private String fileLabel;
private List<Long> files;
public String getFileLabel() {
return fileLabel;
}
public void setFileLabel(String fileLabel) {
this.fileLabel = fileLabel;
}
public List<Long> getFiles() {
return files;
}
public void setFiles(List<Long> files) {
this.files = files;
}
}
private String uri = "http://127.0.0.1:18848/api";
/**
*
* @throws Exception
*/
@Test
public void detail() throws Exception {
String apiName = "/evidence/detail";
HttpRequest httpRequest = createRequestPost(apiName);
// 构建请求参数
Map<String ,Object> body = new HashMap<>();
body.put("attestationId","did:bid:efaE9e45apUbuA87y7Y6zjMTaGfHt7WX");
httpRequest.body(JSONUtil.toJsonStr(body));
HttpResponse httpResponse = httpRequest.execute();
String result = httpResponse.body();
JSON json = JSONUtil.parse(result);
System.out.println(json.toString());
}
@Test
public void list() throws Exception {
// API path
String apiName = "/evidence/list";
HttpRequest httpRequest = createRequestPost(apiName);
// 构建请求参数
Map<String ,Object> body = new HashMap<>();
// body.put("attestationId","");
httpRequest.body(JSONUtil.toJsonStr(body));
HttpResponse httpResponse = httpRequest.execute();
String result = httpResponse.body();
JSON json = JSONUtil.parse(result);
System.out.println(json.toString());
}
@Test
public void hash() throws Exception {
// API path
String apiName = "/evidence/hash";
HttpRequest httpRequest = createRequestPost(apiName);
// 构建请求参数
List<EvidenceHashParam.HashInfo> list = new ArrayList<>();
EvidenceHashParam.HashInfo hashInfo1 = new EvidenceHashParam.HashInfo();
hashInfo1.setFilename("test1");
hashInfo1.setFileHash("98df1f1dfb3b1a123c1517912dc70447aa61c6be532ac99de973abb6219e1653");
list.add(hashInfo1);
EvidenceHashParam evidenceHashParam = new EvidenceHashParam();
evidenceHashParam.setFileLabel("标签");
evidenceHashParam.setList(list);
httpRequest.body(JSONUtil.toJsonStr(evidenceHashParam));
HttpResponse httpResponse = httpRequest.execute();
String result = httpResponse.body();
JSON json = JSONUtil.parse(result);
System.out.println(json.toString());
}
@Test
public void file() throws Exception {
// API path
String apiName = "/evidence/file";
HttpRequest httpRequest = createRequestPost(apiName);
// 构建请求参数
List<Long> list = new ArrayList<>();
list.add(1544567382363930624L);
EvidenceFileParam evidenceFileParam = new EvidenceFileParam();
evidenceFileParam.setFileLabel("标签");
evidenceFileParam.setFiles(list);
httpRequest.body(JSONUtil.toJsonStr(evidenceFileParam));
HttpResponse httpResponse = httpRequest.execute();
String result = httpResponse.body();
JSON json = JSONUtil.parse(result);
System.out.println(json.toString());
}
@Test
public void uploadFile() throws Exception {
// API path
String apiName = "/file/upload";
HttpRequest httpRequest = createRequestPost(apiName);
httpRequest.form("file",new File("/tmp/背景图.png"));
httpRequest.form("type","video");
HttpResponse httpResponse = httpRequest.execute();
String result = httpResponse.body();
JSON json = JSONUtil.parse(result);
System.out.println(json.toString());
}
@Test
public void download() throws Exception {
// API path
String apiName = "/file/download/1529707935276466176";
HttpRequest httpRequest = createRequestGet(apiName);
HttpResponse httpResponse = httpRequest.execute();
String header = httpResponse.header("Content-Disposition");
Pattern pattern = Pattern.compile(".*filename=\"(.*)\".*");
Matcher matcher = pattern.matcher(header);
String fileName = "";
if (matcher.matches()) {
fileName = matcher.group(1);
}
byte[] bytes = httpResponse.bodyBytes();
IoUtil.write(new FileOutputStream("/tmp/" + fileName),true,bytes);
}
private HttpRequest createRequestPost(String apiName) throws Exception {
// 构建请求
HttpRequest httpRequest = HttpUtil.createPost(uri + apiName);
setHttpRequestHeaders(httpRequest);
return httpRequest;
}
private HttpRequest createRequestGet(String apiName) throws Exception {
// 构建请求
HttpRequest httpRequest = HttpUtil.createGet(uri + apiName);
setHttpRequestHeaders(httpRequest);
return httpRequest;
}
private HttpRequest setHttpRequestHeaders(HttpRequest httpRequest) throws Exception {
// RSA私钥文件路径
String privateKey = "308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420ab398da2bb9268c226f4c5908e94841ca6d254a90cf6e66ad848c8e01ee86d33a00a06082a811ccf5501822da144034200049ab45581431741df119e74c8699fd2cb70caeda3c6f05383dd8b4294f3ff5f3c2d7959877584ec884b75a09af99aa69d69c17f6e3018283d0452cbd0debd5262";
// 请求头
String requestId = IdUtil.simpleUUID();
String accessKey = "9d82aeae8c9b4c479715fc2923619472";
String nonce = String.valueOf(System.currentTimeMillis() / 1000);
//待签名数据 = requestId+accessKey+nonce
String data = requestId + accessKey + nonce;
// 开始签名
SM2 sm2 = new SM2(privateKey,null);
sm2.setMode(SM2Engine.Mode.C1C2C3);
sm2.usePlainEncoding();
// 签名使用Base64编码后得到的值即为请求头中signature字段的值
String signatureData = Base64.getEncoder().encodeToString(sm2.sign(data.getBytes(StandardCharsets.UTF_8)));
// 构建请求头
Map<String ,String> headers = new HashMap<>();
headers.put("request_id", requestId);
headers.put("access_key", accessKey);
headers.put("nonce",nonce);
headers.put("signature",signatureData);
httpRequest.addHeaders(headers);
return httpRequest;
}
}