修改网联签名验签接口,添加银联签名验签接口

This commit is contained in:
zhoubin 2021-12-03 16:06:29 +08:00
parent e877e80da9
commit c3c0c47095
5 changed files with 360 additions and 4 deletions

View File

@ -5,6 +5,7 @@ import com.sunyard.inf.*;
import com.sunyard.inf.customize.SydHAApi; import com.sunyard.inf.customize.SydHAApi;
import com.sunyard.inf.customize.SydQNUCApi; import com.sunyard.inf.customize.SydQNUCApi;
import com.sunyard.inf.customize.SydSDNXApi; import com.sunyard.inf.customize.SydSDNXApi;
import com.sunyard.inf.customize.SydUnionPayApi;
import racal.sunyard.main.SydApiBuilder; import racal.sunyard.main.SydApiBuilder;
import java.io.InputStream; import java.io.InputStream;
@ -13,7 +14,10 @@ import java.io.InputStream;
* Created by Cheney on 2017/11/11. * Created by Cheney on 2017/11/11.
* 程序调用接口 * 程序调用接口
*/ */
public interface SydApi extends SydRawApi, SydCertApi, SydCryptionApi, SydGenApi, SydSVApi, SydHAApi, SydQNUCApi, SydPledge, SydPINOpt, SydManufactureApi, SydZYOption { public interface SydApi extends
SydRawApi, SydCertApi, SydCryptionApi, SydGenApi,
SydSVApi, SydHAApi, SydQNUCApi, SydPledge, SydPINOpt,
SydManufactureApi, SydZYOption, SydUnionPayApi {
byte ALG_AES = 0x30; byte ALG_AES = 0x30;
byte ALG_SM4 = 0x31; byte ALG_SM4 = 0x31;

View File

@ -19,6 +19,15 @@ public interface SydCryptionApi extends SydSYMApi, SydASYMApi, SydHashApi {
*/ */
interface SydHashApi { interface SydHashApi {
/**
* 计算SM3散列值
* @param sn 证书序号
* @param userId 用户ID
* @param msg 原始数据二进制的消息块
* @return
*/
byte[] SM3HashSNaId(String sn, String userId, byte[] msg);
/** /**
* 输入对指定的原始数据和公钥通过 SM3 算法计算数据的散列值 * 输入对指定的原始数据和公钥通过 SM3 算法计算数据的散列值
* @param publicKey 公钥的 DER 编码(可以为空) * @param publicKey 公钥的 DER 编码(可以为空)

View File

@ -0,0 +1,33 @@
package com.sunyard.inf.customize;
/**
* 银联接口
*/
public interface SydUnionPayApi {
/**
* 用指定的证书私钥对指定的数据进行数字签名
* 银联
* @param pCertSerial 输入签名 X509 证书的序列号
* @param userId 输入用户id
* @param pOrgData 输入 待签名的原始数据
* @return 签名数据(BASE64 编码)
*/
String netSign(String pCertSerial, String userId, byte[] pOrgData);
/**
* 用指定证书对指定的原始数据进行数字签名验证
* 银联
* @param pCertSerial 输入 签名 X509 证书的序列号
* @param userId 输入用户id
* @param pOrgData 输入 待签名的原始数据
* @param pSignData 输入 签名数据(BASE64 编码)
* @return true 表示验证正确false 表示失败
*/
boolean netVerify(
String pCertSerial,
String userId,
byte[] pOrgData,
String pSignData
);
}

View File

@ -2232,9 +2232,139 @@ public class SydApi4j implements SydApi {
return ((PacketSection) ret.get("random")).getString(); return ((PacketSection) ret.get("random")).getString();
} }
// 测试通过
/**
* 用指定的证书私钥对指定的数据进行数字签名
* 银联
* @param pCertSerial 输入签名 X509 证书的序列号
* @param userId 输入用户id
* @param pOrgData 输入 待签名的原始数据
* @return 签名数据(BASE64 编码)
*/
@Override
public String netSign(String pCertSerial, String userId, byte[] pOrgData) {
// 针对传化需求定制
// 获取协议
Proto6C proto = new Proto6C();
// 先进行 HASH
pOrgData = SM3HashSNaId(pCertSerial, userId, pOrgData );
// 填充数据
PacketSN sn = PacketSN.gen();
HashMap<String, Object> packet = new HashMap<String, Object>();
packet.put("header", sn.getSn().array());
HashMap<String, Object> data = new HashMap<String, Object>();
data.put("certificateModel$option", 0);
data.put("certificateSn", pCertSerial);
data.put("inData", pOrgData);
data.put("dataFlag", 'P');
packet.put("data", data);
// 渲染发送
ByteBuffer bb = proto.rend(packet);
synchronized (this) {
// 响应解析
bb = syncRead(syncSend(bb));
}
//ProtocolParser parser = pset.getProtocolResParse();
Packet p = proto.parse(bb);
Map<String, Object> ret = p.toMap();
if (
!Arrays.equals(sn.getSn().array(), ((PacketSection) ret.get("header")).getBytes())
) {
throw new SydApiException("包序号不匹配", -4);
}
ret = (Map<String, Object>) ret.get("data");
PacketSection error = (PacketSection) ret.get("error");
if (!"00".equals(error.getString())) {
throw new SydApiException(error.getInfo(), error.autoInt());
}
return ((PacketSection) ret.get("sign")).getString();
}
/**
* 用指定证书对指定的原始数据进行数字签名验证
* 银联
* @param pCertSerial 输入 签名 X509 证书的序列号
* @param userId 输入用户id
* @param pOrgData 输入 待签名的原始数据
* @param pSignData 输入 签名数据(BASE64 编码)
* @return true 表示验证正确false 表示失败
*/
@Override
public boolean netVerify(String pCertSerial, String userId, byte[] pOrgData, String pSignData) {
// 获取协议
Proto6D proto=new Proto6D();
pOrgData = SM3HashSNaId(pCertSerial, userId, pOrgData );
// 填充数据
PacketSN sn = PacketSN.gen();
HashMap<String, Object> packet = new HashMap<String, Object>();
packet.put("header", sn.getSn().array());
HashMap<String, Object> data = new HashMap<String, Object>();
data.put("certificateModel$option", 0);
data.put("certificateSn", pCertSerial);
data.put("inData", pOrgData);
data.put("sign", pSignData);
data.put("dataFlag", 'P');
packet.put("data", data);
// 渲染发送
ByteBuffer bb = proto.rend(packet);
synchronized (this) {
// 响应解析
bb = syncRead( syncSend(bb) );
}
//ProtocolParser parser = pset.getProtocolResParse();
Packet p = proto.parse(bb);
Map<String, Object> ret = p.toMap();
if (
!Arrays.equals(sn.getSn().array(), ((PacketSection) ret.get("header")).getBytes())
) {
throw new SydApiException("包序号不匹配", -4);
}
ret = (Map<String, Object>) ret.get("data");
PacketSection error = (PacketSection) ret.get("error");
if (!"00".equals(error.getString())) {
throw new SydApiException(error.getInfo(), error.autoInt());
}
return true;
}
/**
* 用指定的证书私钥对指定的数据进行数字签名
* 网联
* @param pCertSerial 输入签名 X509 证书的序列号
* @param pOrgData 输入 待签名的原始数据
* @return 签名数据(BASE64 编码)
*/
@Override @Override
public String netSign(String pCertSerial, byte[] pOrgData) { public String netSign(String pCertSerial, byte[] pOrgData) {
return netSign(pCertSerial, null, pOrgData);
}
/**
* 用指定证书对指定的原始数据进行数字签名验证
* 网联
* @param pCertSerial 输入 签名 X509 证书的序列号
* @param pOrgData 输入 待签名的原始数据
* @param pSignData 输入 签名数据(BASE64 编码)
* @return true 表示验证正确false 表示失败
*/
@Override
public boolean netVerify(String pCertSerial, byte[] pOrgData, String pSignData) {
return netVerify(pCertSerial, null, pOrgData, pSignData);
}
// 测试通过
private String _netSign(String pCertSerial, byte[] pOrgData) {
// 获取协议 // 获取协议
@ -2276,8 +2406,7 @@ public class SydApi4j implements SydApi {
} }
// 测试通过 // 测试通过
@Override private boolean _netVerify(String pCertSerial, byte[] pOrgData, String pSignData) {
public boolean netVerify(String pCertSerial, byte[] pOrgData, String pSignData) {
// 获取协议 // 获取协议
//ProtocolRender render = pset.getProtocolReqRender("6D"); //ProtocolRender render = pset.getProtocolReqRender("6D");
Proto6D proto = new Proto6D(); Proto6D proto = new Proto6D();
@ -3811,6 +3940,173 @@ public class SydApi4j implements SydApi {
return ((PacketSection) ret.get("data")).getBytes(); return ((PacketSection) ret.get("data")).getBytes();
} }
public byte[] SM3HashSNaId(String sn, String userId, byte[] msg) {
return SM3Hash(null, sn, null, userId, msg);
}
/**
* 计算SM3散列值
* @param publicKey 公钥的 DER 编码(可以为空)
* @param csn 证书序号
* @param cdn 证书DN
* @param userId 用户ID
* @param msg 原始数据二进制的消息块
* @return
*/
private byte[] SM3Hash(String publicKey, String csn, String cdn, String userId, byte[] msg) {
// 填充数据
PacketSN sn = PacketSN.gen();
final int packLen = 2048;
int loop = msg.length / packLen;
if (msg.length % packLen > 0) {
loop++;
}
// 保存上一轮的 hash
byte[] ctx = new byte[0];
byte packtype;
int i = 0;
while (i < loop) {
if (0 == i) {
if (1 == loop) {
packtype = '0';
} else {
packtype = '1';
}
} else {
if (i == (loop - 1)) {
packtype = '3';
} else {
packtype = '2';
}
}
ByteBuffer bb = ByteBuffer.allocate(3000);
byte[] ret = new byte[2];
bb.put(ret);
bb.put(sn.getSn().array());
bb.put("76".getBytes());
bb.put(packtype);
int tmplen = (i < (loop - 1) || !(msg.length % packLen > 0)) ? packLen : (msg.length % packLen);
// 填充长度
bb.put(String.format("%04d", tmplen).getBytes());
// 填充数据
bb.put(msg, i * packLen, tmplen);
if ('0' == packtype || '1' == packtype) {
if ( null != csn || null != cdn ){
if (userId != null) {
bb.put(String.format("%02d", userId.length()).getBytes());
bb.put(userId.getBytes());
} else {
bb.put(String.format("%02d", 16).getBytes());
bb.put("1234567812345678".getBytes());
}
// 通过证书查找公钥
bb.put("P".getBytes());
byte[] content;
if ( null != csn ){
content = csn.getBytes();
bb.put( "0".getBytes() );
} else {
content = cdn.getBytes();
bb.put( "1".getBytes() );
}
bb.put( String.format("%04d", content.length ).getBytes() );
bb.put( content );
}
else if (null == publicKey) {
// 无公钥
bb.put(String.format("%02d", 0).getBytes());
bb.put("".getBytes());
bb.put(String.format("%04d", 0).getBytes());
bb.put("".getBytes());
}
else {
// 传入公钥
bb.put(String.format("%02d", 16).getBytes());
bb.put("1234567812345678".getBytes());
bb.put(String.format("%04d", publicKey.length() / 2).getBytes());
bb.put(Util.hexString2Bytes(publicKey));
}
} else if ('2' == packtype || '3' == packtype) {
bb.put(String.format("%04d", ctx.length).getBytes());
bb.put(ctx);
}
int len = bb.position() - 2;
bb.put(0, (byte) (0xFF & (len / 256)));
bb.put(1, (byte) (0xFF & (len % 256)));
synchronized (this) {
// 响应解析
bb = syncRead(syncSend(bb));
}
bb.flip();
// 忽略头部
bb.get(new byte[12], 0, 12);
// 检测错误
byte[] error = new byte[2];
bb.get(error, 0, 2);
int e = Util.a2i(error, 16);
if (0 != e) {
throw new SydApiException(0x80 == e ? "数据长度错" : "输入长度错", e);
}
if ('0' == packtype || '3' == packtype) {
byte[] hash = new byte[32];
bb.get(hash, 0, 32);
return hash;
} else if ('1' == packtype || '2' == packtype) {
byte[] hashLen = new byte[4];
bb.get(hashLen, 0, 4);
byte[] hash = new byte[Util.a2i(hashLen, 10)];
bb.get(hash, 0, hash.length);
ctx = hash;
}
i++;
}
return new byte[0];
}
@Override @Override
public byte[] SM3Hash(byte[] msg) { public byte[] SM3Hash(byte[] msg) {
return SM3Hash(null, msg); return SM3Hash(null, msg);
@ -3823,6 +4119,7 @@ public class SydApi4j implements SydApi {
} }
@Override @Override
public byte[] SM3Hash(String publicKey, byte[] msg, int block, byte[] mac) { public byte[] SM3Hash(String publicKey, byte[] msg, int block, byte[] mac) {

View File

@ -1254,6 +1254,19 @@ public class TestAllMethods {
System.out.println(b); System.out.println(b);
} }
// 测试通过
// 6D
@Test
public void testNetVerify2() {
String sn = "1004282451";
String userId = "1234567812345678";
String data = "111";
String s = api.netSign(sn, userId, data.getBytes());
boolean b = api.netVerify(sn, userId, data.getBytes(), s);
System.out.println(b);
}
// 测试通过 // 测试通过
// 6E // 6E
@Test @Test