天津滨海CA是一家优秀的电子认证服务商,依托自身优势和资源整合能力,推出了滨海CA云平台,该平台整合了证书服务、时间戳服务、统一身份认证服务、硬件服务等,为不同行业客户提供了高效的互联互通和快速接入的渠道,实现了电子认证服务及信息安全整体解决方案。
• 一站式解决方案
作为一个综合性的服务平台,提供完整的服务接口定义,让技术更简单、实用。
• 简单的接入方式
实现了轻量级Restful服务,对所有服务进行了标准化封装。接入人员更加专注于自身系统的开发,只需接口文档便可完成系统间的交互,无需过多相互了解。
• 稳定的服务支撑
依托于微服务架构的优势及滨海CA的软硬一体化方案,提供了稳定的服务支撑。
• 专业的服务支持
天津滨海CA是经国家认证的电子认证服务商,所提供的服务也是国家标准规范框架下的,我们专业且专注。
• 无限可扩展的开放能力
随着智能AI的发展,语音识别、声纹识别、人脸识别等技术相继成熟,结合自身优势,打造更加智能交互的开放平台。
• 时间戳服务: 时间戳是使用数字签名技术产生的数据,签名的对象包括了原始文件信息、签名参数、签名时间等信息。时间戳系统用来产生和管理时间戳,对签名对象进行数字签名产生时间戳,以证明原始文件在签名时间之前已经存在。可信时间戳用于证明电子数据文件自申请可信时间戳后内容保持完整、未被更改,并将起到电子档案和档案数字化副本内容防篡改的作用。
• 身份认证网关: 身份认证网关是用户进入CA证书认证服务的网络信任域和接入应用服务系统前的访问控制,它具有用户身份认证代理的功能,能够和证书认证服务系统交互,完成用户身份认证,根据认证结果核对该用户的可信网络访问权限,完成网络接入的鉴权控制。
• 签名服务: 数字签名是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。数字签名是个加密的过程,数字签名验证是个解密的过程,也是一个验证发送者身份的过程。
• 事件证书: 用于生成针对某次事件的证书,一般证书有效期比较短。
• 身份核验服务:身份核验是通过个人信息与公安信息比对、人脸识别、活体验证等手段,完成对用户身份的确认。
云平台调用API遵循以下规则:
1、传输方式: 为保证交易安全性,建议采用HTTPS传输
2、提交方式: 采用POST方法提交
3、数据格式: 提交和返回数据都为JSON格式的字符串。
4、字符编码: 统一采用UTF-8字符编码
5、签名算法: RSA2,SM2,后续会兼容MD5等
6、签名要求: 请求和接收数据均需要校验签名。所有提交数据必须经过客户端签名;云平台返回值会经过滨海CA云平台签名,使用统一公钥进行签名验证。
7、APPID校验:客户方接入滨海CA云平台时,平台会为其分配一堆APPID,滨海CA云平台将根据此APPID校验客户方身份。
为了防止 API 调用过程中被恶意篡改,对所有 API 请求参数(包括公共参数和业务参数,但除去sign参数)必须加入签名,服务端会根据请求参数,对签名进行验证,签名不合法的请求将会被拒绝。拼接好的字符串和密钥分别按照UTF-8编码,用编码后的密钥字符流结合HmacSHA256算法对编码后的参数字符流进行摘要。将摘要后的字符流转换为十六进制大写字符串,即得到签名值。
报文的签名机制
对于报文的签名处理机制如下:
1.首先组成待签名字符串。待签名字符串组成规则为:对待验签的所有请求参数按从a到z的字典顺序排列,如果首字母相同,按第二个字母排列,以此类推。排序完成后按将所有键值对以“&”符号拼接:
param1=value1¶m2=value2&……
2.将appId,appKey, transactionId分别增加“&”后,附加到待签名字符串后,按指定签名算法进行运算。假设待签名字符串为strToSign,算法为sha256,对strToSign&appId&appKey&transactionId进行sha256签名运算获得签名结果byte数组,将byte数组转换为16进制即为最终的签名结果。
样例报文:
{ "version": "1.0", "charset": "UTF-8", "sign": "5060C36C28F6AF6C546C0203598F349A4BC72C40262846A84B0CEEB298A98E76", "signtype": "SHA-256", "format": "JSON", "isEncrypted": "0", "transactionId": "58e2284bb71947f5b625c64c85951e34", "appId": "abc", "jsonRequestData": "{ "dateTime": "20200825143140", "cert": "MIIDATCCAqWg......le8TrOtVd7XVDgRk91yvSAkn8g=" } }待签名字符串为: cert=MIIDATCCAqWg......le8TrOtVd7XVDgRk91yvSAkn8g=&dateTime=20200825143140&abc&appKey&58e2284bb71947f5b625c64c85951e34 注意:appId 由滨海CA云平台提供,appKey由用户在滨海CA平台上设定。
待签名字符串为:
cert=MIIDATCCAqWg......le8TrOtVd7XVDgRk91yvSAkn8g=&dateTime=20200825143140&abc&appKey&58e2284bb71947f5b625c64c85951e34
注意:appId 由滨海CA云平台提供,appKey由用户在滨海CA云平台上设定。
平台响应结果验签机制:
一般情况下,平台发送的报文使用滨海CA云平台私钥签名。调用方接受到通知报文后需要使用滨海CA云平台公钥验签。
验签说明如下:
首先组成待签名字符串。待签名字符串组成规则为:所有参数按从a到z的字典顺序排列,如果首字母相同,按第二个字母排列,以此类推。排序完成后按将所有键值对以“&”符号拼接:
param1=value1¶m2=value2&……
假设待签名字符串为strToSign,招行通知公钥为publicKey, 签名内容为strSign,算法为标准的RSA算法,加密算法为SHA1WithRSA,对strToSign进行签名校验。
//排序并获取待签名串 public static StringBuilder mapSortedByKey(Mapparam) throws ApiException { try { StringBuilder stringBuilder = new StringBuilder(); ArrayList keyList = new ArrayList (param.keySet()); Collections.sort(keyList); for (int i = 0; i < keyList.size(); i++) { String key = keyList.get(i); Object value = param.getOrDefault(key, "") ; value = value == null ? "":value; if (i == keyList.size() - 1) { stringBuilder.append(key).append("=").append(URLDecoder.decode(value.toString(), "UTF-8")); } else { stringBuilder.append(key).append("=").append(URLDecoder.decode(value.toString(), "UTF-8")).append("&"); } } return stringBuilder; }catch (Exception e){ throw new ApiException("获取待签名数据失败", e); } } //生成签名数据 private static String sign(StringBuilder strToSign,String appId,String appKey,String transactionId) throws ApiException { String result = ""; try { //假设已排序字符串为strToSign //添加商户密钥 strToSign.append("&").append(appId).append("&").append(appKey).append("&").append(transactionId); //System.out.println("待验签字符串:"+strToSign.toString()); //System.out.println(strToSign.toString()); // 创建加密对象 MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); // 传入要加密的字符串,按指定的字符集将字符串转换为字节流 messageDigest.update(strToSign.toString().getBytes("UTF-8")); byte byteBuffer[] = messageDigest.digest(); // 將 byte数组转换为16进制string result = StringToHexUtil.bytesToHexString(byteBuffer); } catch (Exception e) { throw new ApiException("生成签名数据失败", e); } return result; } /** * 获取公钥 * * @return */ private static PublicKey getPublicKey() throws Exception { String publicKey = BhcaConstant.RSAPUBLICKEY; KeyFactory keyFactory = KeyFactory.getInstance("RSA"); byte[] decodedKey = Base64Util.decode(publicKey.getBytes()); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey); return keyFactory.generatePublic(keySpec); } /** * 验签 * * @param srcData 原始字符串 * @param sign 签名 * @return 是否验签通过 */ public static boolean verify(String srcData, String sign) throws ApiException { try { byte[] keyBytes = getPublicKey().getEncoded(); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey key = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance("MD5withRSA"); signature.initVerify(key); signature.update(srcData.getBytes()); return signature.verify(Base64Util.decode(sign.getBytes())); }catch (Exception e){ throw new ApiException("验签失败",e); } }
公共请求参数是指每个接口都需要使用到的请求参数,如下:
Headers中参数如下:
序号 | 参数名称 | 类型 | 是否必填 | 参数说明 |
---|---|---|---|---|
1 | version | String | 是 | 版本号目前为(1.0) |
2 | charset | String | 是 | 编码类型,固定为UTF-8 |
3 | sign | String | 否 | 签名(参考签名) |
4 | signtype | String | 是 | 签名方式,固定为SHA-256 |
5 | format | String | 是 | 传输格式,固定为JSON |
6 | isEncrypted | String | 是 | 是否加密,0 未加密 1 已加密。 |
7 | transactionId | String | 是 | 交易编号(业务自定义唯一编码) |
8 | appId | String | 是 | 应用ID(由TJBHCA提供) |
序号 | 参数名称 | 类型 | 是否必填 | 参数说明 |
---|---|---|---|---|
1 | jsonRequestData | String | 是 | 请求JSON串,对应业务接口请求参数(详见各个业务请求参数) |
{ "dateTime":"20200518154102", "method":"certQuery", "name":"测试" }API请求示例:
BhcaApiClient bhcaApiClient = new BhcaApiClient("http://xxx/tsa/getTsa", "abc", "10001"); Map< String, String> map = new HashMap< String, String>(); try { map.put("tsatxt", "hello"); String transactionId = "58e2284bb71947f5b625c64c85951e34"; String result = bhcaApiClient.execute(transactionId, map); System.out.println("返回结果:" + result); if (result != null) { //将结果转化成JSON对象 JSONObject obj = JSONObject.parseObject(result); //验签 if (BhcaUtil.verifyResponseSign(obj)) { System.out.println("验签成功。"); if ("200".equals(obj.get("code").toString())) { System.out.println("调用成功,准备处理业务逻辑。"); }else{ System.out.println("调用时间戳接口失败,失败原因:" + obj.get("code") + ":" + obj.get("message")); } }else{ System.out.println("验签失败。"); } } else { System.out.println("调用接口失败。"); } }catch (ApiException e){ e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); }
调用 API 服务后返回数据采用统一格式,code为200 ,请求成功,其他为失败,这时没有data结果信息
名称 | 类型 | 描述 |
---|---|---|
code | Integer | 状态码,200请求成功,其他为失败,具体见 API错误码说明 |
message | String | 状态码的描述 |
data | Object | 结果信息,code为200时出现,具体看各个接口说明 |
sign | String | 服务端签名值 |
JSON示例
{ "code": 200, "data": { …… }, "sign": "PIBXi7DCv9iynh7……X0OOk1lVM16U=", "transactionId": "58e2284bb71947f5b625c64c85951e34" }
JSON示例
{ "code": 200, "code": 1000030000, "sign": "af1WhBEt7jfxJ……9xFw7xZ6AugHtl3kMObmsUjk=", "message":"交易流水号重复,58e2284bb71947f5b625c64c85951e34" }
返回码 | 系统错误 | 备注 |
---|---|---|
200 | 系统调用成功 | |
504 | 网关超时 | |
404 | 未找到 | |
500 | 服务内部异常 | |
1000010000 | 请求签名错误 | |
1000020000 | 请求参数错误 | |
1000030000 | 交易流水号重复 | |
1000040000 | 请求头错误 | |
1000050000 | 请求地址禁止访问 | |
1000060000 | 请求方式错误 |