SCFP外联平台对接技术说明书
1. 文档说明
1.1 阅读对象
1.2 修订记录
版本号 | 修订时间 | 更改内容描述 | 修订人 | 审核人 |
---|---|---|---|---|
V1.0 | 20220103 | 初稿 | *** |
2. 接口规范
2.1 协议规则
- 传输方式:采用HTTPS传输
- 提交方式:采用POST方法提交
- 数据格式:提交和返回数据都为JSON格式
- 字符编码:统一采用UTF-8字符编码
- 签名算法:统一采用SM2摘要签名算法,具体为sm3withsm2
- 加密算法:统一采用SM4国密算法对报文加解密
2.2 环境地址
2.3 系统对接
2.4 HTTP请求头
序号 | 字段 | 是否必输 | 说明 | 备注 |
---|---|---|---|---|
1 | Content-Type | 是 | 内容类型 | application/json;charset=utf-8 |
2 | x-app-id | 是 | 对接方标识 | 由供应链金融平台提供 |
3 | x-sign | 是 | 签名 | base64(sm3withsm2(报文明文)) |
2.5 请求样例
POST https://{环境地址}/{接口地址} HTTP/1.1
Content-Type: application/json;charset=utf-8
x-app-id: {请求方标识}
x-sign: {签名}
base64密文
2.6 响应样例
HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
base64密文
3. 签名验签
3.1 密钥生成
对接方需生成一对sm2密钥对,并将公钥发送供应链金融平台。
3.1.1 Maven依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.69</version>
</dependency>
3.1.2 静态代码
// 使用BouncyCastle密码库
private static final BouncyCastleProvider bc = new BouncyCastleProvider();
3.1.3 示例代码
// 使用BouncyCastle密码库
BouncyCastleProvider bc = new BouncyCastleProvider();
// 获取SM2椭圆曲线的参数
final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
// 获取一个椭圆曲线类型的密钥对生成器
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", bc);
// 使用SM2参数初始化生成器
kpg.initialize(sm2Spec);
// 使用SM2的算法区域初始化密钥生成器
kpg.initialize(sm2Spec, new SecureRandom());
// 获取密钥对
KeyPair keyPair = kpg.generateKeyPair();
byte[] pkcs8PriKeyBytes = keyPair.getPrivate().getEncoded();
byte[] x509PubKeyBytes = keyPair.getPublic().getEncoded();
System.out.println("\nBASE64编码(默认采用):");
System.out.println("SM2 PKCS#8私钥:" + BaseEncoding.base64().encode(pkcs8PriKeyBytes));
System.out.println("SM2 X.509 公钥:" + BaseEncoding.base64().encode(x509PubKeyBytes));
3.2 供应链金融平台公钥
供应链金融平台通知对接方,对于通知报文验签。
3.2.1 测试环境
# SM2公钥 X.509:
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEa3QD4dsDhscjeLhENFl4MoSx1zscuMbKkxrrfogYLgJPBBS6tdYbFmAeYIt39ZEiKZZoZNpjb6op5R7++XUE/w==
3.2.2 生产环境
# SM2公钥 X.509:
后续提供
3.3 签名示例
对发送的JSON报文,即HTTP body需要进行全文SM2签名,再对签名结果进行Base64编码。
方法声明
public static String sign(String base64PriKey, String data);
方法示例
KeyFactory keyFact = KeyFactory.getInstance("EC", bc);
byte[] pkcs8PriKeyBytes = BaseEncoding.base64().decode(base64PriKey);
// 根据采用的编码结构反序列化公私钥
PrivateKey privateKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(pkcs8PriKeyBytes));
// 生成SM2sign with sm3签名验签算法实例
Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), bc);
// 签名需要使用私钥,使用私钥初始化签名实例
signature.initSign(privateKey);
// 写入签名原文到算法中
signature.update(data.getBytes(StandardCharsets.UTF_8));
// 计算签名值
byte[] signBytes = signature.sign();
// Base64编码
return Base64.getEncoder().encodeToString(signBytes);
3.4 验签示例
对接收的JSON报文进行验签。
方法声明
public static boolean verify(String base64PubKey, String data, String base64Sign);
方法示例
KeyFactory keyFact = KeyFactory.getInstance("EC", bc);
byte[] x509PubKeyBytes = BaseEncoding.base64().decode(base64PubKey);
// 根据采用的编码结构反序列化公私钥
PublicKey publicKey = keyFact.generatePublic(new X509EncodedKeySpec(x509PubKeyBytes));
// 生成SM2sign with sm3签名验签算法实例
Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), bc);
// 签名需要使用公钥,使用公钥初始化签名实例
signature.initVerify(publicKey);
// 写入待验签的签名原文到算法中
signature.update(data.getBytes(StandardCharsets.UTF_8));
// 验签
return signature.verify(Base64.getDecoder().decode(base64Sign));
4. 加解密
对于请求和响应报文,即HTTP body全文均使用SM4国密算法加密,对加密后的密文字节数组再进行Base64编码后进行传输。
4.1 对称密钥
4.1.1 测试环境
wXZyNYMeHmoEMwJKR4h7YA==
4.2 IV计算
private byte[] getIv(String appId) {
return (appId + "0000000000000000").substring(0, 16).getBytes(StandardCharsets.UTF_8);
}
4.3 报文加密
方法声明
public static String encrypt(String base64Sm4Key, String appId, String plaintext);
方法示例
SecretKeySpec spec = new SecretKeySpec(Base64.getDecoder().decode(base64Sm4Key), "SM4");
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding", bc);
IvParameterSpec ivParameterSpec = new IvParameterSpec(getIv(appId));
cipher.init(Cipher.ENCRYPT_MODE, spec, ivParameterSpec);
byte[] ciphertextBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
String ciphertext = Base64.getEncoder().encodeToString(ciphertextBytes);
4.4 报文解密
方法声明
public static String decrypt(String base64Sm4Key, String appId, String base64Ciphertext);
方法示例
SecretKeySpec spec = new SecretKeySpec(Base64.getDecoder().decode(base64Sm4Key), "SM4");
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding", bc);
IvParameterSpec ivParameterSpec = new IvParameterSpec(getIv(appId));
cipher.init(Cipher.DECRYPT_MODE, spec, ivParameterSpec);
byte[] ciphertextBytes = Base64.getDecoder().decode(base64Ciphertext);
byte[] plaintextBytes = cipher.doFinal(ciphertextBytes);
String plaintext = new String(plaintextBytes, StandardCharsets.UTF_8);