From 970c631e52eeb7b4991dbf3c9d106ed13ca8ef75 Mon Sep 17 00:00:00 2001 From: cheney Date: Thu, 21 May 2026 15:56:35 +0800 Subject: [PATCH] init --- .gitignore | 26 + cbc.cms | 1 + icbc.cms | 1 + pom.xml | 119 +++++ .../cisd/device/tool/CMSSignatureUtil.java | 130 +++++ .../com/sunyard/cisd/device/tool/CMSUtil.java | 162 ++++++ .../cisd/device/tool/DeviceFingerprint.java | 83 +++ .../device/tool/DeviceFingerprintService.java | 254 +++++++++ .../com/sunyard/cisd/device/tool/Main.java | 504 ++++++++++++++++++ .../device/tool/SM2SignedEnvelopeUtil.java | 258 +++++++++ .../com/sunyard/cisd/device/tool/SM2Util.java | 134 +++++ .../com/sunyard/cisd/device/tool/SM3Util.java | 162 ++++++ .../cisd/device/tool/SdfDeviceInfo.java | 33 ++ .../cisd/device/tool/SdfNativeLibrary.java | 13 + src/main/resources/CFCA_TEST_CS_SM2_CA.cer | Bin 0 -> 536 bytes .../resources/fingerprint_sm2_private_key.pem | 6 + .../resources/fingerprint_sm2_public_key.pem | 4 + src/main/resources/mac_sm2_private_key.pem | 6 + src/main/resources/mac_sm2_public_key.pem | 4 + .../com/sunyard/cisd/CMSSignatureTest.java | 51 ++ .../java/com/sunyard/cisd/SM2UtilTest.java | 103 ++++ 21 files changed, 2054 insertions(+) create mode 100644 .gitignore create mode 100644 cbc.cms create mode 100644 icbc.cms create mode 100644 pom.xml create mode 100644 src/main/java/com/sunyard/cisd/device/tool/CMSSignatureUtil.java create mode 100644 src/main/java/com/sunyard/cisd/device/tool/CMSUtil.java create mode 100644 src/main/java/com/sunyard/cisd/device/tool/DeviceFingerprint.java create mode 100644 src/main/java/com/sunyard/cisd/device/tool/DeviceFingerprintService.java create mode 100644 src/main/java/com/sunyard/cisd/device/tool/Main.java create mode 100644 src/main/java/com/sunyard/cisd/device/tool/SM2SignedEnvelopeUtil.java create mode 100644 src/main/java/com/sunyard/cisd/device/tool/SM2Util.java create mode 100644 src/main/java/com/sunyard/cisd/device/tool/SM3Util.java create mode 100644 src/main/java/com/sunyard/cisd/device/tool/SdfDeviceInfo.java create mode 100644 src/main/java/com/sunyard/cisd/device/tool/SdfNativeLibrary.java create mode 100644 src/main/resources/CFCA_TEST_CS_SM2_CA.cer create mode 100644 src/main/resources/fingerprint_sm2_private_key.pem create mode 100644 src/main/resources/fingerprint_sm2_public_key.pem create mode 100644 src/main/resources/mac_sm2_private_key.pem create mode 100644 src/main/resources/mac_sm2_public_key.pem create mode 100644 src/test/java/com/sunyard/cisd/CMSSignatureTest.java create mode 100644 src/test/java/com/sunyard/cisd/SM2UtilTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b073d14 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +/target/classes/com/sunyard/cisd/device/tool/CMSSignatureUtil.class +/target/classes/com/sunyard/cisd/device/tool/CMSUtil.class +/target/classes/com/sunyard/cisd/device/tool/DeviceFingerprint.class +/target/classes/com/sunyard/cisd/device/tool/DeviceFingerprintService.class +/target/classes/com/sunyard/cisd/device/tool/Main.class +/target/classes/com/sunyard/cisd/device/tool/SdfDeviceInfo.class +/target/classes/com/sunyard/cisd/device/tool/SdfNativeLibrary.class +/target/classes/com/sunyard/cisd/device/tool/SM2SignedEnvelopeUtil$DecryptedResult.class +/target/classes/com/sunyard/cisd/device/tool/SM2SignedEnvelopeUtil$SM2KeyPair.class +/target/classes/com/sunyard/cisd/device/tool/SM2SignedEnvelopeUtil.class +/target/classes/com/sunyard/cisd/device/tool/SM2Util.class +/target/classes/com/sunyard/cisd/device/tool/SM3Util.class +/target/classes/CFCA_TEST_CS_SM2_CA.cer +/target/classes/fingerprint_sm2_private_key.pem +/target/classes/fingerprint_sm2_public_key.pem +/target/classes/mac_sm2_private_key.pem +/target/classes/mac_sm2_public_key.pem +/target/maven-archiver/pom.properties +/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst +/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst +/target/test-classes/com/sunyard/cisd/CMSSignatureTest.class +/target/test-classes/com/sunyard/cisd/SM2UtilTest.class +/target/sectool-1.0.3.jar +/target/sectool-1.0.3-jar-with-dependencies.jar diff --git a/cbc.cms b/cbc.cms new file mode 100644 index 0000000..39de057 --- /dev/null +++ b/cbc.cms @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/icbc.cms b/icbc.cms new file mode 100644 index 0000000..390d60f --- /dev/null +++ b/icbc.cms @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a8ae509 --- /dev/null +++ b/pom.xml @@ -0,0 +1,119 @@ + + + 4.0.0 + + com.sunyard.cisd + sectool + 1.0.3 + sectool + 设备指纹信息获取工具 + + + 1.8 + 1.8 + 1.8 + UTF-8 + + + + + + nexus-own + repo-snapshot + ${SnapshotRepository} + + + nexus-own + repo-release + ${ReleaseRepository} + + + + + + net.java.dev.jna + jna + 5.9.0 + + + net.java.dev.jna + jna-platform + 5.9.0 + + + org.projectlombok + lombok + 1.18.42 + provided + + + org.bouncycastle + bcprov-jdk15on + 1.70 + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + junit + junit + 4.13.2 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + true + com.sunyard.cisd.device.tool.Main + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + jar-with-dependencies + + + + com.sunyard.cisd.device.tool.Main + + + + + + make-assembly + package + + single + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/sunyard/cisd/device/tool/CMSSignatureUtil.java b/src/main/java/com/sunyard/cisd/device/tool/CMSSignatureUtil.java new file mode 100644 index 0000000..14e0666 --- /dev/null +++ b/src/main/java/com/sunyard/cisd/device/tool/CMSSignatureUtil.java @@ -0,0 +1,130 @@ +package com.sunyard.cisd.device.tool; + +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cms.*; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Store; + +import java.security.Security; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.Collection; + +public class CMSSignatureUtil { + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + public static boolean verifyCMSSignature(String base64CmsData) throws Exception { + byte[] cmsData = Base64.getDecoder().decode(base64CmsData); + return verifyCMSSignature(cmsData); + } + + public static boolean verifyCMSSignature(byte[] cmsData) throws Exception { + CMSSignedData cmsSignedData = new CMSSignedData(cmsData); + + Store certStore = cmsSignedData.getCertificates(); + SignerInformationStore signerStore = cmsSignedData.getSignerInfos(); + + for (Object signerObj : signerStore.getSigners()) { + SignerInformation signer = (SignerInformation) signerObj; + Collection certCollection = certStore.getMatches(signer.getSID()); + + if (certCollection.isEmpty()) { + throw new CMSException("未找到签名证书"); + } + + X509CertificateHolder certHolder = (X509CertificateHolder) certCollection.iterator().next(); + X509Certificate cert = convertCertificate(certHolder); + + try { + if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) { + return true; + } + } catch (Exception e) { + System.err.println("签名验证失败: " + e.getMessage()); + } + } + + return false; + } + + public static boolean verifyCMSSignatureWithCertificate(String base64CmsData, X509Certificate publicCert) throws Exception { + byte[] cmsData = Base64.getDecoder().decode(base64CmsData); + return verifyCMSSignatureWithCertificate(cmsData, publicCert); + } + + public static boolean verifyCMSSignatureWithCertificate(byte[] cmsData, X509Certificate publicCert) throws Exception { + CMSSignedData cmsSignedData = new CMSSignedData(cmsData); + SignerInformationStore signerStore = cmsSignedData.getSignerInfos(); + + for (Object signerObj : signerStore.getSigners()) { + SignerInformation signer = (SignerInformation) signerObj; + try { + if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(publicCert))) { + return true; + } + } catch (Exception e) { + System.err.println("使用指定证书验证失败: " + e.getMessage()); + } + } + + return false; + } + + public static byte[] extractContent(String base64CmsData) throws Exception { + byte[] cmsData = Base64.getDecoder().decode(base64CmsData); + return extractContent(cmsData); + } + + public static byte[] extractContent(byte[] cmsData) throws Exception { + CMSSignedData cmsSignedData = new CMSSignedData(cmsData); + return (byte[]) cmsSignedData.getSignedContent().getContent(); + } + + public static X509Certificate loadCertificateFromBytes(byte[] certBytes) throws Exception { + CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC"); + return (X509Certificate) cf.generateCertificate(new java.io.ByteArrayInputStream(certBytes)); + } + + public static X509Certificate loadCertificateFromBase64(String base64Cert) throws Exception { + byte[] certBytes = Base64.getDecoder().decode(base64Cert); + return loadCertificateFromBytes(certBytes); + } + + private static X509Certificate convertCertificate(X509CertificateHolder certHolder) throws Exception { + CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC"); + return (X509Certificate) cf.generateCertificate(new java.io.ByteArrayInputStream(certHolder.getEncoded())); + } + + public static void main(String[] args) { + if (args.length < 1) { + System.out.println("用法: java CMSSignatureUtil [base64_certificate]"); + return; + } + + try { + String cmsData = args[0]; + + if (args.length >= 2) { + String certBase64 = args[1]; + X509Certificate cert = loadCertificateFromBase64(certBase64); + boolean result = verifyCMSSignatureWithCertificate(cmsData, cert); + System.out.println("使用指定证书验签结果: " + result); + } else { + boolean result = verifyCMSSignature(cmsData); + System.out.println("使用CMS内置证书验签结果: " + result); + } + + byte[] content = extractContent(cmsData); + System.out.println("提取的内容长度: " + content.length + " bytes"); + + } catch (Exception e) { + System.err.println("验签失败: " + e.getMessage()); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/sunyard/cisd/device/tool/CMSUtil.java b/src/main/java/com/sunyard/cisd/device/tool/CMSUtil.java new file mode 100644 index 0000000..d065f84 --- /dev/null +++ b/src/main/java/com/sunyard/cisd/device/tool/CMSUtil.java @@ -0,0 +1,162 @@ +package com.sunyard.cisd.device.tool; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; +import org.bouncycastle.asn1.cms.ContentInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cms.*; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Store; + +import java.security.Security; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.Collection; + +public class CMSUtil { + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + public static String detectCMSType(String base64CmsData) throws Exception { + byte[] cmsData = Base64.getDecoder().decode(base64CmsData); + return detectCMSType(cmsData); + } + + public static String detectCMSType(byte[] cmsData) throws Exception { + try (ASN1InputStream asn1InputStream = new ASN1InputStream(cmsData)) { + ContentInfo contentInfo = ContentInfo.getInstance(asn1InputStream.readObject()); + String typeOid = contentInfo.getContentType().getId(); + + if (typeOid.equals(CMSObjectIdentifiers.signedData.getId())) { + return "SignedData"; + } else if (typeOid.equals(CMSObjectIdentifiers.envelopedData.getId())) { + return "EnvelopedData"; + } else if (typeOid.equals(CMSObjectIdentifiers.signedAndEnvelopedData.getId())) { + return "SignedAndEnvelopedData"; + } else if (typeOid.equals(CMSObjectIdentifiers.digestedData.getId())) { + return "DigestedData"; + } else if (typeOid.equals(CMSObjectIdentifiers.encryptedData.getId())) { + return "EncryptedData"; + } else { + return "Unknown: " + typeOid; + } + } + } + + public static boolean verifySignedData(String base64CmsData) throws Exception { + byte[] cmsData = Base64.getDecoder().decode(base64CmsData); + return verifySignedData(cmsData); + } + + public static boolean verifySignedData(byte[] cmsData) throws Exception { + CMSSignedData cmsSignedData = new CMSSignedData(cmsData); + return verifySignedData(cmsSignedData); + } + + public static boolean verifySignedData(CMSSignedData cmsSignedData) throws Exception { + Store certStore = cmsSignedData.getCertificates(); + SignerInformationStore signerStore = cmsSignedData.getSignerInfos(); + + for (Object signerObj : signerStore.getSigners()) { + SignerInformation signer = (SignerInformation) signerObj; + Collection certCollection = certStore.getMatches(signer.getSID()); + + if (certCollection.isEmpty()) { + System.err.println("未找到签名证书"); + continue; + } + + X509CertificateHolder certHolder = (X509CertificateHolder) certCollection.iterator().next(); + X509Certificate cert = convertCertificate(certHolder); + + try { + if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) { + return true; + } + } catch (Exception e) { + System.err.println("签名验证失败: " + e.getMessage()); + } + } + + return false; + } + + public static boolean verifySignedDataWithCertificate(String base64CmsData, X509Certificate publicCert) throws Exception { + byte[] cmsData = Base64.getDecoder().decode(base64CmsData); + CMSSignedData cmsSignedData = new CMSSignedData(cmsData); + return verifySignedDataWithCertificate(cmsSignedData, publicCert); + } + + public static boolean verifySignedDataWithCertificate(CMSSignedData cmsSignedData, X509Certificate publicCert) throws Exception { + SignerInformationStore signerStore = cmsSignedData.getSignerInfos(); + + for (Object signerObj : signerStore.getSigners()) { + SignerInformation signer = (SignerInformation) signerObj; + try { + if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(publicCert))) { + return true; + } + } catch (Exception e) { + System.err.println("使用指定证书验证失败: " + e.getMessage()); + } + } + + return false; + } + + public static byte[] extractSignedContent(String base64CmsData) throws Exception { + byte[] cmsData = Base64.getDecoder().decode(base64CmsData); + CMSSignedData cmsSignedData = new CMSSignedData(cmsData); + return (byte[]) cmsSignedData.getSignedContent().getContent(); + } + + public static X509Certificate loadCertificateFromBytes(byte[] certBytes) throws Exception { + CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC"); + return (X509Certificate) cf.generateCertificate(new java.io.ByteArrayInputStream(certBytes)); + } + + public static X509Certificate loadCertificateFromBase64(String base64Cert) throws Exception { + byte[] certBytes = Base64.getDecoder().decode(base64Cert); + return loadCertificateFromBytes(certBytes); + } + + private static X509Certificate convertCertificate(X509CertificateHolder certHolder) throws Exception { + CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC"); + return (X509Certificate) cf.generateCertificate(new java.io.ByteArrayInputStream(certHolder.getEncoded())); + } + + public static void main(String[] args) { + if (args.length < 1) { + System.out.println("用法: java CMSUtil "); + return; + } + + try { + String cmsData = args[0]; + + String type = detectCMSType(cmsData); + System.out.println("CMS类型: " + type); + System.out.println(); + + boolean verified = false; + + if (type.equals("SignedData")) { + verified = verifySignedData(cmsData); + System.out.println("SignedData验签结果: " + verified); + } else if (type.equals("EnvelopedData")) { + System.out.println("这是一个数字信封(EnvelopedData),需要私钥解密后才能验证签名"); + } else if (type.equals("SignedAndEnvelopedData")) { + System.out.println("这是一个SignedAndEnvelopedData(同时签名和加密)"); + System.out.println("需要使用对应的私钥解密后才能验证签名"); + } + + } catch (Exception e) { + System.err.println("处理失败: " + e.getMessage()); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/sunyard/cisd/device/tool/DeviceFingerprint.java b/src/main/java/com/sunyard/cisd/device/tool/DeviceFingerprint.java new file mode 100644 index 0000000..9917afc --- /dev/null +++ b/src/main/java/com/sunyard/cisd/device/tool/DeviceFingerprint.java @@ -0,0 +1,83 @@ +package com.sunyard.cisd.device.tool; + +public class DeviceFingerprint { + private String motherboardSerialNumber; + private String deviceSerialNumber; + private String cpuModel; + private String cpuSerialNumber; + private String memorySize; + private String firstNetworkMacAddress; + private String hardDiskSerialNumber; + + public DeviceFingerprint() { + } + + public String getMotherboardSerialNumber() { + return motherboardSerialNumber; + } + + public void setMotherboardSerialNumber(String motherboardSerialNumber) { + this.motherboardSerialNumber = motherboardSerialNumber; + } + + public String getDeviceSerialNumber() { + return deviceSerialNumber; + } + + public void setDeviceSerialNumber(String deviceSerialNumber) { + this.deviceSerialNumber = deviceSerialNumber; + } + + public String getCpuModel() { + return cpuModel; + } + + public void setCpuModel(String cpuModel) { + this.cpuModel = cpuModel; + } + + public String getCpuSerialNumber() { + return cpuSerialNumber; + } + + public void setCpuSerialNumber(String cpuSerialNumber) { + this.cpuSerialNumber = cpuSerialNumber; + } + + public String getMemorySize() { + return memorySize; + } + + public void setMemorySize(String memorySize) { + this.memorySize = memorySize; + } + + public String getFirstNetworkMacAddress() { + return firstNetworkMacAddress; + } + + public void setFirstNetworkMacAddress(String firstNetworkMacAddress) { + this.firstNetworkMacAddress = firstNetworkMacAddress; + } + + public String getHardDiskSerialNumber() { + return hardDiskSerialNumber; + } + + public void setHardDiskSerialNumber(String hardDiskSerialNumber) { + this.hardDiskSerialNumber = hardDiskSerialNumber; + } + + @Override + public String toString() { + return "DeviceFingerprint{" + + "motherboardSerialNumber='" + motherboardSerialNumber + '\'' + + ", deviceSerialNumber='" + deviceSerialNumber + '\'' + + ", cpuModel='" + cpuModel + '\'' + + ", cpuSerialNumber='" + cpuSerialNumber + '\'' + + ", memorySize='" + memorySize + '\'' + + ", firstNetworkMacAddress='" + firstNetworkMacAddress + '\'' + + ", hardDiskSerialNumber='" + hardDiskSerialNumber + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/com/sunyard/cisd/device/tool/DeviceFingerprintService.java b/src/main/java/com/sunyard/cisd/device/tool/DeviceFingerprintService.java new file mode 100644 index 0000000..3d98bcc --- /dev/null +++ b/src/main/java/com/sunyard/cisd/device/tool/DeviceFingerprintService.java @@ -0,0 +1,254 @@ +package com.sunyard.cisd.device.tool; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +public class DeviceFingerprintService { + private static final String SDF_LIBRARY_PATH = "/home/tms/libs/libsdf_syd1408-x64.so.1.0.0"; + private static SdfNativeLibrary sdfLibrary = null; + private static boolean libraryLoaded = false; + private static String loadError = null; + + static { + System.out.println("[DEBUG] Attempting to load SDF library from: " + SDF_LIBRARY_PATH); + + File libFile = new File(SDF_LIBRARY_PATH); + if (libFile.exists()) { + System.out.println("[DEBUG] Library file exists: " + SDF_LIBRARY_PATH); + System.out.println("[DEBUG] File size: " + libFile.length() + " bytes"); + } else { + System.out.println("[ERROR] Library file not found: " + SDF_LIBRARY_PATH); + loadError = "Library file not found"; + } + + try { + sdfLibrary = Native.load(SDF_LIBRARY_PATH, SdfNativeLibrary.class); + libraryLoaded = true; + System.out.println("[DEBUG] SDF library loaded successfully"); + } catch (UnsatisfiedLinkError e) { + loadError = "UnsatisfiedLinkError: " + e.getMessage(); + System.err.println("[ERROR] Failed to load SDF library (UnsatisfiedLinkError): " + e.getMessage()); + System.err.println("[ERROR] Cause: " + (e.getCause() != null ? e.getCause().getMessage() : "null")); + } catch (Exception e) { + loadError = "Exception: " + e.getMessage(); + System.err.println("[ERROR] Failed to load SDF library (Exception): " + e.getClass().getName() + " - " + e.getMessage()); + e.printStackTrace(); + } + } + + public DeviceFingerprint getDeviceFingerprint() { + DeviceFingerprint fingerprint = new DeviceFingerprint(); + + String cpuSerialNumber = getCpuSerialNumber(); + fingerprint.setCpuSerialNumber(cpuSerialNumber); + fingerprint.setMotherboardSerialNumber(cpuSerialNumber); + + fingerprint.setCpuModel(getCpuModel()); + fingerprint.setMemorySize(getMemorySize()); + fingerprint.setFirstNetworkMacAddress(getFirstNetworkMacAddress()); + fingerprint.setHardDiskSerialNumber(getHardDiskSerialNumber()); + fingerprint.setDeviceSerialNumber(getDeviceSerialNumber()); + + return fingerprint; + } + + private String getCpuModel() { + return executeCommand("sudo dmidecode -t processor | grep \"Version\" | awk -F\\ '{print $2$3$4}'"); + } + + private String getCpuSerialNumber() { + return executeCommand("sudo dmidecode -t processor | grep \"Serial Number\" | awk -F\\ '{print $3}'"); + } + + private String getHardDiskSerialNumber() { + return executeCommand("lsblk -o SERIAL /dev/sda | awk 'NR==2{print $1}'"); + } + + private String getMemorySize() { + String result = executeCommand("free -h | grep Mem | awk '{print $2}'"); + return result != null && !result.isEmpty() ? result : "Unknown"; + } + + private String getFirstNetworkMacAddress() { + String result = executeCommand("ip link show | awk '/ether/ {print $2; exit}'"); + return result != null && !result.isEmpty() ? result : "Unknown"; + } + + private static String zeroTerminatedString(byte[] value) { + int len = 0; + while (len < value.length && value[len] != 0) { + len++; + } + return new String(value, 0, len, StandardCharsets.UTF_8).trim(); + } + + private String getDeviceSerialNumber() { + System.out.println("[DEBUG] getDeviceSerialNumber() called"); + System.out.println("[DEBUG] Library loaded: " + libraryLoaded + ", sdfLibrary: " + (sdfLibrary != null ? "not null" : "null")); + + if (!libraryLoaded || sdfLibrary == null) { + System.err.println("[ERROR] SDF library not loaded. Cannot get device serial number."); + System.err.println("[ERROR] Load error: " + loadError); + return "Unknown"; + } + + Pointer deviceHandle = null; + Pointer sessionHandle = null; + + try { + System.out.println("[DEBUG] Calling SDF_OpenDevice..."); + PointerByReference phDeviceHandle = new PointerByReference(); + int ret = sdfLibrary.SDF_OpenDevice(phDeviceHandle); + System.out.println("[DEBUG] SDF_OpenDevice returned: " + ret); + + if (ret != 0) { + System.err.println("[ERROR] SDF_OpenDevice failed with error code: " + ret); + System.err.println("[ERROR] Error code meaning: " + getErrorMessage(ret)); + return "Unknown"; + } + deviceHandle = phDeviceHandle.getValue(); + System.out.println("[DEBUG] Device handle obtained: " + deviceHandle); + + System.out.println("[DEBUG] Calling SDF_OpenSession..."); + PointerByReference phSessionHandle = new PointerByReference(); + ret = sdfLibrary.SDF_OpenSession(deviceHandle, phSessionHandle); + System.out.println("[DEBUG] SDF_OpenSession returned: " + ret); + + if (ret != 0) { + System.err.println("[ERROR] SDF_OpenSession failed with error code: " + ret); + System.err.println("[ERROR] Error code meaning: " + getErrorMessage(ret)); + return "Unknown"; + } + sessionHandle = phSessionHandle.getValue(); + System.out.println("[DEBUG] Session handle obtained: " + sessionHandle); + + System.out.println("[DEBUG] Calling SDF_GetDeviceInfo..."); + SdfDeviceInfo info = new SdfDeviceInfo(); + ret = sdfLibrary.SDF_GetDeviceInfo(sessionHandle, info); + System.out.println("[DEBUG] SDF_GetDeviceInfo returned: " + ret); + + if (ret != 0) { + System.err.println("[ERROR] SDF_GetDeviceInfo failed with error code: " + ret); + System.err.println("[ERROR] Error code meaning: " + getErrorMessage(ret)); + return "Unknown"; + } + + String issuerName = zeroTerminatedString(info.getIssuerName()); + String deviceName = zeroTerminatedString(info.getDeviceName()); + String deviceSerial = zeroTerminatedString(info.getDeviceSerial()); + + System.out.println("[DEBUG] Device info retrieved:"); + System.out.println("[DEBUG] IssuerName: '" + issuerName + "'"); + System.out.println("[DEBUG] DeviceName: '" + deviceName + "'"); + System.out.println("[DEBUG] DeviceSerial: '" + deviceSerial + "'"); + + if (deviceSerial == null || deviceSerial.isEmpty()) { + System.err.println("[WARNING] Device serial number is empty"); + return "Unknown"; + } + + return deviceSerial; + + } catch (UnsatisfiedLinkError e) { + System.err.println("[ERROR] UnsatisfiedLinkError when calling SDF API: " + e.getMessage()); + e.printStackTrace(); + return "Unknown"; + } catch (Exception e) { + System.err.println("[ERROR] Exception when calling SDF API: " + e.getClass().getName() + " - " + e.getMessage()); + e.printStackTrace(); + return "Unknown"; + } finally { + if (sessionHandle != null) { + try { + System.out.println("[DEBUG] Closing session handle: " + sessionHandle); + sdfLibrary.SDF_CloseSession(sessionHandle); + } catch (Exception e) { + System.err.println("[ERROR] Error closing session: " + e.getMessage()); + } + } + if (deviceHandle != null) { + try { + System.out.println("[DEBUG] Closing device handle: " + deviceHandle); + sdfLibrary.SDF_CloseDevice(deviceHandle); + } catch (Exception e) { + System.err.println("[ERROR] Error closing device: " + e.getMessage()); + } + } + } + } + + private String getErrorMessage(int errorCode) { + switch (errorCode) { + case 0: + return "Success"; + case -1: + return "General error"; + case -2: + return "Invalid parameter"; + case -3: + return "Memory allocation failed"; + case -4: + return "Device not found"; + case -5: + return "Device not opened"; + case -6: + return "Session not opened"; + case -7: + return "Operation not supported"; + case -8: + return "Key not found"; + case -9: + return "Key access denied"; + case -10: + return "Invalid key"; + case -11: + return "Invalid signature"; + case -12: + return "Invalid certificate"; + case -13: + return "Buffer too small"; + case -14: + return "Timeout"; + case -15: + return "Hardware error"; + default: + return "Unknown error code: " + errorCode; + } + } + + private String executeCommand(String command) { + try { + Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", command}); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); + + String line; + StringBuilder output = new StringBuilder(); + while ((line = reader.readLine()) != null) { + output.append(line); + } + + String errorOutput = ""; + while ((line = errorReader.readLine()) != null) { + errorOutput += line; + } + + int exitCode = process.waitFor(); + if (exitCode == 0) { + String result = output.toString().trim(); + return result.isEmpty() ? "Unknown" : result; + } else { + System.err.println("[ERROR] Command '" + command + "' failed with exit code " + exitCode + ": " + errorOutput); + return "Unknown"; + } + } catch (Exception e) { + System.err.println("[ERROR] Error executing command '" + command + "': " + e.getMessage()); + return "Unknown"; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/sunyard/cisd/device/tool/Main.java b/src/main/java/com/sunyard/cisd/device/tool/Main.java new file mode 100644 index 0000000..3d355cd --- /dev/null +++ b/src/main/java/com/sunyard/cisd/device/tool/Main.java @@ -0,0 +1,504 @@ +package com.sunyard.cisd.device.tool; + +import java.io.File; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.PublicKey; +import java.util.Base64; +import java.util.Map; + +public class Main { + + private static final String DEVICE_DIR = "/home/tms/device"; + public static final String SIGN_FILE = DEVICE_DIR + "/dev.sign"; + private static final String SIGN_SM3_FILE = DEVICE_DIR + "/dev.sign.sm3"; + private static final String PUBLIC_KEY_FILE = DEVICE_DIR + "/dev.fingerprint.pub"; + private static final String PUBLIC_KEY_SM3_FILE = DEVICE_DIR + "/dev.fingerprint.pub.sm3"; + public static final String FINGERPRINT_DATA_FILE = DEVICE_DIR + "/dev.fingerprint.data"; + private static final String FINGERPRINT_PRIVATE_KEY_PATH = "/fingerprint_sm2_private_key.pem"; + public static final String FINGERPRINT_PUBLIC_KEY_PATH = "/fingerprint_sm2_public_key.pem"; + private static final String MAC_PRIVATE_KEY_PATH = "/mac_sm2_private_key.pem"; + public static final String MAC_PUBLIC_KEY_PATH = "/mac_sm2_public_key.pem"; + private static final String ROOT_CERT_FILE = "/CFCA_TEST_CS_SM2_CA.cer"; + private static final String ROOT_CERT_OUTPUT_FILE = DEVICE_DIR + "/CFCA_TEST_CS_SM2_CA.cer"; + private static final String ROOT_CERT_SIGN_FILE = DEVICE_DIR + "/rootcert.sign"; + private static final String MAC_PUBLIC_KEY_OUTPUT_FILE = DEVICE_DIR + "/dev.mac.pub"; + private static final String MAC_PUBLIC_KEY_SM3_FILE = DEVICE_DIR + "/dev.mac.pub.sm3"; + public static final String MAC_LIST_FILE = "/home/tms/mac.list"; + public static final String MAC_SIGN_FILE = DEVICE_DIR + "/dev.mac.sign"; + + public static final String CMEP_MAC_LIST_FILE = "/home/cmep4i/cmep.mac.list"; + public static final String CMEP_MAC_SIGN_FILE = "/home/cmep4i/cmep.mac.sign"; + + public static final String[] MAC_TARGETS = { + "/home/tms/tms-framework.jar", + "/home/tms/config", + "/home/tms/libs", + "/home/tms/web", + "/home/tms/bin", + "/home/tms/scripts/tms.sh" + }; + + public static final String[] CMEP_TARGETS = { + "/home/cmep4i/cmsp/CMEP-CMSP.jar", + "/home/cmep4i/cmtp/CMEP-CMTP.jar" + }; + + public static void main(String[] args) { + if (args.length == 0) { + printHelp(); + return; + } + + String command = args[0]; + + try { + switch (command) { + case "help": + printHelp(); + break; + case "info": + showDeviceInfo(); + break; + case "fingerprint-gen": + generateFingerprint(); + break; + + case "fingerprint-vertify": + verifyFingerprint(); + break; + case "tms-mac-gen": + generateMAC(); + break; + case "tms-mac-vertify": + verifyMAC(); + break; + case "cmep-mac-gen": + generateCMEPMAC(); + break; + case "cmep-mac-vertify": + verifyCMEPMAC(); + break; + case "rootcert-mac-gen": + generateRootCertMAC(); + break; + case "rootcert-mac-vertify": + verifyRootCertMAC(); + break; + default: + System.err.println("未知命令: " + command); + printHelp(); + break; + } + } catch (Exception e) { + System.err.println("处理过程中发生错误: " + e.getMessage()); + e.printStackTrace(); + } + } + + private static void printHelp() { + System.out.println("设备指纹工具 - 使用说明"); + System.out.println(); + System.out.println("可用命令:"); + System.out.println(" help - 显示帮助信息"); + System.out.println(" info - 获取并显示设备信息"); + System.out.println(" fingerprint-gen - 获取设备信息并生成设备指纹输出到文件"); + System.out.println(" fingerprint-vertify - 获取设备信息并验证设备指纹"); + System.out.println(" tms-mac-gen - 生成TMS系统文件MAC校验值并签名"); + System.out.println(" tms-mac-vertify - 验证TMS系统文件MAC校验值"); + System.out.println(" cmep-mac-gen - 生成CMEP文件MAC校验值并签名"); + System.out.println(" cmep-mac-vertify - 验证CMEP文件MAC校验值"); + System.out.println(" rootcert-mac-gen - 生成根证书MAC签名"); + System.out.println(" rootcert-mac-vertify - 验证根证书MAC签名"); + System.out.println(); + } + + private static void showDeviceInfo() throws Exception { + DeviceFingerprintService service = new DeviceFingerprintService(); + DeviceFingerprint fingerprint = service.getDeviceFingerprint(); + + System.out.println("=== 设备信息 ==="); + System.out.println("主板序列号: " + fingerprint.getMotherboardSerialNumber()); + System.out.println("设备序列号: " + fingerprint.getDeviceSerialNumber()); + System.out.println("CPU型号: " + fingerprint.getCpuModel()); + System.out.println("CPU序列号: " + fingerprint.getCpuSerialNumber()); + System.out.println("内存大小: " + fingerprint.getMemorySize()); + System.out.println("首块网卡MAC地址: " + fingerprint.getFirstNetworkMacAddress()); + System.out.println("硬盘序列号: " + fingerprint.getHardDiskSerialNumber()); + } + + public static String buildPlainText(DeviceFingerprint fingerprint) { + return fingerprint.getMotherboardSerialNumber() + + fingerprint.getDeviceSerialNumber() + + fingerprint.getCpuModel() + + fingerprint.getCpuSerialNumber() + + fingerprint.getMemorySize() + + fingerprint.getFirstNetworkMacAddress() + + fingerprint.getHardDiskSerialNumber(); + } + + private static void generateFingerprint() throws Exception { + DeviceFingerprintService service = new DeviceFingerprintService(); + DeviceFingerprint fingerprint = service.getDeviceFingerprint(); + + System.out.println("=== 设备指纹信息 ==="); + System.out.println("主板序列号: " + fingerprint.getMotherboardSerialNumber()); + System.out.println("设备序列号: " + fingerprint.getDeviceSerialNumber()); + System.out.println("CPU型号: " + fingerprint.getCpuModel()); + System.out.println("CPU序列号: " + fingerprint.getCpuSerialNumber()); + System.out.println("内存大小: " + fingerprint.getMemorySize()); + System.out.println("首块网卡MAC地址: " + fingerprint.getFirstNetworkMacAddress()); + System.out.println("硬盘序列号: " + fingerprint.getHardDiskSerialNumber()); + + String plainText = buildPlainText(fingerprint); + System.out.println("\n拼接后的原文: " + plainText); + + byte[] signature = SM2Util.sign(plainText.getBytes(StandardCharsets.UTF_8), FINGERPRINT_PRIVATE_KEY_PATH); + System.out.println("签名结果(Base64): " + Base64.getEncoder().encodeToString(signature)); + + PublicKey publicKey = SM2Util.loadPublicKey(FINGERPRINT_PUBLIC_KEY_PATH); + String publicKeyPEM = SM2Util.publicKeyToPEM(publicKey); + + SM3Util.writeFile(SIGN_FILE, signature); + System.out.println("签名文件已保存: " + SIGN_FILE); + + String signSm3 = SM3Util.sm3DigestHex(signature); + SM3Util.writeFile(SIGN_SM3_FILE, signSm3); + System.out.println("签名SM3校验值已保存: " + SIGN_SM3_FILE + " (" + signSm3 + ")"); + + SM3Util.writeFile(PUBLIC_KEY_FILE, publicKeyPEM); + System.out.println("公钥文件已保存: " + PUBLIC_KEY_FILE); + + String publicKeySm3 = SM3Util.sm3DigestHex(publicKeyPEM.getBytes(StandardCharsets.UTF_8)); + SM3Util.writeFile(PUBLIC_KEY_SM3_FILE, publicKeySm3); + System.out.println("公钥SM3校验值已保存: " + PUBLIC_KEY_SM3_FILE + " (" + publicKeySm3 + ")"); + + SM3Util.writeFile(FINGERPRINT_DATA_FILE, plainText); + System.out.println("设备指纹数据已保存: " + FINGERPRINT_DATA_FILE); + + boolean verified = SM2Util.verify(plainText.getBytes(StandardCharsets.UTF_8), signature, publicKey); + System.out.println("\n签名验证结果: " + verified); + } + + private static void verifyFingerprint() throws Exception { + DeviceFingerprintService service = new DeviceFingerprintService(); + DeviceFingerprint fingerprint = service.getDeviceFingerprint(); + + System.out.println("=== 当前设备信息 ==="); + System.out.println("主板序列号: " + fingerprint.getMotherboardSerialNumber()); + System.out.println("设备序列号: " + fingerprint.getDeviceSerialNumber()); + System.out.println("CPU型号: " + fingerprint.getCpuModel()); + System.out.println("CPU序列号: " + fingerprint.getCpuSerialNumber()); + System.out.println("内存大小: " + fingerprint.getMemorySize()); + System.out.println("首块网卡MAC地址: " + fingerprint.getFirstNetworkMacAddress()); + System.out.println("硬盘序列号: " + fingerprint.getHardDiskSerialNumber()); + + String plainText = buildPlainText(fingerprint); +// System.out.println("\n当前拼接原文: " + plainText); + + byte[] signature = SM3Util.readFileBytes(SIGN_FILE); + PublicKey publicKey = SM2Util.loadPublicKey(FINGERPRINT_PUBLIC_KEY_PATH); + + + boolean signatureVerified = SM2Util.verify(plainText.getBytes(StandardCharsets.UTF_8), signature, publicKey); + + System.out.println("\n=== 验证结果 ==="); + System.out.println("签名验证: " + signatureVerified); + if( Files.exists( Paths.get(FINGERPRINT_DATA_FILE)) ) { + String savedPlainText = SM3Util.readFile(FINGERPRINT_DATA_FILE).trim(); +// System.out.println("保存的拼接原文: " + savedPlainText); + boolean dataMatch = plainText.equals(savedPlainText); + System.out.println("设备信息与保存的信息匹配: " + dataMatch); + } + +// System.out.println("总体验证: " + (dataMatch && signatureVerified)); + } + + private static void generateMAC() throws Exception { + System.out.println("=== 生成系统文件MAC校验值 ==="); + System.out.println("目标路径:"); + for (String target : MAC_TARGETS) { + System.out.println(" " + target); + } + + Map sm3Map = new java.util.LinkedHashMap<>(); + + for (String target : MAC_TARGETS) { + File file = new File(target); + if (!file.exists()) { + System.out.println("警告: 路径不存在 - " + target); + continue; + } + + if (file.isFile()) { + String sm3Value = SM3Util.sm3FileHex(target); + sm3Map.put(target, sm3Value); + System.out.println("文件: " + target + " -> " + sm3Value); + } else if (file.isDirectory()) { + Map dirSm3Map = SM3Util.calculateDirectorySM3(target); + for (Map.Entry entry : dirSm3Map.entrySet()) { + String absolutePath = target + "/" + entry.getKey(); + sm3Map.put(absolutePath, entry.getValue()); + System.out.println("文件: " + absolutePath + " -> " + entry.getValue()); + } + } + } + + StringBuilder macListContent = new StringBuilder(); + for (Map.Entry entry : sm3Map.entrySet()) { + macListContent.append(entry.getKey()).append("|").append(entry.getValue()).append("\n"); + } + + SM3Util.writeFile(MAC_LIST_FILE, macListContent.toString()); + System.out.println("\nMAC列表文件已保存: " + MAC_LIST_FILE); + + byte[] signature = SM2Util.sign(macListContent.toString().getBytes(StandardCharsets.UTF_8), MAC_PRIVATE_KEY_PATH); + SM3Util.writeFile(MAC_SIGN_FILE, signature); + System.out.println("MAC签名文件已保存: " + MAC_SIGN_FILE); + + PublicKey publicKey = SM2Util.loadPublicKey(MAC_PUBLIC_KEY_PATH); + boolean verified = SM2Util.verify(macListContent.toString().getBytes(StandardCharsets.UTF_8), signature, publicKey); + System.out.println("\n签名验证结果: " + verified); + } + + private static void verifyMAC() throws Exception { + System.out.println("=== 验证系统文件MAC校验值 ==="); + + PublicKey publicKey = SM2Util.loadPublicKey(MAC_PUBLIC_KEY_PATH); + byte[] savedSignature = SM3Util.readFileBytes(MAC_SIGN_FILE); + String savedMacList = SM3Util.readFile(MAC_LIST_FILE); + + boolean macListSignatureValid = SM2Util.verify(savedMacList.getBytes(StandardCharsets.UTF_8), savedSignature, publicKey); + System.out.println("MAC列表签名验证: " + (macListSignatureValid ? "通过" : "失败")); + + if (!macListSignatureValid) { + System.out.println("错误: mac.list 签名验证失败,可能被篡改!"); + System.out.println("\n=== 验证结果 ==="); + System.out.println("完整性验证: false"); + return; + } + + Map savedSm3Map = SM3Util.readSM3File(MAC_LIST_FILE); + + Map currentSm3Map = new java.util.LinkedHashMap<>(); + for (String target : MAC_TARGETS) { + File file = new File(target); + if (!file.exists()) { + continue; + } + + if (file.isFile()) { + String sm3Value = SM3Util.sm3FileHex(target); + currentSm3Map.put(target, sm3Value); + } else if (file.isDirectory()) { + Map dirSm3Map = SM3Util.calculateDirectorySM3(target); + for (Map.Entry entry : dirSm3Map.entrySet()) { + String absolutePath = target + "/" + entry.getKey(); + currentSm3Map.put(absolutePath, entry.getValue()); + } + } + } + + boolean allMatch = true; + + for (Map.Entry entry : savedSm3Map.entrySet()) { + String filePath = entry.getKey(); + String savedHash = entry.getValue(); + String currentHash = currentSm3Map.get(filePath); + + if (currentHash == null) { + System.out.println("文件缺失: " + filePath); + allMatch = false; + } else if (!savedHash.equals(currentHash)) { + System.out.println("文件被修改: " + filePath + " (期望: " + savedHash + ", 实际: " + currentHash + ")"); + allMatch = false; + } + } + + for (String filePath : currentSm3Map.keySet()) { + if (!savedSm3Map.containsKey(filePath)) { + System.out.println("新增文件: " + filePath); + allMatch = false; + } + } + + System.out.println(); + System.out.println("=== 验证结果 ==="); + System.out.println("完整性验证: " + allMatch); + } + + private static void generateRootCertMAC() throws Exception { + System.out.println("=== 生成根证书MAC签名 ==="); + + InputStream certInputStream = Main.class.getResourceAsStream(ROOT_CERT_FILE); + if (certInputStream == null) { + throw new IllegalArgumentException("根证书文件未找到: " + ROOT_CERT_FILE); + } + + byte[] certData = readInputStream(certInputStream); + + SM3Util.writeFile(ROOT_CERT_OUTPUT_FILE, certData); + System.out.println("根证书已输出: " + ROOT_CERT_OUTPUT_FILE); + + byte[] signature = SM2Util.sign(certData, MAC_PRIVATE_KEY_PATH); + SM3Util.writeFile(ROOT_CERT_SIGN_FILE, signature); + System.out.println("签名文件已保存: " + ROOT_CERT_SIGN_FILE); + + PublicKey macPublicKey = SM2Util.loadPublicKey(MAC_PUBLIC_KEY_PATH); + String publicKeyPEM = SM2Util.publicKeyToPEM(macPublicKey); + SM3Util.writeFile(MAC_PUBLIC_KEY_OUTPUT_FILE, publicKeyPEM); + System.out.println("MAC公钥文件已保存: " + MAC_PUBLIC_KEY_OUTPUT_FILE); + + String publicKeySm3 = SM3Util.sm3DigestHex(macPublicKey.getEncoded()); + SM3Util.writeFile(MAC_PUBLIC_KEY_SM3_FILE, publicKeySm3); + System.out.println("MAC公钥SM3校验值已保存: " + MAC_PUBLIC_KEY_SM3_FILE + " (" + publicKeySm3 + ")"); + + boolean verified = SM2Util.verify(certData, signature, macPublicKey); + System.out.println("\n签名验证结果: " + verified); + } + + private static void verifyRootCertMAC() throws Exception { + System.out.println("=== 验证根证书MAC签名 ==="); + + String savedPublicKeySm3 = SM3Util.readFile(MAC_PUBLIC_KEY_SM3_FILE).trim(); + + PublicKey publicKey = SM2Util.loadPublicKey(MAC_PUBLIC_KEY_PATH); + String currentPublicKeySm3 = SM3Util.sm3DigestHex(publicKey.getEncoded()); + boolean publicKeyValid = savedPublicKeySm3.equals(currentPublicKeySm3); + System.out.println("公钥校验值验证: " + publicKeyValid); + + InputStream certInputStream = Main.class.getResourceAsStream(ROOT_CERT_FILE); + if (certInputStream == null) { + throw new IllegalArgumentException("根证书文件未找到: " + ROOT_CERT_FILE); + } + byte[] certData = readInputStream(certInputStream); + + byte[] signature = SM3Util.readFileBytes(ROOT_CERT_SIGN_FILE); + + boolean signatureVerified = SM2Util.verify(certData, signature, publicKey); + System.out.println("签名验证结果: " + signatureVerified); + + System.out.println(); + System.out.println("=== 验证结果 ==="); + System.out.println("总体验证: " + (publicKeyValid && signatureVerified)); + } + + private static byte[] readInputStream(InputStream is) throws Exception { + try { + java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int len; + while ((len = is.read(buffer)) != -1) { + baos.write(buffer, 0, len); + } + return baos.toByteArray(); + } finally { + if (is != null) { + is.close(); + } + } + } + + private static void generateCMEPMAC() throws Exception { + System.out.println("=== 生成CMEP文件MAC校验值 ==="); + System.out.println("目标路径:"); + for (String target : CMEP_TARGETS) { + System.out.println(" " + target); + } + + Map sm3Map = new java.util.LinkedHashMap<>(); + + for (String target : CMEP_TARGETS) { + File file = new File(target); + if (!file.exists()) { + System.out.println("警告: 路径不存在 - " + target); + continue; + } + + if (file.isFile()) { + String sm3Value = SM3Util.sm3FileHex(target); + sm3Map.put(target, sm3Value); + System.out.println("文件: " + target + " -> " + sm3Value); + } + } + + StringBuilder macListContent = new StringBuilder(); + for (Map.Entry entry : sm3Map.entrySet()) { + macListContent.append(entry.getKey()).append("|").append(entry.getValue()).append("\n"); + } + + SM3Util.writeFile(CMEP_MAC_LIST_FILE, macListContent.toString()); + System.out.println("\nCMEP MAC列表文件已保存: " + CMEP_MAC_LIST_FILE); + + byte[] signature = SM2Util.sign(macListContent.toString().getBytes(StandardCharsets.UTF_8), MAC_PRIVATE_KEY_PATH); + SM3Util.writeFile(CMEP_MAC_SIGN_FILE, signature); + System.out.println("CMEP MAC签名文件已保存: " + CMEP_MAC_SIGN_FILE); + + PublicKey publicKey = SM2Util.loadPublicKey(MAC_PUBLIC_KEY_PATH); + boolean verified = SM2Util.verify(macListContent.toString().getBytes(StandardCharsets.UTF_8), signature, publicKey); + System.out.println("\n签名验证结果: " + verified); + } + + private static void verifyCMEPMAC() throws Exception { + System.out.println("=== 验证CMEP文件MAC校验值 ==="); + + PublicKey publicKey = SM2Util.loadPublicKey(MAC_PUBLIC_KEY_PATH); + byte[] savedSignature = SM3Util.readFileBytes(CMEP_MAC_SIGN_FILE); + String savedMacList = SM3Util.readFile(CMEP_MAC_LIST_FILE); + + boolean macListSignatureValid = SM2Util.verify(savedMacList.getBytes(StandardCharsets.UTF_8), savedSignature, publicKey); + System.out.println("CMEP MAC列表签名验证: " + (macListSignatureValid ? "通过" : "失败")); + + if (!macListSignatureValid) { + System.out.println("错误: cmep.mac.list 签名验证失败,可能被篡改!"); + System.out.println("\n=== 验证结果 ==="); + System.out.println("完整性验证: false"); + return; + } + + Map savedSm3Map = SM3Util.readSM3File(CMEP_MAC_LIST_FILE); + + Map currentSm3Map = new java.util.LinkedHashMap<>(); + for (String target : CMEP_TARGETS) { + File file = new File(target); + if (!file.exists()) { + continue; + } + + if (file.isFile()) { + String sm3Value = SM3Util.sm3FileHex(target); + currentSm3Map.put(target, sm3Value); + } + } + + boolean allMatch = true; + + for (Map.Entry entry : savedSm3Map.entrySet()) { + String filePath = entry.getKey(); + String savedHash = entry.getValue(); + String currentHash = currentSm3Map.get(filePath); + + if (currentHash == null) { + System.out.println("文件缺失: " + filePath); + allMatch = false; + } else if (!savedHash.equals(currentHash)) { + System.out.println("文件被修改: " + filePath + " (期望: " + savedHash + ", 实际: " + currentHash + ")"); + allMatch = false; + } + } + + for (String filePath : currentSm3Map.keySet()) { + if (!savedSm3Map.containsKey(filePath)) { + System.out.println("新增文件: " + filePath); + allMatch = false; + } + } + + System.out.println(); + System.out.println("=== 验证结果 ==="); + System.out.println("完整性验证: " + allMatch); + } +} diff --git a/src/main/java/com/sunyard/cisd/device/tool/SM2SignedEnvelopeUtil.java b/src/main/java/com/sunyard/cisd/device/tool/SM2SignedEnvelopeUtil.java new file mode 100644 index 0000000..95e5e9c --- /dev/null +++ b/src/main/java/com/sunyard/cisd/device/tool/SM2SignedEnvelopeUtil.java @@ -0,0 +1,258 @@ +package com.sunyard.cisd.device.tool; + +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; +import org.bouncycastle.cms.*; +import org.bouncycastle.cms.jcajce.*; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.util.encoders.Hex; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.ByteArrayOutputStream; +import java.security.*; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.*; + +public class SM2SignedEnvelopeUtil { + + private static final String PROVIDER = "BC"; + private static final String CURVE_NAME = "sm2p256v1"; + private static final String KEY_ALGORITHM = "EC"; + private static final String SIGNATURE_ALGORITHM = "SM3withSM2"; + private static final String SM4_ALGORITHM = "SM4/GCM/NoPadding"; + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + public static class SM2KeyPair { + public final PrivateKey privateKey; + public final PublicKey publicKey; + public final byte[] privateKeyBytes; + public final byte[] publicKeyBytes; + + public SM2KeyPair(PrivateKey privateKey, PublicKey publicKey) { + this.privateKey = privateKey; + this.publicKey = publicKey; + this.privateKeyBytes = privateKey.getEncoded(); + this.publicKeyBytes = publicKey.getEncoded(); + } + + public String getPrivateKeyBase64() { + return Base64.getEncoder().encodeToString(privateKeyBytes); + } + + public String getPublicKeyBase64() { + return Base64.getEncoder().encodeToString(publicKeyBytes); + } + + public String getPrivateKeyHex() { + return Hex.toHexString(privateKeyBytes); + } + + public String getPublicKeyHex() { + return Hex.toHexString(publicKeyBytes); + } + } + + public static SM2KeyPair generateKeyPair() throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM, PROVIDER); + ECGenParameterSpec ecSpec = new ECGenParameterSpec(CURVE_NAME); + keyPairGenerator.initialize(ecSpec, new SecureRandom()); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + return new SM2KeyPair(keyPair.getPrivate(), keyPair.getPublic()); + } + + public static PrivateKey loadPrivateKey(byte[] privateKeyBytes) throws Exception { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM, PROVIDER); + return keyFactory.generatePrivate(keySpec); + } + + public static PublicKey loadPublicKey(byte[] publicKeyBytes) throws Exception { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM, PROVIDER); + return keyFactory.generatePublic(keySpec); + } + + public static byte[] createSignedAndEnvelopedData(byte[] plaintext, PrivateKey signingKey, PublicKey encryptionPublicKey) throws Exception { + byte[] signature = signData(plaintext, signingKey); + + ByteArrayOutputStream signedContentBaos = new ByteArrayOutputStream(); + signedContentBaos.write(signature.length >> 24 & 0xFF); + signedContentBaos.write(signature.length >> 16 & 0xFF); + signedContentBaos.write(signature.length >> 8 & 0xFF); + signedContentBaos.write(signature.length & 0xFF); + signedContentBaos.write(signature); + signedContentBaos.write(plaintext); + byte[] signedContent = signedContentBaos.toByteArray(); + + byte[] encryptedContent = encryptData(signedContent, encryptionPublicKey); + + return encryptedContent; + } + + private static byte[] signData(byte[] data, PrivateKey privateKey) throws Exception { + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM, PROVIDER); + signature.initSign(privateKey); + signature.update(data); + return signature.sign(); + } + + private static byte[] encryptData(byte[] data, PublicKey publicKey) throws Exception { + SecureRandom random = new SecureRandom(); + byte[] iv = new byte[12]; + random.nextBytes(iv); + + KeyGenerator keyGen = KeyGenerator.getInstance("SM4", PROVIDER); + keyGen.init(128, random); + SecretKey sm4Key = keyGen.generateKey(); + + Cipher sm4Cipher = Cipher.getInstance(SM4_ALGORITHM, PROVIDER); + GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv); + sm4Cipher.init(Cipher.ENCRYPT_MODE, sm4Key, gcmSpec); + byte[] encryptedData = sm4Cipher.doFinal(data); + + Cipher ecCipher = Cipher.getInstance("ECIES", PROVIDER); + ecCipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] encryptedKey = ecCipher.doFinal(sm4Key.getEncoded()); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] keyLenBytes = new byte[]{ + (byte)(encryptedKey.length >> 8), + (byte)encryptedKey.length + }; + baos.write(keyLenBytes[0]); + baos.write(keyLenBytes[1]); + baos.write(encryptedKey); + baos.write(iv); + baos.write(encryptedData); + + return baos.toByteArray(); + } + + public static class DecryptedResult { + public final byte[] plaintext; + public final byte[] signature; + public final boolean signatureValid; + + public DecryptedResult(byte[] plaintext, byte[] signature, boolean signatureValid) { + this.plaintext = plaintext; + this.signature = signature; + this.signatureValid = signatureValid; + } + } + + public static DecryptedResult openAndVerifySignedEnvelopedData(byte[] envelopeData, PrivateKey decryptionKey, PublicKey signingPublicKey) throws Exception { + int offset = 0; + int encryptedKeyLen = ((envelopeData[offset] & 0xFF) << 8) | (envelopeData[offset + 1] & 0xFF); + offset += 2; + + byte[] encryptedKey = new byte[encryptedKeyLen]; + System.arraycopy(envelopeData, offset, encryptedKey, 0, encryptedKeyLen); + offset += encryptedKeyLen; + + byte[] iv = new byte[12]; + System.arraycopy(envelopeData, offset, iv, 0, 12); + offset += 12; + + byte[] encryptedData = new byte[envelopeData.length - offset]; + System.arraycopy(envelopeData, offset, encryptedData, 0, encryptedData.length); + + Cipher ecCipher = Cipher.getInstance("ECIES", PROVIDER); + ecCipher.init(Cipher.DECRYPT_MODE, decryptionKey); + byte[] sm4KeyBytes = ecCipher.doFinal(encryptedKey); + + SecretKeySpec sm4Key = new SecretKeySpec(sm4KeyBytes, "SM4"); + + Cipher sm4Cipher = Cipher.getInstance(SM4_ALGORITHM, PROVIDER); + GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv); + sm4Cipher.init(Cipher.DECRYPT_MODE, sm4Key, gcmSpec); + byte[] signedContent = sm4Cipher.doFinal(encryptedData); + + int signatureLength = ((signedContent[0] & 0xFF) << 24) | + ((signedContent[1] & 0xFF) << 16) | + ((signedContent[2] & 0xFF) << 8) | + (signedContent[3] & 0xFF); + + byte[] signature = new byte[signatureLength]; + byte[] plaintext = new byte[signedContent.length - 4 - signatureLength]; + + System.arraycopy(signedContent, 4, signature, 0, signatureLength); + System.arraycopy(signedContent, 4 + signatureLength, plaintext, 0, plaintext.length); + + boolean signatureValid = verifySignature(plaintext, signature, signingPublicKey); + + return new DecryptedResult(plaintext, signature, signatureValid); + } + + private static boolean verifySignature(byte[] data, byte[] signature, PublicKey publicKey) throws Exception { + Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM, PROVIDER); + sig.initVerify(publicKey); + sig.update(data); + return sig.verify(signature); + } + + public static String toBase64(byte[] data) { + return Base64.getEncoder().encodeToString(data); + } + + public static byte[] fromBase64(String base64) { + return Base64.getDecoder().decode(base64); + } + + public static String bytesToHex(byte[] data) { + return Hex.toHexString(data); + } + + public static byte[] hexToBytes(String hex) { + return Hex.decode(hex); + } + + public static void main(String[] args) throws Exception { + System.out.println("=== SM2 带签名数字信封测试 ==="); + System.out.println(); + + SM2KeyPair signingKeyPair = generateKeyPair(); + SM2KeyPair encryptionKeyPair = generateKeyPair(); + + System.out.println("签名密钥对已生成"); + System.out.println("加密密钥对已生成"); + System.out.println(); + + String testData = "这是测试数据 - Hello World! 你好世界!"; + byte[] plaintext = testData.getBytes("UTF-8"); + + System.out.println("原文: " + testData); + System.out.println("原文长度: " + plaintext.length + " bytes"); + System.out.println(); + + System.out.println("创建带签名的数字信封..."); + byte[] envelope = createSignedAndEnvelopedData(plaintext, signingKeyPair.privateKey, encryptionKeyPair.publicKey); + System.out.println("信封已创建,长度: " + envelope.length + " bytes"); + System.out.println("信封Base64: " + toBase64(envelope).substring(0, 80) + "..."); + System.out.println(); + + System.out.println("打开并验证数字信封..."); + DecryptedResult result = openAndVerifySignedEnvelopedData(envelope, encryptionKeyPair.privateKey, signingKeyPair.publicKey); + + System.out.println("解密后原文: " + new String(result.plaintext, "UTF-8")); + System.out.println("签名有效: " + result.signatureValid); + System.out.println("签名: " + bytesToHex(result.signature)); + System.out.println(); + + if (result.signatureValid) { + System.out.println("=== 验签成功 ==="); + } else { + System.out.println("=== 验签失败 ==="); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/sunyard/cisd/device/tool/SM2Util.java b/src/main/java/com/sunyard/cisd/device/tool/SM2Util.java new file mode 100644 index 0000000..0bc7755 --- /dev/null +++ b/src/main/java/com/sunyard/cisd/device/tool/SM2Util.java @@ -0,0 +1,134 @@ +package com.sunyard.cisd.device.tool; + +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +public class SM2Util { + private static final String ALGORITHM = "EC"; + private static final String PROVIDER = "BC"; + private static final String CURVE_NAME = "sm2p256v1"; + private static final String SIGNATURE_ALGORITHM = "SM3withSM2"; + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + public static byte[] sign(byte[] data, PrivateKey privateKey) throws Exception { + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM, PROVIDER); + signature.initSign(privateKey); + signature.update(data); + return signature.sign(); + } + + public static boolean verify(byte[] data, byte[] sign, PublicKey publicKey) throws Exception { + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM, PROVIDER); + signature.initVerify(publicKey); + signature.update(data); + return signature.verify(sign); + } + + public static byte[] sign(byte[] data, String privateKeyFilePath) throws Exception { + PrivateKey privateKey = loadPrivateKey(privateKeyFilePath); + return sign(data, privateKey); + } + + public static boolean verify(byte[] data, byte[] sign, String publicKeyFilePath) throws Exception { + PublicKey publicKey = loadPublicKey(publicKeyFilePath); + return verify(data, sign, publicKey); + } + + public static PrivateKey loadPrivateKey(String filePath) throws Exception { + InputStream inputStream = SM2Util.class.getResourceAsStream(filePath); + if (inputStream == null) { + throw new IllegalArgumentException("Private key file not found: " + filePath); + } + + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + StringBuilder keyBuilder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + if (!line.startsWith("-----")) { + keyBuilder.append(line.trim()); + } + } + reader.close(); + + String keyBase64 = keyBuilder.toString(); + byte[] keyBytes = Base64.getDecoder().decode(keyBase64); + + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, PROVIDER); + return keyFactory.generatePrivate(keySpec); + } + + public static PublicKey loadPublicKey(String filePath) throws Exception { + InputStream inputStream = SM2Util.class.getResourceAsStream(filePath); + if (inputStream == null) { + throw new IllegalArgumentException("Public key file not found: " + filePath); + } + + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + StringBuilder keyBuilder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + if (!line.startsWith("-----")) { + keyBuilder.append(line.trim()); + } + } + reader.close(); + + String keyBase64 = keyBuilder.toString(); + byte[] keyBytes = Base64.getDecoder().decode(keyBase64); + + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, PROVIDER); + return keyFactory.generatePublic(keySpec); + } + + public static KeyPair generateKeyPair() throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER); + ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(CURVE_NAME); + keyPairGenerator.initialize(ecSpec, new SecureRandom()); + return keyPairGenerator.generateKeyPair(); + } + + public static String privateKeyToPEM(PrivateKey privateKey) { + byte[] encoded = privateKey.getEncoded(); + String base64 = Base64.getEncoder().encodeToString(encoded); + StringBuilder pem = new StringBuilder(); + pem.append("-----BEGIN PRIVATE KEY-----\n"); + int index = 0; + while (index < base64.length()) { + pem.append(base64.substring(index, Math.min(index + 64, base64.length()))); + pem.append("\n"); + index += 64; + } + pem.append("-----END PRIVATE KEY-----\n"); + return pem.toString(); + } + + public static String publicKeyToPEM(PublicKey publicKey) { + byte[] encoded = publicKey.getEncoded(); + String base64 = Base64.getEncoder().encodeToString(encoded); + StringBuilder pem = new StringBuilder(); + pem.append("-----BEGIN PUBLIC KEY-----\n"); + int index = 0; + while (index < base64.length()) { + pem.append(base64.substring(index, Math.min(index + 64, base64.length()))); + pem.append("\n"); + index += 64; + } + pem.append("-----END PUBLIC KEY-----\n"); + return pem.toString(); + } +} diff --git a/src/main/java/com/sunyard/cisd/device/tool/SM3Util.java b/src/main/java/com/sunyard/cisd/device/tool/SM3Util.java new file mode 100644 index 0000000..100a630 --- /dev/null +++ b/src/main/java/com/sunyard/cisd/device/tool/SM3Util.java @@ -0,0 +1,162 @@ +package com.sunyard.cisd.device.tool; + +import org.bouncycastle.crypto.digests.SM3Digest; +import org.bouncycastle.util.encoders.Hex; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +public class SM3Util { + + public static byte[] sm3Digest(byte[] data) { + SM3Digest digest = new SM3Digest(); + digest.update(data, 0, data.length); + byte[] result = new byte[digest.getDigestSize()]; + digest.doFinal(result, 0); + return result; + } + + public static String sm3DigestHex(byte[] data) { + byte[] digest = sm3Digest(data); + return Hex.toHexString(digest); + } + + public static String sm3FileHex(String filePath) throws Exception { + File file = new File(filePath); + if (!file.exists() || !file.isFile()) { + throw new IllegalArgumentException("File not found: " + filePath); + } + + try (FileInputStream fis = new FileInputStream(file)) { + SM3Digest digest = new SM3Digest(); + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = fis.read(buffer)) != -1) { + digest.update(buffer, 0, bytesRead); + } + byte[] result = new byte[digest.getDigestSize()]; + digest.doFinal(result, 0); + return Hex.toHexString(result); + } + } + + public static Map calculateDirectorySM3(String directoryPath) throws Exception { + File dir = new File(directoryPath); + if (!dir.exists() || !dir.isDirectory()) { + throw new IllegalArgumentException("Directory not found: " + directoryPath); + } + + Map sm3Map = new HashMap<>(); + calculateDirectorySM3Recursive(dir, dir.getAbsolutePath(), sm3Map); + return sm3Map; + } + + private static void calculateDirectorySM3Recursive(File dir, String basePath, Map sm3Map) throws Exception { + File[] files = dir.listFiles(); + if (files == null) return; + + for (File file : files) { + if (file.isDirectory()) { + calculateDirectorySM3Recursive(file, basePath, sm3Map); + } else { + String relativePath = file.getAbsolutePath().substring(basePath.length() + 1); + String sm3Value = sm3FileHex(file.getAbsolutePath()); + sm3Map.put(relativePath, sm3Value); + } + } + } + + public static void writeSM3File(Map sm3Map, String outputFilePath) throws Exception { + StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : sm3Map.entrySet()) { + sb.append(entry.getKey()).append("|").append(entry.getValue()).append("\n"); + } + writeFile(outputFilePath, sb.toString()); + } + + public static Map readSM3File(String filePath) throws Exception { + Map sm3Map = new HashMap<>(); + try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split("\\|", 2); + if (parts.length == 2) { + sm3Map.put(parts[0], parts[1]); + } + } + } + return sm3Map; + } + + public static boolean verifyDirectorySM3(String directoryPath, String sm3FilePath) throws Exception { + Map savedSM3 = readSM3File(sm3FilePath); + Map currentSM3 = calculateDirectorySM3(directoryPath); + + boolean allMatch = true; + + for (Map.Entry entry : savedSM3.entrySet()) { + String filePath = entry.getKey(); + String savedHash = entry.getValue(); + String currentHash = currentSM3.get(filePath); + + if (currentHash == null) { + System.out.println("文件缺失: " + filePath); + allMatch = false; + } else if (!savedHash.equals(currentHash)) { + System.out.println("文件校验失败: " + filePath + " (期望: " + savedHash + ", 实际: " + currentHash + ")"); + allMatch = false; + } + } + + for (String filePath : currentSM3.keySet()) { + if (!savedSM3.containsKey(filePath)) { + System.out.println("新增文件: " + filePath); + allMatch = false; + } + } + + return allMatch; + } + + public static void writeFile(String filePath, byte[] content) throws Exception { + File file = new File(filePath); + File parentDir = file.getParentFile(); + if (parentDir != null && !parentDir.exists()) { + parentDir.mkdirs(); + } + try (OutputStream os = new FileOutputStream(file)) { + os.write(content); + } + } + + public static void writeFile(String filePath, String content) throws Exception { + writeFile(filePath, content.getBytes(StandardCharsets.UTF_8)); + } + + public static String readFile(String filePath) throws Exception { + StringBuilder sb = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + } + return sb.toString(); + } + + public static byte[] readFileBytes(String filePath) throws Exception { + File file = new File(filePath); + try (FileInputStream fis = new FileInputStream(file)) { + byte[] content = new byte[(int) file.length()]; + fis.read(content); + return content; + } + } +} diff --git a/src/main/java/com/sunyard/cisd/device/tool/SdfDeviceInfo.java b/src/main/java/com/sunyard/cisd/device/tool/SdfDeviceInfo.java new file mode 100644 index 0000000..b22e12b --- /dev/null +++ b/src/main/java/com/sunyard/cisd/device/tool/SdfDeviceInfo.java @@ -0,0 +1,33 @@ +package com.sunyard.cisd.device.tool; + +import com.sun.jna.Structure; +import lombok.Data; + +@Data +@Structure.FieldOrder({ + "issuerName", + "deviceName", + "deviceSerial", + "deviceVersion", + "standardVersion", + "asymAlgAbility", + "symAlgAbility", + "hashAlgAbility", + "bufferSize" +}) +public class SdfDeviceInfo extends Structure { + + public byte[] issuerName = new byte[40]; + public byte[] deviceName = new byte[16]; + public byte[] deviceSerial = new byte[16]; + public int deviceVersion; + public int standardVersion; + public int[] asymAlgAbility = new int[2]; + public int symAlgAbility; + public int hashAlgAbility; + public int bufferSize; + + public SdfDeviceInfo() { + super(ALIGN_NONE); + } +} \ No newline at end of file diff --git a/src/main/java/com/sunyard/cisd/device/tool/SdfNativeLibrary.java b/src/main/java/com/sunyard/cisd/device/tool/SdfNativeLibrary.java new file mode 100644 index 0000000..ef3d559 --- /dev/null +++ b/src/main/java/com/sunyard/cisd/device/tool/SdfNativeLibrary.java @@ -0,0 +1,13 @@ +package com.sunyard.cisd.device.tool; + +import com.sun.jna.Library; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; + +public interface SdfNativeLibrary extends Library { + int SDF_OpenDevice(PointerByReference phDeviceHandle); + int SDF_CloseDevice(Pointer hDeviceHandle); + int SDF_OpenSession(Pointer hDeviceHandle, PointerByReference phSessionHandle); + int SDF_CloseSession(Pointer hSessionHandle); + int SDF_GetDeviceInfo(Pointer hSessionHandle, SdfDeviceInfo deviceInfo); +} \ No newline at end of file diff --git a/src/main/resources/CFCA_TEST_CS_SM2_CA.cer b/src/main/resources/CFCA_TEST_CS_SM2_CA.cer new file mode 100644 index 0000000000000000000000000000000000000000..a1631776185aeaf779ec407a389ed83a7e2e74a4 GIT binary patch literal 536 zcmXqLViGZEV%)xfnTe5!i7SFfm(}h}>vg-`20UyWT8%R2Lm8V(Ss4ss4Y>_C*_cCF z*o2v!{R|BZ^gtXg9(Cu8%)CSeHz1LhoSB%T;G9}il9`s7oLG{XpQqqhT9T1plvz?~ zC}SW6(!tCl?Cj?3s1V{B9HQVHtPt#Lq~PpmAScdiWME)yXl!6+00dFuyvC*m<_4w) z#zqz<#!!1(!~wSYq~MFmPj1WSHy4xGlSduT@@o^^Ej3@