From 9c212c4f831dee2634e927d5e0bb51674b92649e Mon Sep 17 00:00:00 2001 From: cheney Date: Fri, 11 Jul 2025 14:52:57 +0800 Subject: [PATCH] =?UTF-8?q?MAC=20=E7=BB=9F=E4=B8=80=E4=B8=BA=20bytes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.properties | 3 +- config2.properties | 7 + pom.xml | 18 +- .../java/com/sunyard/sge/bytes/BytesUtil.java | 20 + .../com/sunyard/sge/database/DataAndMac.java | 8 +- .../java/com/sunyard/sge/database/SydApi.java | 13 +- .../sunyard/sge/database/SydApi4Database.java | 22 +- .../sge/database/SydApiBaseFunction.java | 46 +- .../sunyard/sge/database/SydApiBuilder.java | 10 + .../sge/database/SydApiLongLinkWrap.java | 316 +++++++-- .../sge/plus/ConnectHandlerManager.java | 50 ++ src/main/resources/linux-x86-64/libsunyard.so | Bin 0 -> 155328 bytes src/test/java/sample/Sample.java | 4 +- src/test/java/test/BatchDataTest.java | 10 +- src/test/java/test/FuncTest.java | 625 ++++++++++++++++-- src/test/java/test/ShortBatchDataTest.java | 10 +- src/test/java/test/TestAll.java | 582 +--------------- 说明.txt | 23 +- 18 files changed, 1009 insertions(+), 758 deletions(-) create mode 100644 config2.properties create mode 100644 src/main/java/com/sunyard/sge/bytes/BytesUtil.java create mode 100644 src/main/java/com/sunyard/sge/plus/ConnectHandlerManager.java create mode 100644 src/main/resources/linux-x86-64/libsunyard.so diff --git a/config.properties b/config.properties index 5084357..244c4fb 100644 --- a/config.properties +++ b/config.properties @@ -3,5 +3,4 @@ debug=true # 地址列表 (格式: addX=ip:port) -addr1=172.1.41.129:8889 -addr2=172.1.41.129:8889 +addr1=baishe.fullstack.club:18889 diff --git a/config2.properties b/config2.properties new file mode 100644 index 0000000..5084357 --- /dev/null +++ b/config2.properties @@ -0,0 +1,7 @@ +# 应用配置 +# 调试模式 (true/false) +debug=true + +# 地址列表 (格式: addX=ip:port) +addr1=172.1.41.129:8889 +addr2=172.1.41.129:8889 diff --git a/pom.xml b/pom.xml index 2cceca6..2f98ab1 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ com.sunyard sydapi4j - 1.15.20250701 + 1.15.20250709 org.apache.logging.log4j @@ -50,9 +50,25 @@ ${log.version} + + net.java.dev.jna + jna + 5.13.0 + + + + + src/main/resources + + **/*.so + + false + + + diff --git a/src/main/java/com/sunyard/sge/bytes/BytesUtil.java b/src/main/java/com/sunyard/sge/bytes/BytesUtil.java new file mode 100644 index 0000000..42c3e2a --- /dev/null +++ b/src/main/java/com/sunyard/sge/bytes/BytesUtil.java @@ -0,0 +1,20 @@ +package com.sunyard.sge.bytes; + +public class BytesUtil { + private static final int BLOCK_SIZE = 16; + /** + * 计算填充后的长度 + * @param originalLen 原始长度 + * @return 16字节对齐后的长度 + */ + public static int calculatePaddedLength(int originalLen) { + if (originalLen <= 0) { + return BLOCK_SIZE; + } + // 如果已经是16的倍数(包括0),则补一个完整的块(16字节) + // 否则补足到下一个16的倍数 + return (originalLen % BLOCK_SIZE == 0) + ? originalLen + BLOCK_SIZE + : ((originalLen / BLOCK_SIZE) + 1) * BLOCK_SIZE; + } +} diff --git a/src/main/java/com/sunyard/sge/database/DataAndMac.java b/src/main/java/com/sunyard/sge/database/DataAndMac.java index 2ea21fc..2d94470 100644 --- a/src/main/java/com/sunyard/sge/database/DataAndMac.java +++ b/src/main/java/com/sunyard/sge/database/DataAndMac.java @@ -2,9 +2,9 @@ package com.sunyard.sge.database; public class DataAndMac { private byte[] data; - private String mac; + private byte[] mac; - public DataAndMac(byte[] data, String mac) { + public DataAndMac(byte[] data, byte[] mac) { this.data = data; this.mac = mac; } @@ -17,11 +17,11 @@ public class DataAndMac { this.data = data; } - public String getMac() { + public byte[] getMac() { return mac; } - public void setMac(String mac) { + public void setMac(byte[] mac) { this.mac = mac; } } diff --git a/src/main/java/com/sunyard/sge/database/SydApi.java b/src/main/java/com/sunyard/sge/database/SydApi.java index 1c026db..854f25a 100644 --- a/src/main/java/com/sunyard/sge/database/SydApi.java +++ b/src/main/java/com/sunyard/sge/database/SydApi.java @@ -177,10 +177,7 @@ public interface SydApi { * @return String[] 数组,长度等于 pcData List 的长度。 * String[] 数组中的每个值为 mac值(固定长度16字节)的 HEX字符串。 */ - String[] SYD_SM4Mac_BatchData( - int[] iKeyIndex, - List pcData - ); + public List SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData); /** @@ -195,7 +192,7 @@ public interface SydApi { boolean[] SYD_SM4Mac_BatchData( int[] iKeyIndex, List pcData, - String[] pcMac + List pcMac ); @@ -251,7 +248,7 @@ public interface SydApi { * @param pcData: 输入。计算mac所需要的数据。 * @return mac值(固定长度16字节)转HEX 字符串格式 */ - String SYD_SM4Mac_ShortData( + byte[] SYD_SM4Mac_ShortData( int iKeyIndex, byte[] pcData ); @@ -268,7 +265,7 @@ public interface SydApi { boolean SYD_SM4Mac_ShortData( int iKeyIndex, byte[] pcData, - String pcMac + byte[] pcMac ); @@ -300,7 +297,7 @@ public interface SydApi { int iEncKeyIndex, int iMacKeyIndex, byte[] pcData, - String mac + byte[] mac ); diff --git a/src/main/java/com/sunyard/sge/database/SydApi4Database.java b/src/main/java/com/sunyard/sge/database/SydApi4Database.java index 7694c76..344c98e 100644 --- a/src/main/java/com/sunyard/sge/database/SydApi4Database.java +++ b/src/main/java/com/sunyard/sge/database/SydApi4Database.java @@ -83,7 +83,7 @@ public class SydApi4Database extends SydApiBaseFunction { } @Override - public String[] SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData) { + public List SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData) { if ( null == iKeyIndex || null == pcData ) { throw new IllegalArgumentException("批量输入数据为 null"); @@ -94,7 +94,7 @@ public class SydApi4Database extends SydApiBaseFunction { } if ( 0 == iKeyIndex.length ) { - return new String[0]; + return new ArrayList<>(); } // 将数据进行分组,防止超长。 @@ -132,16 +132,16 @@ public class SydApi4Database extends SydApiBaseFunction { keyGroups.add(curKeyGroup); // 排队处理 - List ret = new ArrayList<>(); + List ret = new ArrayList<>(); for (int i = 0; i < dataGroups.size(); i++) { - String[] macs = super.SYD_SM4Mac_BatchData(toIntArray(keyGroups.get(i)), dataGroups.get(i)); - ret.addAll(Arrays.asList(macs)); + List macs = super.SYD_SM4Mac_BatchData(toIntArray(keyGroups.get(i)), dataGroups.get(i)); + ret.addAll(macs); } - return ret.toArray(new String[0]); + return ret; } @Override - public boolean[] SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData, String[] pcMac) { + public boolean[] SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData, List pcMac) { if ( null == iKeyIndex || null == pcData ) { throw new IllegalArgumentException("批量输入数据为 null"); @@ -158,12 +158,12 @@ public class SydApi4Database extends SydApiBaseFunction { // 将数据进行分组,防止超长。 List> dataGroups = new ArrayList<>(); List> keyGroups = new ArrayList<>(); - List> macGroups = new ArrayList<>(); + List> macGroups = new ArrayList<>(); int totalLen = 0; List curDataGroup = new ArrayList<>(); List curKeyGroup = new ArrayList<>(); - List curMacGroup = new ArrayList<>(); + List curMacGroup = new ArrayList<>(); for (int i = 0; i < pcData.size(); i++) { byte[] data = pcData.get(i); @@ -188,7 +188,7 @@ public class SydApi4Database extends SydApiBaseFunction { curDataGroup.add(data); curKeyGroup.add(iKeyIndex[i]); - curMacGroup.add(pcMac[i]); + curMacGroup.add(pcMac.get(i)); totalLen += data.length; } @@ -203,7 +203,7 @@ public class SydApi4Database extends SydApiBaseFunction { boolean[] rets = super.SYD_SM4Mac_BatchData( toIntArray(keyGroups.get(i)), dataGroups.get(i), - macGroups.get(i).toArray(new String[0]) + macGroups.get(i) ); for (boolean r : rets) { ret.add(r); diff --git a/src/main/java/com/sunyard/sge/database/SydApiBaseFunction.java b/src/main/java/com/sunyard/sge/database/SydApiBaseFunction.java index d02c16c..25cdea6 100644 --- a/src/main/java/com/sunyard/sge/database/SydApiBaseFunction.java +++ b/src/main/java/com/sunyard/sge/database/SydApiBaseFunction.java @@ -515,14 +515,14 @@ public class SydApiBaseFunction implements SydApi { } @Override - public String[] SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData) { + public List SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData) { Logger log = LogFactory.getLogger(); if (LogFactory.iLogLevel == 3) { log.debug("SYD_SM4Mac_BatchData:iKeyIndex:{},pcData元素个数为:{}", iKeyIndex, pcData.size()); } if (pcData.isEmpty()) { - return new String[0]; + return new ArrayList<>(); } if (null == this.hsms || this.hsms.length < 1) { @@ -545,12 +545,12 @@ public class SydApiBaseFunction implements SydApi { if (isShortLinkMode) { try { List list = api.SYMEncryptDecryptBatchData(8, 2, null, keys, 1, pcData); - String[] ret = new String[list.size()]; + List ret = new ArrayList<>(list.size()); for (int i = 0; i < list.size(); i++) { byte[] mac = new byte[16]; byte[] buff = list.get(i); System.arraycopy(buff, buff.length - 16, mac, 0, 16); - ret[i] = Util.bytes2HexString(mac); + ret.add( mac ); } return ret; @@ -566,12 +566,12 @@ public class SydApiBaseFunction implements SydApi { synchronized (api) { try { List list = api.SYMEncryptDecryptBatchData(8, 2, null, keys, 1, pcData); - String[] ret = new String[list.size()]; + List ret = new ArrayList<>(list.size()); for (int i = 0; i < list.size(); i++) { byte[] mac = new byte[16]; byte[] buff = list.get(i); System.arraycopy(buff, buff.length - 16, mac, 0, 16); - ret[i] = Util.bytes2HexString(mac); + ret.add( mac ); } return ret; @@ -593,10 +593,10 @@ public class SydApiBaseFunction implements SydApi { } @Override - public boolean[] SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData, String[] pcMac) { + public boolean[] SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData, List pcMac) { Logger log = LogFactory.getLogger(); if (LogFactory.iLogLevel == 3) { - log.debug("SYD_SM4Mac_BatchData方法接收参数:iKeyIndex:{}, pcData元素个数为:{},pcMac:{}", iKeyIndex, pcData.size(), Arrays.toString(pcMac)); + log.debug("SYD_SM4Mac_BatchData 方法接收参数:iKeyIndex:{}, pcData元素个数为:{},pcMac:{}", iKeyIndex, pcData.size(), pcMac.size()); } if (pcData.isEmpty()) { @@ -626,7 +626,7 @@ public class SydApiBaseFunction implements SydApi { byte[] mac = new byte[16]; byte[] buff = list.get(i); System.arraycopy(buff, buff.length - 16, mac, 0, 16); - ret[i] = pcMac[i].equals(Util.bytes2HexString(mac)); + ret[i] = Arrays.equals(pcMac.get(i), mac); } return ret; @@ -647,7 +647,7 @@ public class SydApiBaseFunction implements SydApi { byte[] mac = new byte[16]; byte[] buff = list.get(i); System.arraycopy(buff, buff.length - 16, mac, 0, 16); - ret[i] = pcMac[i].equals(Util.bytes2HexString(mac)); + ret[i] = Arrays.equals(pcMac.get(i), mac); } return ret; @@ -777,11 +777,11 @@ public class SydApiBaseFunction implements SydApi { } @Override - public String SYD_SM4Mac_ShortData(int iKeyIndex, byte[] pcData) { + public byte[] SYD_SM4Mac_ShortData(int iKeyIndex, byte[] pcData) { return SYD_SM4Mac_ShortData(iKeyIndex, pcData, 1024); } - public String SYD_SM4Mac_ShortData(int iKeyIndex, byte[] pcData, int limitDataLen) { + public byte[] SYD_SM4Mac_ShortData(int iKeyIndex, byte[] pcData, int limitDataLen) { Logger log = LogFactory.getLogger(); if (LogFactory.iLogLevel == 3) { log.debug("SYD_SM4Mac_ShortData方法接收参数:iKeyIndex:{},pcData:{},iFlag:{}", iKeyIndex, Arrays.toString(pcData)); @@ -843,15 +843,15 @@ public class SydApiBaseFunction implements SydApi { } @Override - public boolean SYD_SM4Mac_ShortData(int iKeyIndex, byte[] pcData, String pcMac) { + public boolean SYD_SM4Mac_ShortData(int iKeyIndex, byte[] pcData, byte[] pcMac) { return SYD_SM4Mac_ShortData(iKeyIndex, pcData, 1024, pcMac); } - public boolean SYD_SM4Mac_ShortData(int iKeyIndex, byte[] pcData, int limitDataLen, String pcMac) { + public boolean SYD_SM4Mac_ShortData(int iKeyIndex, byte[] pcData, int limitDataLen, byte[] pcMac) { Logger log = LogFactory.getLogger(); if (LogFactory.iLogLevel == 3) { - log.debug("SYD_SM4Mac_ShortData方法接收参数:{}", iKeyIndex, Arrays.toString(pcData), pcMac); + log.debug("SYD_SM4Mac_ShortData 方法接收参数:{}", iKeyIndex, Util.bytes2HexString(pcData), Util.bytes2HexString(pcMac)); } if ( null == pcData || pcData.length > limitDataLen ) { @@ -866,7 +866,7 @@ public class SydApiBaseFunction implements SydApi { throw new IllegalArgumentException(); } updateLastUseTime(); - String mac = this.SYD_SM4Mac_ShortData(iKeyIndex, pcData, limitDataLen); + byte[] mac = this.SYD_SM4Mac_ShortData(iKeyIndex, pcData, limitDataLen); log.debug(this + ".SYD_SM4Mac_ShortData方法返回值:{}", mac); return pcMac.equals(mac); } @@ -888,14 +888,14 @@ public class SydApiBaseFunction implements SydApi { // 参数检查在函数内部 byte[] enData = SYD_SM4_ShortData(iEncKeyIndex, pcData, Consts.ECB_ENC); // mac 长度放宽到 1024,因为密文填充 - String mac = SYD_SM4Mac_ShortData(iMacKeyIndex, enData, 1040); + byte[] mac = SYD_SM4Mac_ShortData(iMacKeyIndex, enData, 1040); return new DataAndMac(enData, mac); } @Override - public DataAndMacCheck SYD_SM4_CheckMacAndDecrypt_ShortData(int iEncKeyIndex, int iMacKeyIndex, byte[] pcData, String mac) { + public DataAndMacCheck SYD_SM4_CheckMacAndDecrypt_ShortData(int iEncKeyIndex, int iMacKeyIndex, byte[] pcData, byte[] mac) { Logger log = LogFactory.getLogger(); if (LogFactory.iLogLevel == 3) { log.debug("SYD_SM4_CheckMacAndDecrypt_ShortData:k1={} k2={}", iEncKeyIndex, iMacKeyIndex); @@ -933,10 +933,10 @@ public class SydApiBaseFunction implements SydApi { } List enDatas = SYD_SM4_BatchData(iEncKeyIndex, pcData, Consts.ECB_ENC); - String[] macs = SYD_SM4Mac_BatchData(iMacKeyIndex, enDatas); + List macs = SYD_SM4Mac_BatchData(iMacKeyIndex, enDatas); List ret = new ArrayList(); for ( int i=0 ; i< enDatas.size() ; i++ ) { - DataAndMac dam = new DataAndMac(enDatas.get(i), macs[i]); + DataAndMac dam = new DataAndMac(enDatas.get(i), macs.get(i)); ret.add(dam); } return ret; @@ -961,10 +961,10 @@ public class SydApiBaseFunction implements SydApi { throw new SydApiException("密钥长度和数据长度不对应", 0x01000001); } - String[] macs = new String[pcData.size()]; + List macs = new ArrayList<>(pcData.size()); Listdatas = new ArrayList<>(pcData.size()); - for ( int i=0 ; i task = builder.getLongLinkWorks().fTask(new Callable() { + public byte[] SYD_SM4Mac_ShortData(int iKeyIndex, byte[] pcData) { + FutureTask task = builder.getLongLinkWorks().fTask(new Callable() { @Override - public String call() throws Exception { + public byte[] call() throws Exception { return builder.get().SYD_SM4Mac_ShortData(iKeyIndex, pcData); } }); try { return task.get(); } catch (Exception e) { + if ( e.getCause() instanceof SydApiException) { + throw (SydApiException) e.getCause(); + } throw new SydApiException("并行任务执行失败", 0xC101, e); } } @Override - public boolean SYD_SM4Mac_ShortData(int iKeyIndex, byte[] pcData, String pcMac) { + public boolean SYD_SM4Mac_ShortData(int iKeyIndex, byte[] pcData, byte[] pcMac) { FutureTask task = builder.getLongLinkWorks().fTask(new Callable() { @Override public Boolean call() throws Exception { @@ -151,6 +177,9 @@ public class SydApiLongLinkWrap implements SydApi { try { return task.get(); } catch (Exception e) { + if ( e.getCause() instanceof SydApiException) { + throw (SydApiException) e.getCause(); + } throw new SydApiException("并行任务执行失败", 0xC101, e); } } @@ -166,16 +195,19 @@ public class SydApiLongLinkWrap implements SydApi { byte[] enData = taskEnc.get(); - FutureTask taskMac = builder.getLongLinkWorks().fTask(new Callable() { + FutureTask taskMac = builder.getLongLinkWorks().fTask(new Callable() { @Override - public String call() throws Exception { + public byte[] call() throws Exception { return builder.get().SYD_SM4Mac_ShortData(iMacKeyIndex, enData); } }); - String mac = taskMac.get(); + byte[] mac = taskMac.get(); return new DataAndMac(enData, mac); } catch (Exception e) { + if ( e.getCause() instanceof SydApiException) { + throw (SydApiException) e.getCause(); + } throw new SydApiException("并行任务执行失败", 0xC101, e); } } @@ -183,8 +215,40 @@ public class SydApiLongLinkWrap implements SydApi { @Override public DataAndMac SYD_SM4_EncryptAndMac_ShortData(int iEncKeyIndex, int iMacKeyIndex, byte[] pcData) { try { + if ( ! ConnectHandlerManager.isInited() ) { + throw new SydApiException(-1); + } - return SYD_SM4_AllInOne(iEncKeyIndex, iMacKeyIndex, pcData); + int enLen = BytesUtil.calculatePaddedLength(pcData.length); + byte[] encData = new byte[enLen]; + byte[] mac = new byte[16]; + IntByReference encDataLen = new IntByReference(encData.length); + + int i = SydLibrary.INSTANCE.SYD_SM4_Encrypt_Mac_ShortData( + ConnectHandlerManager.getHandler(), + iEncKeyIndex, + iMacKeyIndex, + pcData, + pcData.length, + encData, + encDataLen, + mac + ); + + if ( 0 == i ){ + byte[] data = new byte[encDataLen.getValue()]; + if( data.length == encData.length ) { + data = encData; + } else { + System.arraycopy(encData, 0, data, 0, data.length); + } + + return new DataAndMac(data, mac); + } else { + throw new SydApiException(i); + } + +// return SYD_SM4_AllInOne(iEncKeyIndex, iMacKeyIndex, pcData); // // 线程池忙, 使用单线程计算 // if( builder.getLongLinkWorks().threadPoolIsBusy() ) { @@ -245,53 +309,203 @@ public class SydApiLongLinkWrap implements SydApi { // // return new DataAndMac(enData, mac.getData()); } catch (Exception e) { + if ( e.getCause() instanceof SydApiException) { + throw (SydApiException) e.getCause(); + } throw new SydApiException("并行任务执行失败", 0xC101, e); } } @Override - public DataAndMacCheck SYD_SM4_CheckMacAndDecrypt_ShortData(int iEncKeyIndex, int iMacKeyIndex, byte[] pcData, String mac) { - FutureTask task = builder.getLongLinkWorks().fTask(new Callable() { - @Override - public DataAndMacCheck call() throws Exception { - return builder.get().SYD_SM4_CheckMacAndDecrypt_ShortData(iEncKeyIndex, iMacKeyIndex, pcData, mac); - } - }); + public DataAndMacCheck SYD_SM4_CheckMacAndDecrypt_ShortData(int iEncKeyIndex, int iMacKeyIndex, byte[] pcData, byte[] macs) { + try { - return task.get(); + if ( ! ConnectHandlerManager.isInited() ) { + throw new SydApiException(-1); + } + + byte[] decryptedData = new byte[pcData.length]; + IntByReference decryptedDataLen = new IntByReference(decryptedData.length); + + + int i = SydLibrary.INSTANCE.SYD_SM4_Decrypt_Mac_ShortData( + ConnectHandlerManager.getHandler(), + iEncKeyIndex, + iMacKeyIndex, + pcData, + pcData.length, + macs, + decryptedData, + decryptedDataLen + ); + + byte[] data = new byte[decryptedDataLen.getValue()]; + System.arraycopy(decryptedData, 0, data, 0, data.length); + System.out.println("outlen=" + data.length); + + if ( 0 == i ){ + return new DataAndMacCheck(data, true); + } if (0x01000010 == i) { + return new DataAndMacCheck(data, false); + } else { + throw new SydApiException(i); + } } catch (Exception e) { + if ( e instanceof SydApiException) { + throw e; + } throw new SydApiException("并行任务执行失败", 0xC101, e); } + + + // FutureTask task = builder.getLongLinkWorks().fTask(new Callable() { +// @Override +// public DataAndMacCheck call() throws Exception { +// return builder.get().SYD_SM4_CheckMacAndDecrypt_ShortData(iEncKeyIndex, iMacKeyIndex, pcData, mac); +// } +// }); +// try { +// return task.get(); +// } catch (Exception e) { +// throw new SydApiException("并行任务执行失败", 0xC101, e); +// } } @Override public List SYD_SM4_EncryptAndMac_BatchData(int[] iEncKeyIndex, int[] iMacKeyIndex, List pcData) { - FutureTask> task = builder.getLongLinkWorks().fTask(new Callable>() { - @Override - public List call() throws Exception { - return builder.get().SYD_SM4_EncryptAndMac_BatchData(iEncKeyIndex, iMacKeyIndex, pcData); - } - }); + if ( ! ConnectHandlerManager.isInited() ) { + throw new SydApiException(-1); + } + try { - return task.get(); + int size = pcData.size(); + + // 准备输入参数 - 直接使用 byte[][] + byte[][] pcDataArray = pcData.toArray(new byte[0][]); + int[] iDataLen = pcData.stream() + .mapToInt(bytes -> bytes.length) + .toArray(); + + // 准备输出缓冲区 + byte[][] pcEncData = new byte[size][]; + int[] iEncDataLen = new int[size]; + for (int i = 0; i < size; i++) { + int maxEncLen = ((pcData.get(i).length + 15) / 16 * 16); + pcEncData[i] = new byte[maxEncLen]; + iEncDataLen[i] = maxEncLen; + } + + byte[] pcMac = new byte[16]; + + // 调用加密函数 + int result = SydLibrary.INSTANCE.SYD_SM4_Encrypt_Mac_BatchData( + ConnectHandlerManager.getHandler(), + iEncKeyIndex, + iMacKeyIndex, + pcDataArray, + iDataLen, + size, + pcEncData, + iEncDataLen, + pcMac); + + if (0 == result) { + // 转换结果为Java风格的对象列表 + List results = new ArrayList<>(); + for (int i = 0; i < size; i++) { + results.add(new DataAndMac(pcEncData[i], pcMac)); + } + + return results; + } else { + throw new SydApiException(result); + } } catch (Exception e) { + if ( e.getCause() instanceof SydApiException) { + throw (SydApiException) e.getCause(); + } throw new SydApiException("并行任务执行失败", 0xC101, e); } + +// FutureTask> task = builder.getLongLinkWorks().fTask(new Callable>() { +// @Override +// public List call() throws Exception { +// return builder.get().SYD_SM4_EncryptAndMac_BatchData(iEncKeyIndex, iMacKeyIndex, pcData); +// } +// }); +// try { +// return task.get(); +// } catch (Exception e) { +// throw new SydApiException("并行任务执行失败", 0xC101, e); +// } + } @Override public List SYD_SM4_CheckMacAndDecrypt_BatchData(int[] iEncKeyIndex, int[] iMacKeyIndex, List pcData) { - FutureTask> task = builder.getLongLinkWorks().fTask(new Callable>() { - @Override - public List call() throws Exception { - return builder.get().SYD_SM4_CheckMacAndDecrypt_BatchData(iEncKeyIndex, iMacKeyIndex, pcData); - } - }); - try { - return task.get(); - } catch (Exception e) { - throw new SydApiException("并行任务执行失败", 0xC101, e); + + int size = pcData.size(); + + // 准备输入参数 + byte[][] pcEncData = pcData.stream() + .map(DataAndMac::getData) + .toArray(byte[][]::new); + + int[] iEncDataLen = pcData.stream() + .mapToInt(d -> d.getData().length) + .toArray(); + + byte[][] pcMac = pcData.stream() + .map(d -> d.getMac()) + .toArray(byte[][]::new); + + // 准备输出缓冲区 + byte[][] pcOutData = new byte[size][]; + int[] iOutDataLen = new int[size]; + for (int i = 0; i < size; i++) { + int maxOutLen = pcData.get(i).getData().length; + pcOutData[i] = new byte[maxOutLen]; + iOutDataLen[i] = maxOutLen; } + + int[] iCheckRet = new int[size]; + + // 调用解密函数 + int result = SydLibrary.INSTANCE.SYD_SM4_Decrypt_Mac_BatchData( + ConnectHandlerManager.getHandler(), + iEncKeyIndex, + iMacKeyIndex, + pcEncData, + iEncDataLen, + pcMac, + size, + pcOutData, + iOutDataLen, + iCheckRet); + + if (0 == result) { + // 转换结果为Java风格的对象列表 + List results = new ArrayList<>(); + for (int i = 0; i < size; i++) { + results.add(new DataAndMacCheck(pcOutData[i], iCheckRet[i] == 0)); + } + + return results; + } else { + throw new SydApiException(result); + } + + // FutureTask> task = builder.getLongLinkWorks().fTask(new Callable>() { +// @Override +// public List call() throws Exception { +// return builder.get().SYD_SM4_CheckMacAndDecrypt_BatchData(iEncKeyIndex, iMacKeyIndex, pcData); +// } +// }); +// try { +// return task.get(); +// } catch (Exception e) { +// throw new SydApiException("并行任务执行失败", 0xC101, e); +// } } @Override @@ -395,7 +609,7 @@ public class SydApiLongLinkWrap implements SydApi { } @Override - public String[] SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData) { + public List SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData) { if ( null == iKeyIndex || null == pcData ) { throw new IllegalArgumentException("批量输入数据为 null"); } @@ -405,7 +619,7 @@ public class SydApiLongLinkWrap implements SydApi { } if ( 0 == iKeyIndex.length ) { - return new String[0]; + return new ArrayList<>(); } // 将数据进行分组,防止超长。 List> dataGroups = new ArrayList<>(); @@ -442,13 +656,13 @@ public class SydApiLongLinkWrap implements SydApi { keyGroups.add(curKeyGroup); // 并发处理 - List> tasks = new ArrayList<>(); + List>> tasks = new ArrayList<>(); for (int i = 0; i < dataGroups.size(); i++) { int finalI = i; - FutureTask task = builder.getLongLinkWorks().fTask(new Callable() { + FutureTask> task = builder.getLongLinkWorks().fTask(new Callable>() { @Override - public String[] call() throws Exception { + public List call() throws Exception { SydApi api = builder.get(); return api.SYD_SM4Mac_BatchData(toIntArray(keyGroups.get(finalI)), dataGroups.get(finalI)); } @@ -457,20 +671,23 @@ public class SydApiLongLinkWrap implements SydApi { } try { - List ret = new ArrayList<>(); - for (FutureTask t : tasks) { - String[] macs = t.get(); - ret.addAll(Arrays.asList(macs)); + List ret = new ArrayList<>(); + for (FutureTask> t : tasks) { + List macs = t.get(); + ret.addAll(macs); } - return ret.toArray(new String[0]); + return ret; } catch (Exception e) { + if ( e.getCause() instanceof SydApiException) { + throw (SydApiException) e.getCause(); + } throw new SydApiException("并行任务执行失败", 0xC101, e); } } @Override - public boolean[] SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData, String[] pcMac) { + public boolean[] SYD_SM4Mac_BatchData(int[] iKeyIndex, List pcData, List pcMac) { if ( null == iKeyIndex || null == pcData ) { throw new IllegalArgumentException("批量输入数据为 null"); } @@ -486,12 +703,12 @@ public class SydApiLongLinkWrap implements SydApi { // 将数据进行分组,防止超长。 List> dataGroups = new ArrayList<>(); List> keyGroups = new ArrayList<>(); - List> macGroups = new ArrayList<>(); + List> macGroups = new ArrayList<>(); int totalLen = 0; List curDataGroup = new ArrayList<>(); List curKeyGroup = new ArrayList<>(); - List curMacGroup = new ArrayList<>(); + List curMacGroup = new ArrayList<>(); for (int i = 0; i < pcData.size(); i++) { byte[] data = pcData.get(i); @@ -516,7 +733,7 @@ public class SydApiLongLinkWrap implements SydApi { curDataGroup.add(data); curKeyGroup.add(iKeyIndex[i]); - curMacGroup.add(pcMac[i]); + curMacGroup.add(pcMac.get(i)); totalLen += data.length; } @@ -536,7 +753,7 @@ public class SydApiLongLinkWrap implements SydApi { return api.SYD_SM4Mac_BatchData( toIntArray(keyGroups.get(finalI)), dataGroups.get(finalI), - macGroups.get(finalI).toArray(new String[0]) + macGroups.get(finalI) ); } }); @@ -554,6 +771,9 @@ public class SydApiLongLinkWrap implements SydApi { return toBoolArray(ret); } catch (Exception e) { + if ( e.getCause() instanceof SydApiException) { + throw (SydApiException) e.getCause(); + } throw new SydApiException("并行任务执行失败", 0xC101, e); } } diff --git a/src/main/java/com/sunyard/sge/plus/ConnectHandlerManager.java b/src/main/java/com/sunyard/sge/plus/ConnectHandlerManager.java new file mode 100644 index 0000000..c495b11 --- /dev/null +++ b/src/main/java/com/sunyard/sge/plus/ConnectHandlerManager.java @@ -0,0 +1,50 @@ +package com.sunyard.sge.plus; + +import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; + +/** + * JNA 部分, C 接口的长链接维护。 + */ +public class ConnectHandlerManager { + + private static PointerByReference phHandle = new PointerByReference(); + private static Boolean inited = false; + + /** + * 初始化链接 + * @param pcIpList + * @param iPortList + * @param iConnectTimeOut + * @param iDealTimeOut + * @return + */ + public static int init(String[] pcIpList, + int[] iPortList, + int iConnectTimeOut, + int iDealTimeOut){ + + int connectResult = SydLibrary.INSTANCE.SYD_Connect_Ex( + pcIpList, + iPortList, + pcIpList.length, + iConnectTimeOut, + iDealTimeOut, + 16, + phHandle); + + if ( 0 == connectResult ){ + inited = true; + } + + return connectResult; + } + + public static boolean isInited(){ + return inited; + } + + public static Pointer getHandler(){ + return phHandle.getValue(); + } +} diff --git a/src/main/resources/linux-x86-64/libsunyard.so b/src/main/resources/linux-x86-64/libsunyard.so new file mode 100644 index 0000000000000000000000000000000000000000..cb6fa8bf6dda798ccaa873a4f7cd10d63a3bca01 GIT binary patch literal 155328 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
  • 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 macs = api.SYD_SM4Mac_BatchData( keyIdx , data); boolean[] checks = api.SYD_SM4Mac_BatchData( keyIdx , data, macs ); for ( int i = 0 ; i < data.size() ; i++ ) { diff --git a/src/test/java/test/BatchDataTest.java b/src/test/java/test/BatchDataTest.java index c44c26c..0f17960 100644 --- a/src/test/java/test/BatchDataTest.java +++ b/src/test/java/test/BatchDataTest.java @@ -177,7 +177,7 @@ public class BatchDataTest { data.add(new byte[16]); data.add(new byte[1024]); - String[] macs = api.SYD_SM4Mac_BatchData(keyIdx, data); + List macs = api.SYD_SM4Mac_BatchData(keyIdx, data); boolean[] checks = api.SYD_SM4Mac_BatchData(keyIdx, data, macs); for (int i = 0; i < data.size(); i++) { @@ -204,7 +204,7 @@ public class BatchDataTest { data.add(new byte[1024]); data.add(new byte[1024]); - String[] macs = api.SYD_SM4Mac_BatchData(keyIdx, data); + List macs = api.SYD_SM4Mac_BatchData(keyIdx, data); boolean[] checks = api.SYD_SM4Mac_BatchData(keyIdx, data, macs); for (int i = 0; i < data.size(); i++) { @@ -231,7 +231,7 @@ public class BatchDataTest { data.add(new byte[1024]); data.add(new byte[1024]); - String[] macs = api.SYD_SM4Mac_BatchData(keyIdx, data); + List macs = api.SYD_SM4Mac_BatchData(keyIdx, data); boolean[] checks = api.SYD_SM4Mac_BatchData(keyIdx, data, macs); for (int i = 0; i < data.size(); i++) { @@ -244,7 +244,7 @@ public class BatchDataTest { // 测试数据 RandomTestData data = new RandomTestData(); - String[] macs = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData()); + List macs = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData()); boolean[] checks = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData(), macs); for (int i = 0; i < data.getSize(); i++) { @@ -258,7 +258,7 @@ public class BatchDataTest { // 测试数据 RandomTestData data = new RandomTestData(0); - String[] macs = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData()); + List macs = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData()); boolean[] checks = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData(), macs); for (int i = 0; i < data.getSize(); i++) { diff --git a/src/test/java/test/FuncTest.java b/src/test/java/test/FuncTest.java index 4a8b137..82a7763 100644 --- a/src/test/java/test/FuncTest.java +++ b/src/test/java/test/FuncTest.java @@ -1,94 +1,591 @@ package test; +import com.sunyard.SydApiException; +import com.sunyard.proto.Util; import com.sunyard.sge.database.*; import com.sunyard.sge.log.LogFactory; +import com.sunyard.sge.plus.ConnectHandlerManager; import org.apache.logging.log4j.Logger; import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * 功能确认 */ public class FuncTest { - private SydApi api; - @Before - public void init(){ + public static final int ECB_DEC = 0; + public static final int ECB_ENC = 1; + private Config config; + + private boolean isShortLink = false; + private int connectTimeout = 1000; + private int dealTimeout = 2000; + + public FuncTest(Config config){ + this.config = config; } - @Test - public void syd_Short_Connect_Ex(){ - String[] pcipList = {"192.168.1.129", "192.168.1.129" }; - int[] iportList ={8889,8889}; - SydApi api = new SydApi4Database(); + + + public SydApi connect(){ + if ( isShortLink ) { + + SydApi api = new SydApi4Database(); + api.SYD_Short_Connect_Ex(config.getIps(), config.getPorts(), connectTimeout, dealTimeout); + return api; + } + + else { + SydApiBuilder builder = new SydApiBuilder( + config.getIps(), + config.getPorts(), + connectTimeout, + dealTimeout + ); + SydApi api = builder.build(); + return api; + } + } + + + public void connectFunc() { + isShortLink = true; + System.out.println("-------- 切换到短连模式 --------"); + } + + public void longConnectFunc() { + System.out.println("-------- 设置长连接参数与获取长连接功能测试开始 --------"); +SydApi api = connect(); + System.out.println("设置长连接参数与获取长连接成功"); + + System.out.println("-------- 设置长连接参数与获取长连接功能测试结束 --------\n"); + } + + public void sm3ShortFunc() { + System.out.println("-------- SM3短数据功能测试开始 --------"); + +SydApi api = connect(); + + String res1 = api.SYD_SM3_Hash_ShortData(new byte[1024]); + String res2 = api.SYD_SM3_Hash_ShortData(new byte[0]); + + if (res1.equals(CalcRes.SM3_SHORT_RESULT1) && res2.equals(CalcRes.SM3_SHORT_RESULT2)) { + System.out.println("SM3短数据测试成功"); + } else { + System.out.println("SM3短数据测试失败"); + System.out.println("计算结果:" + res1); + System.out.println("预期结果:" + CalcRes.SM3_SHORT_RESULT1); + System.out.println("计算结果:" + res2); + System.out.println("预期结果:" + CalcRes.SM3_SHORT_RESULT2); + } + try { - api.SYD_Short_Connect_Ex(pcipList,iportList,10,10); - } finally { - if ( null != api ) { - api.SYD_Disconnect_Ex(); + api.SYD_SM3_Hash_ShortData(new byte[4096]); + System.out.println("SM3短数据超出测试失败"); + } catch (SydApiException e) { + System.out.println("SM3短数据超出错误检测成功"); + } + + + System.out.println("-------- SM3短数据功能测试结束 --------\n"); + } + + public void sm3LongFunc() { + System.out.println("-------- SM3长数据功能测试开始 --------"); +SydApi api = connect(); + + ISM3 sm3 = api.initSM3(); + byte[] data = new byte[4064]; + for (int i = 0; i < 10; i++) { + sm3.update(data); + } + String res1 = sm3.digest(); + + data = new byte[0]; + sm3 = api.initSM3(); + for (int i = 0; i < 10; i++) { + sm3.update(data); + } + + String res2 = sm3.digest(); + + if (res1.equals(CalcRes.SM3_LONG_RESULT1) && res2.equals(CalcRes.SM3_LONG_RESULT2)) { + System.out.println("SM3长数据测试成功"); + } else { + System.out.println("SM3长数据测试失败"); + System.out.println("计算结果:" + res1); + System.out.println("预期结果:" + CalcRes.SM3_LONG_RESULT1); + System.out.println("计算结果:" + res2); + System.out.println("预期结果:" + CalcRes.SM3_LONG_RESULT2); + } + + System.out.println("-------- SM3长数据功能测试结束 --------\n"); + } + + public void sm4ShortFunc() { + System.out.println("-------- SM4短数据加解密功能测试开始 --------"); +SydApi api = connect(); + int key = 0; + + byte[] enc1 = api.SYD_SM4_ShortData(key, new byte[1024], ECB_ENC); + byte[] dec1 = api.SYD_SM4_ShortData(key, enc1, ECB_DEC); + + byte[] enc2 = api.SYD_SM4_ShortData(key, new byte[0], ECB_ENC); + byte[] dec2 = api.SYD_SM4_ShortData(key, enc2, ECB_DEC); + + String res1 = Util.bytes2HexString(enc1); + String res2 = Util.bytes2HexString(enc2); + + if (res1.equals(CalcRes.SM4_SHORT_RESULT1) && res2.equals(CalcRes.SM4_SHORT_RESULT2) + && Arrays.equals(dec1, new byte[1024]) && Arrays.equals(dec2, new byte[0])) { + System.out.println("SM4短数据测试成功"); + } else { + System.out.println("SM4短数据测试失败"); + System.out.println("加密计算结果:" + res1); + System.out.println("加密预期结果:" + CalcRes.SM4_SHORT_RESULT1); + System.out.println("解密计算结果:" + Util.bytes2HexString(dec1)); + System.out.println("解密预期结果:" + Util.bytes2HexString(new byte[1024])); + System.out.println("加密计算结果:" + res2); + System.out.println("加密预期结果:" + CalcRes.SM4_SHORT_RESULT2); + System.out.println("解密计算结果:" + Util.bytes2HexString(dec2)); + System.out.println("解密预期结果:" + Util.bytes2HexString(new byte[0])); + } + + try { + enc1 = api.SYD_SM4_ShortData(key, new byte[1025], ECB_ENC); + dec1 = api.SYD_SM4_ShortData(key, enc1, ECB_DEC); + System.out.println("SM4短数据长度超出测试失败"); + } catch (SydApiException e) { + System.out.println("SM4短数据长度超出测试成功"); + } + + System.out.println("-------- SM4短数据加解密功能测试结束 --------\n"); + } + + public void sm4LongFunc() { + System.out.println("-------- SM4长数据加解密功能测试开始 --------"); +SydApi api = connect(); + boolean ret = LongDataTest.case1(api); + if ( ! ret ){ + System.out.println("-------- SM4长数据加解密功能测试失败 --------\n"); + } else { + System.out.println("-------- SM4长数据加解密功能测试结束 --------\n"); + } + + } + + public void sm4macShortFunc() { + System.out.println("-------- SM4MAC短数据功能测试开始 --------"); + SydApi api = connect(); + + int key = 0; + byte[] data1 = new byte[1024]; + byte[] data2 = new byte[0]; + + byte[] res1 = api.SYD_SM4Mac_ShortData(key, data1); + boolean check1 = api.SYD_SM4Mac_ShortData(key, data1, res1); + + byte[] res2 = api.SYD_SM4Mac_ShortData(key, data2); + boolean check2 = api.SYD_SM4Mac_ShortData(key, data2, res2); + + if (res1.equals(CalcRes.SM4MAC_SHORT_RESULT1) && res2.equals(CalcRes.SM4MAC_SHORT_RESULT2) + && check1 && check2) { + System.out.println("SM4MAC短数据测试成功"); + } else { + System.out.println("SM4MAC短数据测试失败"); + System.out.println("计算结果:" + res1); + System.out.println("预期结果:" + CalcRes.SM4MAC_SHORT_RESULT1); + System.out.println("计算结果:" + res2); + System.out.println("预期结果:" + CalcRes.SM4MAC_SHORT_RESULT2); + } + + try { + res1 = api.SYD_SM4Mac_ShortData(key, new byte[1025]); + check1 = api.SYD_SM4Mac_ShortData(key, new byte[1025], res1); + System.out.println("SM4MAC短数据长度超出测试失败"); + } catch (SydApiException e) { + System.out.println("SM4MAC短数据长度超出测试成功"); + } + + System.out.println("-------- SM4MAC短数据功能测试结束 --------\n"); + } + + public void sm4macLongFunc() { + System.out.println("-------- SM4MAC长数据功能测试开始 --------"); +SydApi api = connect(); + + byte[] data1 = new byte[4064]; + int key = 0; + ISM4Mac sm4mac = api.initSM4Mac(key); + for (int i = 0; i < 10; i++) { + sm4mac.update(data1); + } + String res1 = sm4mac.digest(); + + byte[] data2 = new byte[16]; + sm4mac = api.initSM4Mac(key); + for (int i = 0; i < 10; i++) { + sm4mac.update(data2); + } + String res2 = sm4mac.digest(); + +// c = opt.check(hexStringTobytes(SM4MAC_LONG_RESULT1)) +// s = opt.hexcheck(SM4MAC_LONG_RESULT1) +// +// c2 = opt2.check(hexStringTobytes(SM4MAC_LONG_RESULT2)) +// s2 = opt2.hexcheck(SM4MAC_LONG_RESULT2) + + if (res1.equals(CalcRes.SM4MAC_LONG_RESULT1) && res2.equals(CalcRes.SM4MAC_LONG_RESULT2)) { + System.out.println("SM4MAC长数据测试成功"); + } else { + System.out.println("SM4MAC长数据测试失败"); + System.out.println("计算结果:" + res1); + System.out.println("预期结果:" + CalcRes.SM4MAC_LONG_RESULT1); + System.out.println("计算结果:" + res2); + System.out.println("预期结果:" + CalcRes.SM4MAC_LONG_RESULT2); + } + + +// try: +// input = bytes(4080) +// opt2 = conn.sm4_mac_init(0) +// for _ in range(10): +// opt2.update(input) +// res2 = opt2.digest() +// System.out.println('SM4MAC长数据长度超出测试失败') +// except SmxApiException as e: +// System.out.println('SM4MAC长数据长度超出测试成功') +// +// try: +// input = bytes(17) +// opt2 = conn.sm4_mac_init(0) +// for _ in range(10): +// opt2.update(input) +// res2 = opt2.digest() +// System.out.println('SM4MAC长数据16字节未对齐测试失败') +// except SmxApiException as e: +// System.out.println('SM4MAC长数据16字节未对齐测试成功') +// +// System.out.println('-------- SM4MAC长数据功能测试结束 --------\n') + } + + public void sm4BatchFunc() { + System.out.println("-------- SM4批量数据加解密功能测试开始 --------"); +SydApi api = connect(); + + List data = new ArrayList<>(); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + int[] key = new int[]{0, 0, 0, 0, 0, 0, 0}; + + + List en_data = api.SYD_SM4_BatchData(key, data, ECB_ENC); + + List de_data = api.SYD_SM4_BatchData(key, en_data, ECB_DEC); + + if (en_data.equals(CalcRes.SM4_BATCH_RESULT) && data.equals(de_data)) { + System.out.println("SM4批量数据加解密测试成功"); + } else { + System.out.println("SM4批量数据加解密测试失败"); +// System.out.println("加密计算结果:" + en_data); + System.out.println("加密预期结果:" + CalcRes.SM4_BATCH_RESULT); +// System.out.println("解密计算结果:" + de_data); +// System.out.println("解密预期结果:" + data); + } + + System.out.println("-------- SM4批量数据加解密功能测试结束 --------\n"); + } + + public void sm4macBatchFunc() { + System.out.println("-------- SM4MAC批量数据功能测试开始 --------"); +SydApi api = connect(); + + List data = new ArrayList<>(); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + int[] keyIdx = new int[]{0, 0, 0, 0}; + + List s = api.SYD_SM4Mac_BatchData(keyIdx, data); + + boolean[] check = api.SYD_SM4Mac_BatchData(keyIdx, data, s); + + boolean flag = true; + if ( null != s) { + for (int i = 0; i < check.length; i++) { + if (!check[i]) { + flag = false; + break; + } + } + if (flag) { + System.out.println("SM4MAC批量数据测试成功"); + } else { + System.out.println("SM4MAC批量数据测试失败"); + System.out.println("计算结果:" + check); + System.out.println("预期结果:" + new boolean[]{true, true, true, true, true, true, true}); + } + } else { + System.out.println("SM4MAC批量数据测试失败"); + System.out.println("计算结果:" + s); + System.out.println("预期结果:" + CalcRes.SM4MAC_BATCH_RESULT); + } + + System.out.println("-------- SM4MAC批量数据功能测试结束 --------\n"); + } + + public void sm4ShortMacFunc() { + System.out.println("-------- 短数据加密计算MAC,校验MAC解密功能测试开始 --------"); + SydApi api = connect(); + + int keyEnc = 0; + int keyMac = 0; + byte[] data1 = new byte[1024]; + byte[] data2 = new byte[0]; + System.out.println("step1"); + DataAndMac en_data1 = api.SYD_SM4_EncryptAndMac_ShortData(keyEnc, keyMac, data1); + System.out.println("step2"); + DataAndMacCheck de_data1 = api.SYD_SM4_CheckMacAndDecrypt_ShortData(keyEnc, keyMac, en_data1.getData(), en_data1.getMac()); + System.out.println("step3"); + + + try { + DataAndMac en_data2 = api.SYD_SM4_EncryptAndMac_ShortData(keyEnc, keyMac, data2); + System.out.println("step4"); + }catch ( SydApiException e) { + if ( e.getRetCode() == 0x1000001 ) { + System.out.println("SYD_SM4_EncryptAndMac_ShortData 不支持数据长度为 0, 测试通过"); + } else { + System.out.println("SYD_SM4_EncryptAndMac_ShortData 不支持数据长度为 0, 测试失败"); + } + } + + try { + DataAndMacCheck de_data2 = api.SYD_SM4_CheckMacAndDecrypt_ShortData(keyEnc, keyMac, new byte[15], new byte[0]); + System.out.println("step5"); + }catch ( SydApiException e) { + if ( e.getRetCode() == 0x1000001 ) { + System.out.println("SYD_SM4_CheckMacAndDecrypt_ShortData 不支持数据长度为非 16 字节, 测试通过"); + } else { + System.out.println("SYD_SM4_CheckMacAndDecrypt_ShortData 不支持数据长度为非 16 字节, 测试失败"); + } + } + + + try { + data1 = new byte[1025]; + api.SYD_SM4_EncryptAndMac_ShortData(keyEnc, keyMac, data1); + System.out.println("短数据加密计算MAC,校验MAC解密长度超出测试失败"); + } catch (SydApiException e) { + System.out.println("短数据加密计算MAC,校验MAC解密长度超出测试成功"); + } + + System.out.println("-------- 短数据加密计算MAC,校验MAC解密功能测试结束 --------\n"); + } + + public void sm4BatchMacFunc() { + System.out.println("-------- 批量数据加密计算MAC,校验MAC解密功能测试开始 --------"); + SydApi api = connect(); + + List data = new ArrayList<>(); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + int[] keyEnc = new int[]{0, 0, 0, 0, 0, 0, 0}; + int[] keyMac = new int[]{0, 0, 0, 0, 0, 0, 0}; + + List en_data = api.SYD_SM4_EncryptAndMac_BatchData(keyEnc, keyMac, data); + List de_data = api.SYD_SM4_CheckMacAndDecrypt_BatchData(keyEnc, keyMac, en_data); + + boolean flag = true; + for (int i = 0; i < de_data.size(); i++) { + flag = flag & de_data.get(i).getMacCheck(); + } + + if (flag) { + System.out.println("批量数据加密计算MAC,校验MAC解密测试成功"); + } else { + System.out.println("批量数据加密计算MAC,校验MAC解密测试失败"); + System.out.println("加密计算结果:" + en_data); + System.out.println("加密预期结果:" + CalcRes.SM4ANDMAC_BATCH_RESULT); + } + + + try { + data = new ArrayList<>(); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + data.add(new byte[1024]); + keyEnc = new int[]{0, 1, 0, 1, 1, 1, 1, 0}; + keyMac = new int[]{1, 0, 1, 0, 0, 0, 0, 0}; + + en_data = api.SYD_SM4_EncryptAndMac_BatchData(keyEnc, keyMac, data); + de_data = api.SYD_SM4_CheckMacAndDecrypt_BatchData(keyEnc, keyMac, en_data); + System.out.println("批量数据加密计算MAC,校验MAC解密长度超出测试失败"); + } catch (SydApiException e) { + System.out.println("批量数据加密计算MAC,校验MAC解密长度超出测试成功"); + } + + System.out.println("-------- 批量数据加密计算MAC,校验MAC解密功能测试结束 --------\n"); + } + + + public void loggerFunc() { + System.out.println("-------- 设置日志配置功能测试开始 --------"); + SydApi.SYD_SetLogConfig("./logs", 3); + System.out.println("日志配置成功,存储路径./logs,级别3"); + System.out.println("-------- 设置日志配置功能测试结束 --------\n"); + } + + + public void funcAll() { + this.loggerFunc(); + this.connectFunc(); + this.longConnectFunc(); + + this.sm3ShortFunc(); + + + this.sm4ShortFunc(); + this.sm4BatchFunc(); + + + this.sm4macShortFunc(); + this.sm4macBatchFunc(); + + + this.sm4ShortMacFunc(); + this.sm4BatchMacFunc(); + + + this.sm4LongFunc(); + this.sm4macLongFunc(); + this.sm3LongFunc(); + + } + + public void funcMenu(Config config, Choise choise, FuncTest test) throws Exception{ + + boolean flag = true; + SydApi.SYD_SetLogConfig("./logs", 3); + while (flag) { + System.out.println("请选择测试项:"); + System.out.println("1. 日志测试"); + System.out.println("2. 短链接测试"); + System.out.println("3. 长链接测试"); + + System.out.println("4. SM3短数据"); + + + System.out.println("5. SM4 短数据加解密"); + System.out.println("6. SM4 批量数据加解密"); + System.out.println("7. SM4MAC 短数据"); + System.out.println("8. SM4MAC 批量数据"); + + System.out.println("9. SM4短数据加密后生成MAC,解密验证MAC"); + System.out.println("10. SM4批量数据加密后生成MAC,验证MAC解密"); + + + System.out.println("11. SM4长数据加解密"); + System.out.println("12. SM4长数据MAC"); + + + System.out.println("13. SM3长数据"); + System.out.println("-1.退出"); + + switch (choise.getAsInt()) { + case 1: + test.loggerFunc(); + break; + case 2: + test.connectFunc(); + break; + case 3: + test.longConnectFunc(); + break; + case 4: + test.sm3ShortFunc(); + break; + + case 5: + test.sm4ShortFunc(); + break; + case 6: + test.sm4BatchFunc(); + break; + + case 7: + test.sm4macShortFunc(); + break; + case 8: { + test.sm4macBatchFunc(); + break; + } + case 9: { + test.sm4ShortMacFunc(); + break; + } + case 10: { + test.sm4BatchMacFunc(); + break; + } + case 11: + test.sm4macLongFunc(); + break; + case 12: + test.sm4LongFunc(); + break; + case 13: + test.sm3LongFunc(); + break; + case -1: + flag = false; + break; } } } - @Test - public void sm3(){ - SydApiBuilder builder = new SydApiBuilder( - new String[]{ "192.168.1.129" }, - new int[]{ 8889 }, - 10, - 15 - ); - - api = builder.build(); - byte[] bytes = api.SYD_SM4_LongData(1, new byte[16], 0, 1); - String s = Arrays.toString(bytes); - System.out.println(s); - } - - @Test - public void originLong() throws Exception { - SydApiBuilder builder = new SydApiBuilder(new String[]{"192.168.1.129"}, new int[]{8889}, 10, 15); - SydApi.SYD_SetLogConfig("./logs/",3); - for (int i = 0; i < 1000; i++) { - int tmp = i; - new Thread(() -> { - SydApi api = builder.build(); - for (int j = 0; j < 20; j++) { - byte[] bytes = api.SYD_SM4_ShortData(0, (tmp + "t" + j).getBytes(), Consts.ECB_ENC); - byte[] result = api.SYD_SM4_ShortData(0, bytes, Consts.ECB_DEC); - } - }, i + "t").start(); - } - } - @Test - public void originShort() throws Exception { - SydApi.SYD_SetLogConfig("./logs/",3); - Logger log = LogFactory.getLogger(); - String[] pcipList = {"192.168.1.129", "192.168.1.129" }; - int[] iportList ={8080,8889}; - for (int i = 0; i < 1000; i++) { - int tmp = i; - new Thread(() -> { - SydApi api = new SydApi4Database(); - try { - for (int j = 0; j < 20; j++) { - api.SYD_Short_Connect_Ex(pcipList,iportList,50,15); - byte[] bytes = api.SYD_SM4_ShortData(0, (tmp + "t" + j).getBytes(), Consts.ECB_ENC); - byte[] result = api.SYD_SM4_ShortData(0, bytes, Consts.ECB_DEC); - } - } - finally { - if ( null != api ) { - api.SYD_Disconnect_Ex(); - } - } } , i + "t").start(); + public static void main(String[] args) throws Exception { + Config config = config = Config.loadConfig(); + if (config == null || config.isDebug()) { + System.setProperty("com.sunyard.sydapi4j.debug", "true"); } + + System.out.println("ips=" + Arrays.toString(config.getIps())); + System.out.println("ports=" + Arrays.toString(config.getPorts())); + System.out.println("debug=" + config.isDebug()); + + Choise choise = new Choise(args); + + FuncTest funcTest = new FuncTest(config); + SydApi.SYD_SetLogConfig("./logs", 3); + funcTest.funcMenu(config, choise, funcTest); + } + } diff --git a/src/test/java/test/ShortBatchDataTest.java b/src/test/java/test/ShortBatchDataTest.java index 8df1ca4..64ccd2d 100644 --- a/src/test/java/test/ShortBatchDataTest.java +++ b/src/test/java/test/ShortBatchDataTest.java @@ -174,7 +174,7 @@ public class ShortBatchDataTest { data.add(new byte[16]); data.add(new byte[1024]); - String[] macs = api.SYD_SM4Mac_BatchData(keyIdx, data); + List macs = api.SYD_SM4Mac_BatchData(keyIdx, data); boolean[] checks = api.SYD_SM4Mac_BatchData(keyIdx, data, macs); for (int i = 0; i < data.size(); i++) { @@ -201,7 +201,7 @@ public class ShortBatchDataTest { data.add(new byte[1024]); data.add(new byte[1024]); - String[] macs = api.SYD_SM4Mac_BatchData(keyIdx, data); + List macs = api.SYD_SM4Mac_BatchData(keyIdx, data); boolean[] checks = api.SYD_SM4Mac_BatchData(keyIdx, data, macs); for (int i = 0; i < data.size(); i++) { @@ -228,7 +228,7 @@ public class ShortBatchDataTest { data.add(new byte[1024]); data.add(new byte[1024]); - String[] macs = api.SYD_SM4Mac_BatchData(keyIdx, data); + List macs = api.SYD_SM4Mac_BatchData(keyIdx, data); boolean[] checks = api.SYD_SM4Mac_BatchData(keyIdx, data, macs); for (int i = 0; i < data.size(); i++) { @@ -241,7 +241,7 @@ public class ShortBatchDataTest { // 测试数据 ShortBatchDataTest.RandomTestData data = new ShortBatchDataTest.RandomTestData(); - String[] macs = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData()); + List macs = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData()); boolean[] checks = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData(), macs); for (int i = 0; i < data.getSize(); i++) { @@ -255,7 +255,7 @@ public class ShortBatchDataTest { // 测试数据 ShortBatchDataTest.RandomTestData data = new ShortBatchDataTest.RandomTestData(0); - String[] macs = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData()); + List macs = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData()); boolean[] checks = api.SYD_SM4Mac_BatchData(data.getKeyIdx(), data.getData(), macs); for (int i = 0; i < data.getSize(); i++) { diff --git a/src/test/java/test/TestAll.java b/src/test/java/test/TestAll.java index f7c67cc..0c6c3db 100644 --- a/src/test/java/test/TestAll.java +++ b/src/test/java/test/TestAll.java @@ -33,6 +33,7 @@ public class TestAll { } + private static void startPerformanceFile() { try (FileWriter fw = new FileWriter(PERF_RESULT_FILE, true); BufferedWriter bw = new BufferedWriter(fw)) { @@ -98,6 +99,7 @@ public class TestAll { public static void menu1(Config config, Choise choise, TestAll test) throws Exception { + FuncTest funcTest = new FuncTest(config); while ( true ) { @@ -112,16 +114,17 @@ public class TestAll { switch (choise.getAsInt()) { case 1: SydApi.SYD_SetLogConfig("./logs", 3); - funcMenu(config, choise, test); + funcTest.funcMenu(config, choise, funcTest); break; case 2: - test.funcAll(); + funcTest.funcAll(); break; case 3: SydApi.SYD_SetLogConfig("./logs", 0); perfMenu(config, choise, test); break; case 4: + SydApi.SYD_SetLogConfig("./logs", 0); startPerformanceFile(); test.perfAll(); stopPerformanceFile(); @@ -375,7 +378,7 @@ public class TestAll { for (int i = 0; i < totalRequest; i++) { poolEnc.submit(() -> { SydApi api = Api.get(); - String[] s = api.SYD_SM4Mac_BatchData(keyIdx, data); + List s = api.SYD_SM4Mac_BatchData(keyIdx, data); }); } poolEnc.shutdown(); @@ -723,574 +726,6 @@ public class TestAll { System.out.println("-------- SM3长数据性能测试结束 --------"); } - public void loggerFunc() { - System.out.println("-------- 设置日志配置功能测试开始 --------"); - SydApi.SYD_SetLogConfig("./logs", 3); - System.out.println("日志配置成功,存储路径./logs,级别3"); - System.out.println("-------- 设置日志配置功能测试结束 --------\n"); - } - - public void connectFunc() { - System.out.println("-------- 短连接建立与关闭功能测试开始 --------"); - - System.out.println("-------- 短连接建立与关闭功能测试结束 --------\n"); - } - - public void longConnectFunc() { - System.out.println("-------- 设置长连接参数与获取长连接功能测试开始 --------"); - SydApiBuilder builder = new SydApiBuilder( - config.getIps(), - config.getPorts(), - 1000, - 2000 - ); - SydApi api = builder.build(); - System.out.println("设置长连接参数与获取长连接成功"); - - System.out.println("-------- 设置长连接参数与获取长连接功能测试结束 --------\n"); - } - - public void sm3ShortFunc() { - System.out.println("-------- SM3短数据功能测试开始 --------"); - - SydApiBuilder builder = new SydApiBuilder( - config.getIps(), - config.getPorts(), - 1000, - 2000 - ); - SydApi api = builder.build(); - - String res1 = api.SYD_SM3_Hash_ShortData(new byte[1024]); - String res2 = api.SYD_SM3_Hash_ShortData(new byte[0]); - - if (res1.equals(CalcRes.SM3_SHORT_RESULT1) && res2.equals(CalcRes.SM3_SHORT_RESULT2)) { - System.out.println("SM3短数据测试成功"); - } else { - System.out.println("SM3短数据测试失败"); - System.out.println("计算结果:" + res1); - System.out.println("预期结果:" + CalcRes.SM3_SHORT_RESULT1); - System.out.println("计算结果:" + res2); - System.out.println("预期结果:" + CalcRes.SM3_SHORT_RESULT2); - } - - try { - api.SYD_SM3_Hash_ShortData(new byte[4096]); - System.out.println("SM3短数据超出测试失败"); - } catch (SydApiException e) { - System.out.println("SM3短数据超出错误检测成功"); - } - - - System.out.println("-------- SM3短数据功能测试结束 --------\n"); - } - - public void sm3LongFunc() { - System.out.println("-------- SM3长数据功能测试开始 --------"); - SydApiBuilder builder = new SydApiBuilder( - config.getIps(), - config.getPorts(), - 1000, - 2000 - ); - SydApi api = builder.build(); - - ISM3 sm3 = api.initSM3(); - byte[] data = new byte[4064]; - for (int i = 0; i < 10; i++) { - sm3.update(data); - } - String res1 = sm3.digest(); - - data = new byte[0]; - sm3 = api.initSM3(); - for (int i = 0; i < 10; i++) { - sm3.update(data); - } - - String res2 = sm3.digest(); - - if (res1.equals(CalcRes.SM3_LONG_RESULT1) && res2.equals(CalcRes.SM3_LONG_RESULT2)) { - System.out.println("SM3长数据测试成功"); - } else { - System.out.println("SM3长数据测试失败"); - System.out.println("计算结果:" + res1); - System.out.println("预期结果:" + CalcRes.SM3_LONG_RESULT1); - System.out.println("计算结果:" + res2); - System.out.println("预期结果:" + CalcRes.SM3_LONG_RESULT2); - } - - System.out.println("-------- SM3长数据功能测试结束 --------\n"); - } - - public void sm4ShortFunc() { - System.out.println("-------- SM4短数据加解密功能测试开始 --------"); - SydApiBuilder builder = new SydApiBuilder( - config.getIps(), - config.getPorts(), - 1000, - 2000 - ); - SydApi api = builder.build(); - int key = 0; - - byte[] enc1 = api.SYD_SM4_ShortData(key, new byte[1024], ECB_ENC); - byte[] dec1 = api.SYD_SM4_ShortData(key, enc1, ECB_DEC); - - byte[] enc2 = api.SYD_SM4_ShortData(key, new byte[0], ECB_ENC); - byte[] dec2 = api.SYD_SM4_ShortData(key, enc2, ECB_DEC); - - String res1 = Util.bytes2HexString(enc1); - String res2 = Util.bytes2HexString(enc2); - - if (res1.equals(CalcRes.SM4_SHORT_RESULT1) && res2.equals(CalcRes.SM4_SHORT_RESULT2) - && Arrays.equals(dec1, new byte[1024]) && Arrays.equals(dec2, new byte[0])) { - System.out.println("SM4短数据测试成功"); - } else { - System.out.println("SM4短数据测试失败"); - System.out.println("加密计算结果:" + res1); - System.out.println("加密预期结果:" + CalcRes.SM4_SHORT_RESULT1); - System.out.println("解密计算结果:" + Util.bytes2HexString(dec1)); - System.out.println("解密预期结果:" + Util.bytes2HexString(new byte[1024])); - System.out.println("加密计算结果:" + res2); - System.out.println("加密预期结果:" + CalcRes.SM4_SHORT_RESULT2); - System.out.println("解密计算结果:" + Util.bytes2HexString(dec2)); - System.out.println("解密预期结果:" + Util.bytes2HexString(new byte[0])); - } - - try { - enc1 = api.SYD_SM4_ShortData(key, new byte[1025], ECB_ENC); - dec1 = api.SYD_SM4_ShortData(key, enc1, ECB_DEC); - System.out.println("SM4短数据长度超出测试失败"); - } catch (SydApiException e) { - System.out.println("SM4短数据长度超出测试成功"); - } - - System.out.println("-------- SM4短数据加解密功能测试结束 --------\n"); - } - - public void sm4LongFunc() { - System.out.println("-------- SM4长数据加解密功能测试开始 --------"); - SydApiBuilder builder = new SydApiBuilder( - config.getIps(), - config.getPorts(), - 1000, - 2000 - ); - SydApi api = builder.build(); - boolean ret = LongDataTest.case1(api); - if ( ! ret ){ - System.out.println("-------- SM4长数据加解密功能测试失败 --------\n"); - } else { - System.out.println("-------- SM4长数据加解密功能测试结束 --------\n"); - } - - } - - public void sm4macShortFunc() { - System.out.println("-------- SM4MAC短数据功能测试开始 --------"); - SydApiBuilder builder = new SydApiBuilder( - config.getIps(), - config.getPorts(), - 1000, - 2000 - ); - SydApi api = builder.build(); - - int key = 0; - byte[] data1 = new byte[1024]; - byte[] data2 = new byte[0]; - - String res1 = api.SYD_SM4Mac_ShortData(key, data1); - boolean check1 = api.SYD_SM4Mac_ShortData(key, data1, res1); - - String res2 = api.SYD_SM4Mac_ShortData(key, data2); - boolean check2 = api.SYD_SM4Mac_ShortData(key, data2, res2); - - if (res1.equals(CalcRes.SM4MAC_SHORT_RESULT1) && res2.equals(CalcRes.SM4MAC_SHORT_RESULT2) - && check1 && check2) { - System.out.println("SM4MAC短数据测试成功"); - } else { - System.out.println("SM4MAC短数据测试失败"); - System.out.println("计算结果:" + res1); - System.out.println("预期结果:" + CalcRes.SM4MAC_SHORT_RESULT1); - System.out.println("计算结果:" + res2); - System.out.println("预期结果:" + CalcRes.SM4MAC_SHORT_RESULT2); - } - - try { - res1 = api.SYD_SM4Mac_ShortData(key, new byte[1025]); - check1 = api.SYD_SM4Mac_ShortData(key, new byte[1025], res1); - System.out.println("SM4MAC短数据长度超出测试失败"); - } catch (SydApiException e) { - System.out.println("SM4MAC短数据长度超出测试成功"); - } - - System.out.println("-------- SM4MAC短数据功能测试结束 --------\n"); - } - - public void sm4macLongFunc() { - System.out.println("-------- SM4MAC长数据功能测试开始 --------"); - SydApiBuilder builder = new SydApiBuilder( - config.getIps(), - config.getPorts(), - 1000, - 2000 - ); - SydApi api = builder.build(); - - byte[] data1 = new byte[4064]; - int key = 0; - ISM4Mac sm4mac = api.initSM4Mac(key); - for (int i = 0; i < 10; i++) { - sm4mac.update(data1); - } - String res1 = sm4mac.digest(); - - byte[] data2 = new byte[16]; - sm4mac = api.initSM4Mac(key); - for (int i = 0; i < 10; i++) { - sm4mac.update(data2); - } - String res2 = sm4mac.digest(); - -// c = opt.check(hexStringTobytes(SM4MAC_LONG_RESULT1)) -// s = opt.hexcheck(SM4MAC_LONG_RESULT1) -// -// c2 = opt2.check(hexStringTobytes(SM4MAC_LONG_RESULT2)) -// s2 = opt2.hexcheck(SM4MAC_LONG_RESULT2) - - if (res1.equals(CalcRes.SM4MAC_LONG_RESULT1) && res2.equals(CalcRes.SM4MAC_LONG_RESULT2)) { - System.out.println("SM4MAC长数据测试成功"); - } else { - System.out.println("SM4MAC长数据测试失败"); - System.out.println("计算结果:" + res1); - System.out.println("预期结果:" + CalcRes.SM4MAC_LONG_RESULT1); - System.out.println("计算结果:" + res2); - System.out.println("预期结果:" + CalcRes.SM4MAC_LONG_RESULT2); - } - - -// try: -// input = bytes(4080) -// opt2 = conn.sm4_mac_init(0) -// for _ in range(10): -// opt2.update(input) -// res2 = opt2.digest() -// System.out.println('SM4MAC长数据长度超出测试失败') -// except SmxApiException as e: -// System.out.println('SM4MAC长数据长度超出测试成功') -// -// try: -// input = bytes(17) -// opt2 = conn.sm4_mac_init(0) -// for _ in range(10): -// opt2.update(input) -// res2 = opt2.digest() -// System.out.println('SM4MAC长数据16字节未对齐测试失败') -// except SmxApiException as e: -// System.out.println('SM4MAC长数据16字节未对齐测试成功') -// -// System.out.println('-------- SM4MAC长数据功能测试结束 --------\n') - } - - public void sm4BatchFunc() { - System.out.println("-------- SM4批量数据加解密功能测试开始 --------"); - SydApiBuilder builder = new SydApiBuilder( - config.getIps(), - config.getPorts(), - 1000, - 2000 - ); - SydApi api = builder.build(); - - List data = new ArrayList<>(); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - int[] key = new int[]{0, 0, 0, 0, 0, 0, 0}; - - - List en_data = api.SYD_SM4_BatchData(key, data, ECB_ENC); - - List de_data = api.SYD_SM4_BatchData(key, en_data, ECB_DEC); - - if (en_data.equals(CalcRes.SM4_BATCH_RESULT) && data.equals(de_data)) { - System.out.println("SM4批量数据加解密测试成功"); - } else { - System.out.println("SM4批量数据加解密测试失败"); -// System.out.println("加密计算结果:" + en_data); - System.out.println("加密预期结果:" + CalcRes.SM4_BATCH_RESULT); -// System.out.println("解密计算结果:" + de_data); -// System.out.println("解密预期结果:" + data); - } - - System.out.println("-------- SM4批量数据加解密功能测试结束 --------\n"); - } - - public void sm4macBatchFunc() { - System.out.println("-------- SM4MAC批量数据功能测试开始 --------"); - SydApiBuilder builder = new SydApiBuilder( - config.getIps(), - config.getPorts(), - 1000, - 2000 - ); - SydApi api = builder.build(); - - List data = new ArrayList<>(); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - int[] keyIdx = new int[]{0, 0, 0, 0}; - - String[] s = api.SYD_SM4Mac_BatchData(keyIdx, data); - - boolean[] check = api.SYD_SM4Mac_BatchData(keyIdx, data, s); - - boolean flag = true; - if (Arrays.equals(s, CalcRes.SM4MAC_BATCH_RESULT)) { - for (int i = 0; i < check.length; i++) { - if (!check[i]) { - flag = false; - break; - } - } - if (flag) { - System.out.println("SM4MAC批量数据测试成功"); - } else { - System.out.println("SM4MAC批量数据测试失败"); - System.out.println("计算结果:" + check); - System.out.println("预期结果:" + new boolean[]{true, true, true, true, true, true, true}); - } - } else { - System.out.println("SM4MAC批量数据测试失败"); - System.out.println("计算结果:" + s); - System.out.println("预期结果:" + CalcRes.SM4MAC_BATCH_RESULT); - } - - System.out.println("-------- SM4MAC批量数据功能测试结束 --------\n"); - } - - public void sm4ShortMacFunc() { - System.out.println("-------- 短数据加密计算MAC,校验MAC解密功能测试开始 --------"); - SydApiBuilder builder = new SydApiBuilder( - config.getIps(), - config.getPorts(), - 1000, - 2000 - ); - SydApi api = builder.build(); - - int keyEnc = 0; - int keyMac = 0; - byte[] data1 = new byte[1024]; - byte[] data2 = new byte[0]; - DataAndMac en_data1 = api.SYD_SM4_EncryptAndMac_ShortData(keyEnc, keyMac, data1); - DataAndMacCheck de_data1 = api.SYD_SM4_CheckMacAndDecrypt_ShortData(keyEnc, keyMac, en_data1.getData(), en_data1.getMac()); - - DataAndMac en_data2 = api.SYD_SM4_EncryptAndMac_ShortData(keyEnc, keyMac, data2); - DataAndMacCheck de_data2 = api.SYD_SM4_CheckMacAndDecrypt_ShortData(keyEnc, keyMac, en_data2.getData(), en_data2.getMac()); - - if ( - de_data2.getMacCheck() - ) { - System.out.println("短数据加密计算MAC,校验MAC解密测试成功"); - } else { - System.out.println("短数据加密计算MAC,校验MAC解密测试失败"); - System.out.println("加密计算结果:" + en_data1); - System.out.println("加密预期结果:" + CalcRes.SM4ANDMAC_SHORT_RESULT1); - System.out.println("解密计算结果:" + de_data1); - System.out.println("解密预期结果:" + data1); - System.out.println("加密计算结果:" + en_data2); - System.out.println("加密预期结果:" + CalcRes.SM4ANDMAC_SHORT_RESULT2); - System.out.println("解密计算结果:" + de_data2); - System.out.println("解密预期结果:" + data2); - } - - try { - data1 = new byte[1025]; - api.SYD_SM4_EncryptAndMac_ShortData(keyEnc, keyMac, data1); - System.out.println("短数据加密计算MAC,校验MAC解密长度超出测试失败"); - } catch (SydApiException e) { - System.out.println("短数据加密计算MAC,校验MAC解密长度超出测试成功"); - } - - System.out.println("-------- 短数据加密计算MAC,校验MAC解密功能测试结束 --------\n"); - } - - public void sm4BatchMacFunc() { - System.out.println("-------- 批量数据加密计算MAC,校验MAC解密功能测试开始 --------"); - SydApiBuilder builder = new SydApiBuilder( - config.getIps(), - config.getPorts(), - 1000, - 2000 - ); - SydApi api = builder.build(); - - List data = new ArrayList<>(); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - int[] keyEnc = new int[]{0, 0, 0, 0, 0, 0, 0}; - int[] keyMac = new int[]{0, 0, 0, 0, 0, 0, 0}; - - List en_data = api.SYD_SM4_EncryptAndMac_BatchData(keyEnc, keyMac, data); - List de_data = api.SYD_SM4_CheckMacAndDecrypt_BatchData(keyEnc, keyMac, en_data); - - boolean flag = true; - for (int i = 0; i < de_data.size(); i++) { - flag = flag & de_data.get(i).getMacCheck(); - } - - if (flag) { - System.out.println("批量数据加密计算MAC,校验MAC解密测试成功"); - } else { - System.out.println("批量数据加密计算MAC,校验MAC解密测试失败"); - System.out.println("加密计算结果:" + en_data); - System.out.println("加密预期结果:" + CalcRes.SM4ANDMAC_BATCH_RESULT); - } - - - try { - data = new ArrayList<>(); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - data.add(new byte[1024]); - keyEnc = new int[]{0, 1, 0, 1, 1, 1, 1, 0}; - keyMac = new int[]{1, 0, 1, 0, 0, 0, 0, 0}; - - en_data = api.SYD_SM4_EncryptAndMac_BatchData(keyEnc, keyMac, data); - de_data = api.SYD_SM4_CheckMacAndDecrypt_BatchData(keyEnc, keyMac, en_data); - System.out.println("批量数据加密计算MAC,校验MAC解密长度超出测试失败"); - } catch (SydApiException e) { - System.out.println("批量数据加密计算MAC,校验MAC解密长度超出测试成功"); - } - - System.out.println("-------- 批量数据加密计算MAC,校验MAC解密功能测试结束 --------\n"); - } - - public void funcAll() { - this.loggerFunc(); - this.connectFunc(); - this.longConnectFunc(); - - this.sm3ShortFunc(); - - - this.sm4ShortFunc(); - this.sm4BatchFunc(); - - - this.sm4macShortFunc(); - this.sm4macBatchFunc(); - - - this.sm4ShortMacFunc(); - this.sm4BatchMacFunc(); - - - this.sm4LongFunc(); - this.sm4macLongFunc(); - this.sm3LongFunc(); - - } - - public static void funcMenu(Config config, Choise choise, TestAll test) throws Exception{ - - boolean flag = true; - SydApi.SYD_SetLogConfig("./logs", 3); - while (flag) { - System.out.println("请选择测试项:"); - System.out.println("1. 日志测试"); - System.out.println("2. 短链接测试"); - System.out.println("3. 长链接测试"); - - System.out.println("4. SM3短数据"); - - - System.out.println("5. SM4 短数据加解密"); - System.out.println("6. SM4 批量数据加解密"); - System.out.println("7. SM4MAC 短数据"); - System.out.println("8. SM4MAC 批量数据"); - - System.out.println("9. SM4短数据加密后生成MAC,解密验证MAC"); - System.out.println("10. SM4批量数据加密后生成MAC,验证MAC解密"); - - - System.out.println("11. SM4长数据加解密"); - System.out.println("12. SM4长数据MAC"); - - - System.out.println("13. SM3长数据"); - System.out.println("-1.退出"); - - switch (choise.getAsInt()) { - case 1: - test.loggerFunc(); - break; - case 2: - test.connectFunc(); - break; - case 3: - test.longConnectFunc(); - break; - case 4: - test.sm3ShortFunc(); - break; - - case 5: - test.sm4ShortFunc(); - break; - case 6: - test.sm4BatchFunc(); - break; - - case 7: - test.sm4macShortFunc(); - break; - case 8: { - test.sm4macBatchFunc(); - break; - } - case 9: { - test.sm4ShortMacFunc(); - break; - } - case 10: { - test.sm4BatchMacFunc(); - break; - } - case 11: - test.sm4macLongFunc(); - break; - case 12: - test.sm4LongFunc(); - break; - case 13: - test.sm3LongFunc(); - break; - case -1: - flag = false; - break; - } - } - } @@ -1400,9 +835,4 @@ public class TestAll { } - void testComm(){ - String hex = "0436000000000000000E453030383130534B303030303030303030303030303030303030413030303030303030303031303030303234303000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - - } - } diff --git a/说明.txt b/说明.txt index c96dca2..9db3a3c 100644 --- a/说明.txt +++ b/说明.txt @@ -1,15 +1,14 @@ +#Readme + + +## 主要类说明 SydApi - 接口 -SydApiBaseFunction - 主要用于长连接,由 Pool 创建。 -SydApi4Database - 主要用于短连接,继承自 SydApiBaseFunction - - - - -sydapi4j-1.0.20220906.jar -性能改动查看 - +SydApiBaseFunction - 通用功能。 +SydApiLongLinkWrap - 用于长连接,包装自 SydApiBaseFunction。 +SydApi4Database - 主要用于短连接,继承自 SydApiBaseFunction。 +## 发布包格式 - doc 文档 - sample 示例代码 - jar @@ -22,7 +21,13 @@ sydapi4j-1.0.20220906.jar - sydapi4j-1.0.20220805.jar 加密机通信包 +## 二期需求 +加密带MAC 复合接口长连接四个都是封装自 C。 + + + +## 测试环境 linux测试服务器 电口:172.1.10.245 光口:192.168.100.245