diff --git a/DProto/6A.xml b/DProto/6A.xml index 37b25e0..c3c4ae8 100644 --- a/DProto/6A.xml +++ b/DProto/6A.xml @@ -17,6 +17,9 @@ + @@ -49,6 +52,14 @@ 可选项,只针对“证书模式”为 1时有。 + + 只针对算法标识为3时传入 + + + + 只针对算法标识为3时传入 + + LMK 下加密密钥的方案。 @@ -72,11 +83,10 @@ - - - + + 6B @@ -162,12 +172,10 @@ } } - function getKeyLen() { return Util.a2i( $final.getSection('data').getSection('keyLen') ) } - function lmkLen() { if( Arrays.equals("L".getBytes(), $final.getSection('data').getSection('lmkHead').getValue() @@ -178,6 +186,10 @@ } } + function dataLen() { + return $packet.data.orgData.length() + } + + + 可选,如果输入P代表使用PKCS7_SM2_SM4_ECB模式解密数字信封 + + - - @@ -104,6 +106,14 @@ + + 明文原始数据长度(只针对P存在时,无P时为0) + + + + 原始数据(只针对P存在时,无P时不存在) + + @@ -173,5 +183,9 @@ function orgCodeLen() { return Util.a2i( $final.getSection('data').getSection('orgCodeLen') ) } + + function oDataLen() { + return Util.a2i( $final.getSection('data').getSection('oDataLen') ) + } diff --git a/DProto/6BXF.xml b/DProto/6BXF.xml new file mode 100644 index 0000000..02f8c96 --- /dev/null +++ b/DProto/6BXF.xml @@ -0,0 +1,191 @@ + + + + + + 6B + + + + + 可选,如果输入P代表使用PKCS7_SM2_SM4_ECB模式解密数字信封 + + + + + + + + + + + + 根据证书模式,需要输入内容项的长度 n + + + + + 可选项,只针对“证书模式”为 0时有。 + + + + 可选项,只针对“证书模式”为 1时有。 + + + + 公钥加密的密钥密文长度 + + + + 公钥加密的密钥密文的 BASE64 编码 + + + + LMK 下加密密钥的方案。 + + + + 可选项。如果显示消息尾域,则该域必须显示。值为 + “X’19”。 + 19 + + + + 可选项。最大长度为 32个字符。 + + + + + + + + 6C + + + + + + + + + + + + + + + + + + + + 明文原始数据长度(只针对P存在时,无P时为0) + + + + 原始数据(只针对P存在时,无P时不存在) + + + + + + + + 如果是 'L' ,lmk 32 个字节长,否则 lmk 64 个字节长。 + + + + 对称密钥的密文,由 LMK30-31 保护。 + + + + 密钥校验值 + + + + 公钥加密的密钥格式3所需机构码长度,仅当格式3以及'P'存在时存在 + + + + 公钥加密的密钥格式3所需机构码长度,仅当格式3以及'P'存在时存在 + + + + 可选项。如果显示消息尾域,则该域必须显示。值为 “X’19”。 + 19 + + + + 可选项。最大长度为 32个字符。 + + + + + + + + diff --git a/doc/java接口说明文档.docx b/doc/java接口说明文档.docx index bb1c88f..1443ad4 100644 Binary files a/doc/java接口说明文档.docx and b/doc/java接口说明文档.docx differ diff --git a/pom.xml b/pom.xml index 2c4dfb8..ab5498d 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ com.sunyard proto - 1.0-20220402 + 1.0-20220412 diff --git a/src/main/java/com/sunyard/inf/SydGenApi.java b/src/main/java/com/sunyard/inf/SydGenApi.java index 421227b..98237ec 100644 --- a/src/main/java/com/sunyard/inf/SydGenApi.java +++ b/src/main/java/com/sunyard/inf/SydGenApi.java @@ -216,6 +216,32 @@ public interface SydGenApi { */ RetWrap generateDEByDn(byte[] dn, int alg, int model) throws Exception; + /** + * 产生对称密钥,通过公钥加密生成网联格式的数字信封。 + + * @param certSerial 证书DN。 + * @param oData 明文数据 + * @return RetWrap 包含 CipherKey SessionKey KCV + * CipherKey 公钥加密的会话密钥 ,BASE64 编码 + * SessionKey 本地LMK 加密的会话密钥,HEX 格式, 本地 LMK 加密的会话密钥长度,HEX 格式(不包括第 1 个字符),一般为 33 个 Bytes。默认输入值为缓冲区的大小。 + * KCV 会话密钥的检验值,HEX 格式,长度为 32H。 + */ + RetWrap generateDEByDn(byte[] certSerial, String oData); + + /** + * 产生对称密钥,通过公钥加密生成网联格式的数字信封。 + + * @param certSerial 证书DN或SN。 + * @param alg 算法标识,0:AES,1-3:SM4 + * @param model 证书模式,0:证书序号,1:证书 DN + * @param oData 明文数据 + * @return RetWrap 包含 CipherKey SessionKey KCV + * CipherKey 公钥加密的会话密钥 ,BASE64 编码 + * SessionKey 本地LMK 加密的会话密钥,HEX 格式, 本地 LMK 加密的会话密钥长度,HEX 格式(不包括第 1 个字符),一般为 33 个 Bytes。默认输入值为缓冲区的大小。 + * KCV 会话密钥的检验值,HEX 格式,长度为 32H。 + */ + RetWrap generateDE(byte[] certSerial, int alg, int model, String oData); + /** * 产生对称密钥,通过公钥加密生成网联格式的数字信封。 @@ -271,6 +297,34 @@ public interface SydGenApi { String pCipherKey ); + /** + * 接收用私钥解密数字信封,还原对称密钥。 + * 默认会传P + * @param certSerial 输入。 对方 X509 证书的DN。 + * @param pCipherKey 输入。 公钥加密的会话密钥,BASE64 编码 + * @return RetWrap 包含 SessionKey KCV + * oData 如果有机构码的话会返回原数据 + * SessionKey 输出。 本地LMK 加密的会话密钥,格式为:密钥类型|密钥密文,比如:02|密钥密文 + * KCV 会话密钥的检验值,HEX 格式,长度为 36H。 + * orgCode 如果有机构码的话会返回机构码 + */ + RetWrap decryptDEByDN(byte[] certSerial, String pCipherKey); + + /** + * 接收用私钥解密数字信封,还原对称密钥。 + * + * @param pFlag 输入。 p标识,如果为true,则传入P + * @param model 输入。 证书模式,0:证书序号,1:证书 DN + * @param certSerial 输入。 对方 X509 证书的序列号或DN。 + * @param pCipherKey 输入。 公钥加密的会话密钥,BASE64 编码 + * @return RetWrap 包含 SessionKey KCV + * oData 如果有机构码的话会返回原数据 + * SessionKey 输出。 本地LMK 加密的会话密钥,格式为:密钥类型|密钥密文,比如:02|密钥密文 + * KCV 会话密钥的检验值,HEX 格式,长度为 36H。 + * orgCode 如果有机构码的话会返回机构码 + */ + RetWrap decryptDE(boolean pFlag, int model, byte[] certSerial, String pCipherKey); + /** * 产生要求长度的随即数据串,可用于身份认证 * diff --git a/src/main/java/racal/sunyard/main/SydApi4j.java b/src/main/java/racal/sunyard/main/SydApi4j.java index 1ed7c56..908e32c 100644 --- a/src/main/java/racal/sunyard/main/SydApi4j.java +++ b/src/main/java/racal/sunyard/main/SydApi4j.java @@ -2115,58 +2115,6 @@ public class SydApi4j implements SydApi { return repackEncrypt(packet); } - // 当证书模式为证书序号时,测试成功 - // 测试通过 - @Override - public RetWrap generateDEByDn(byte[] dn, int alg, int model) { - Proto6A proto = new Proto6A(); - // 填充数据 - PacketSN sn = PacketSN.gen(); - HashMap packet = new HashMap(); - packet.put("header", sn.getSn().array()); - HashMap data = new HashMap(); - data.put("alg$option", alg); - data.put("certificateModel$option", model); - - if (model == 0) { - data.put("certificateSn", dn); - } else { - data.put("certificateDn", dn); - } - data.put("keyType", "X"); - - 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 ret = p.toMap(); - if ( - !Arrays.equals(sn.getSn().array(), ((PacketSection) ret.get("header")).getBytes()) - ) { - throw new SydApiException("包序号不匹配", -4); - } - ret = (Map) ret.get("data"); - PacketSection error = (PacketSection) ret.get("error"); - if (!"00".equals(error.getString())) { - throw new SydApiException(error.getInfo(), error.autoInt()); - } - RetWrap rw = new RetWrap(); - rw.put(SydApi.RET_CIPHER_KEY, ((PacketSection) ret.get("key")).getString()); - rw.put(SydApi.RET_SESSION_KEY, ((PacketSection) ret.get("lmkHead")).getString() + - ((PacketSection) ret.get("lmk")).getString() - ); - - rw.put(SydApi.RET_KCV, ((PacketSection) ret.get("keyCheck")).getString()); - - return rw; - } - @Override public RetWrap generateDEbyGS(String certSerial, byte nAlg) { return generateDE(1, certSerial, nAlg, null); @@ -2238,7 +2186,74 @@ public class SydApi4j implements SydApi { return rw; } + // 当证书模式为证书序号时,测试成功 // 测试通过 + @Override + public RetWrap generateDEByDn(byte[] dn, int alg, int model) { + return generateDE(dn, alg, model, null); + } + + @Override + public RetWrap generateDEByDn(byte[] certSerial, String oData) { + return generateDE(certSerial, 3, 1, oData); + } + + @Override + public RetWrap generateDE(byte[] certSerial, int alg, int model, String oData) { + Proto6A proto = new Proto6A(); + // 填充数据 + PacketSN sn = PacketSN.gen(); + HashMap packet = new HashMap(); + packet.put("header", sn.getSn().array()); + HashMap data = new HashMap(); + data.put("alg$option", alg); + data.put("certificateModel$option", model); + + if (model == 0) { + data.put("certificateSn", certSerial); + } else { + data.put("certificateDn", certSerial); + } + + if (alg == 3) { + data.put("orgData", oData); + } + + data.put("keyType", "X"); + + 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 ret = p.toMap(); + if ( + !Arrays.equals(sn.getSn().array(), ((PacketSection) ret.get("header")).getBytes()) + ) { + throw new SydApiException("包序号不匹配", -4); + } + ret = (Map) ret.get("data"); + PacketSection error = (PacketSection) ret.get("error"); + if (!"00".equals(error.getString())) { + throw new SydApiException(error.getInfo(), error.autoInt()); + } + RetWrap rw = new RetWrap(); + rw.put(SydApi.RET_CIPHER_KEY, ((PacketSection) ret.get("key")).getString()); + rw.put(SydApi.RET_SESSION_KEY, ((PacketSection) ret.get("lmkHead")).getString() + + ((PacketSection) ret.get("lmk")).getString() + ); + + rw.put(SydApi.RET_KCV, ((PacketSection) ret.get("keyCheck")).getString()); + + return rw; + } + + @Override public RetWrap decryptDE(String certSerial, String pCipherKey) { // 获取协议 @@ -2292,6 +2307,78 @@ public class SydApi4j implements SydApi { return rw; } + @Override + public RetWrap decryptDEByDN(byte[] certSerial, String pCipherKey) { + return decryptDE(true, 1, certSerial, pCipherKey); + } + + // 测试通过 + @Override + public RetWrap decryptDE(boolean pFlag, int model, byte[] certSerial, String pCipherKey) { + // 获取协议 + //ProtocolRender render = pset.getProtocolReqRender("6B"); + // 与6B相同 + Proto6BXF proto = new Proto6BXF(); + // 填充数据 + PacketSN sn = PacketSN.gen(); + HashMap packet = new HashMap(); + packet.put("header", sn.getSn().array()); + HashMap data = new HashMap(); + if (pFlag) { + data.put("pFlag", "P"); + } + data.put("certificateModel$option", model); + + if (model == 0) { + data.put("certificateSn", certSerial); + } else { + data.put("certificateDn", certSerial); + } + data.put("key", pCipherKey); + data.put("keyType", "X"); + 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 ret = p.toMap(); + if ( + !Arrays.equals(sn.getSn().array(), ((PacketSection) ret.get("header")).getBytes()) + ) { + throw new SydApiException("包序号不匹配", -4); + } + ret = (Map) ret.get("data"); + PacketSection error = (PacketSection) ret.get("error"); + if (!"00".equals(error.getString())) { + throw new SydApiException(error.getInfo(), error.autoInt()); + } + RetWrap rw = new RetWrap(); + + if (pFlag) { + rw.put("oData", ((PacketSection) ret.get("oData")).getString()); + } + + int orgCodeLen = Util.a2i(((PacketSection) ret.get("orgCodeLen")).getBytes(), 10); + if (orgCodeLen != 0) { + rw.put("orgCode", ((PacketSection) ret.get("orgCode")).getString()); + } + + rw.put(SydApi.RET_SESSION_KEY, ((PacketSection) ret.get("lmkHead")).getString() + + ((PacketSection) ret.get("lmk")).getString() + ); + + rw.put(SydApi.RET_KCV, ((PacketSection) ret.get("keyCheck")).getString()); + + return rw; + } + // 测试通过 @Override public String GenerateRandom(int nRandomSize) { diff --git a/src/test/java/com/sunyard/sydapi/test/TestAllMethods.java b/src/test/java/com/sunyard/sydapi/test/TestAllMethods.java index c919cf4..f5f87bc 100644 --- a/src/test/java/com/sunyard/sydapi/test/TestAllMethods.java +++ b/src/test/java/com/sunyard/sydapi/test/TestAllMethods.java @@ -1353,8 +1353,7 @@ public class TestAllMethods { String sessionKey = (String) retWrap.get("SessionKey"); String CipherKey = (String) retWrap.get("CipherKey"); String KCV = (String) retWrap.get("KCV"); - - System.out.println("CipherKey: " + CipherKey); + printRetWrap(retWrap); RetWrap retWrap1 = api.decryptDE(sn, CipherKey); sessionKey = (String) retWrap1.get("SessionKey"); @@ -1365,8 +1364,8 @@ public class TestAllMethods { // 6B @Test public void testDecryptDE2() { -// String sn = "1004282452"; - String sn = "1FD9F2049860FD072F0CC71B072EBF9952E2784E"; + String sn = "1004282452"; +// String sn = "1FD9F2049860FD072F0CC71B072EBF9952E2784E"; RetWrap retWrap = api.generateDE(sn, SydApi.ALG_SM4); String CipherKey = (String) retWrap.get("CipherKey"); printRetWrap(retWrap); @@ -1426,6 +1425,20 @@ public class TestAllMethods { printRetWrap(retWrap1); } + @Test + public void testDecryptDE5() throws UnsupportedEncodingException { + String dn = "C=CN,O=CFCA OCA1,OU=YCCA,OU=Individual-2,CN=YCCA@黄金交易所@Zhuangj@1"; + String oData = "00001111222233"; + + RetWrap retWrap = api.generateDEByDn(dn.getBytes("gbk"), oData); + String CipherKey = (String) retWrap.get(SydApi.RET_CIPHER_KEY); + String sessionKey = (String) retWrap.get(SydApi.RET_SESSION_KEY); + printRetWrap(retWrap); + + RetWrap retWrap1 = api.decryptDEByDN(dn.getBytes("gbk"), CipherKey); + printRetWrap(retWrap1); + } + // 测试通过 // 6C @Test diff --git a/src/test/java/com/sunyard/sydapi/test/TestHT.java b/src/test/java/com/sunyard/sydapi/test/TestHT.java index 90125f6..006201d 100644 --- a/src/test/java/com/sunyard/sydapi/test/TestHT.java +++ b/src/test/java/com/sunyard/sydapi/test/TestHT.java @@ -9,6 +9,11 @@ import org.junit.Before; import org.junit.Test; import racal.sunyard.main.SydApi4j; +import java.io.UnsupportedEncodingException; +import java.util.Base64; + +import static com.sunyard.sydapi.util.PrintUtil.printRetWrap; + /** * 衡泰例子 */ @@ -71,7 +76,7 @@ public class TestHT { // 8018 @Test public void testAttachedSignAndVerify() { - String dn = "C=CN,O=Sunyard,OU=8001,CN=8001"; + String dn = "C=CN,O=CFCA OCA1,OU=YCCA,OU=Individual-2,CN=YCCA@黄金交易所@Zhuangj@1"; String orgData = "111111213323143243243"; String sign = api.attachedSign(orgData.getBytes(), dn); System.out.println("签名结果: " + sign); @@ -82,7 +87,7 @@ public class TestHT { @Test public void testAttachedSignAndVerifyGetX509() { - String dn = "C=CN,O=Sunyard,OU=8001,CN=8001"; + String dn = "C=CN,O=CFCA OCA1,OU=YCCA,OU=Individual-2,CN=YCCA@黄金交易所@Zhuangj@1"; String orgData = "111111213323143243243"; String sign = api.attachedSign(orgData.getBytes(), dn); System.out.println("签名结果: " + sign); @@ -93,7 +98,7 @@ public class TestHT { @Test public void testAttachedSignAndVerifyRD() { - String dn = "C=CN,O=Sunyard,OU=8001,CN=8001"; + String dn = "C=CN,O=CFCA OCA1,OU=YCCA,OU=Individual-2,CN=YCCA@黄金交易所@Zhuangj@1"; String orgData = "111111213323143243243"; String sign = api.attachedSign(orgData.getBytes(), dn); System.out.println("签名结果: " + sign); @@ -104,7 +109,7 @@ public class TestHT { @Test public void testAttachedSignAndVerifyGetAll() { - String dn = "C=CN,O=Sunyard,OU=8001,CN=8001"; + String dn = "C=CN,O=CFCA OCA1,OU=YCCA,OU=Individual-2,CN=YCCA@黄金交易所@Zhuangj@1"; String orgData = "111111213323143243243"; String sign = api.attachedSign(orgData.getBytes(), dn); System.out.println("签名结果: " + sign); @@ -116,6 +121,20 @@ public class TestHT { } } + @Test + public void testDecryptDE5() throws UnsupportedEncodingException { + String dn = "C=CN,O=CFCA OCA1,OU=YCCA,OU=Individual-2,CN=YCCA@黄金交易所@Zhuangj@1"; + String oData = "00001111222233"; + + RetWrap retWrap = api.generateDEByDn(dn.getBytes("gbk"), oData); + String CipherKey = (String) retWrap.get(SydApi.RET_CIPHER_KEY); + String sessionKey = (String) retWrap.get(SydApi.RET_SESSION_KEY); + printRetWrap(retWrap); + + RetWrap retWrap1 = api.decryptDEByDN(dn.getBytes("gbk"), CipherKey); + printRetWrap(retWrap1); + } + // 测试通过 // 76 @Test