Compare commits
No commits in common. "01077364161b97728fab568b9c97355649370da1" and "971da4fd17e64afc2307ee1a76b5f3676ff7057a" have entirely different histories.
0107736416
...
971da4fd17
28
README.md
28
README.md
@ -1,28 +0,0 @@
|
|||||||
# sectool
|
|
||||||
|
|
||||||
`sectool` 当前统一入口为 `com.sunyard.cisd.device.tool.Main`,`pom.xml` 已配置打包后的 `Main-Class` 指向该类。
|
|
||||||
|
|
||||||
## 支持功能
|
|
||||||
|
|
||||||
- `genkey`:生成 SM2 密钥对,输出 Base64 编码的 PKCS#8 私钥和 X.509 公钥。
|
|
||||||
- `env-create`:生成普通 SM2 数字信封。
|
|
||||||
- `env-open`:解密普通 SM2 数字信封。
|
|
||||||
- `signed-env-create`:生成带签名的 SM2 数字信封。
|
|
||||||
- `signed-env-open`:解密并验证带签名的 SM2 数字信封。
|
|
||||||
|
|
||||||
## 使用方式
|
|
||||||
|
|
||||||
```bash
|
|
||||||
java -jar target/sectool-1.0.3-jar-with-dependencies.jar genkey
|
|
||||||
java -jar target/sectool-1.0.3-jar-with-dependencies.jar env-create <publicKeyBase64> <text|@file>
|
|
||||||
java -jar target/sectool-1.0.3-jar-with-dependencies.jar env-open <privateKeyBase64> <envelopeBase64|@file>
|
|
||||||
java -jar target/sectool-1.0.3-jar-with-dependencies.jar signed-env-create <signPrivateKeyBase64> <encryptPublicKeyBase64> <text|@file>
|
|
||||||
java -jar target/sectool-1.0.3-jar-with-dependencies.jar signed-env-open <decryptPrivateKeyBase64> <signPublicKeyBase64> <envelopeBase64|@file>
|
|
||||||
```
|
|
||||||
|
|
||||||
说明:`@file` 表示从 UTF-8 文件读取输入内容;命令输出的数字信封均为 Base64。
|
|
||||||
|
|
||||||
## 测试用例
|
|
||||||
|
|
||||||
- `1-1`:普通 SM2 数字信封生成和解密。
|
|
||||||
- `1-2`:带签名 SM2 数字信封生成、解密和验签。
|
|
||||||
@ -100,4 +100,31 @@ public class CMSSignatureUtil {
|
|||||||
return (X509Certificate) cf.generateCertificate(new java.io.ByteArrayInputStream(certHolder.getEncoded()));
|
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_cms_data> [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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -819,6 +819,45 @@ public class CMSUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
System.out.println("用法: java CMSUtil <base64_cms_data>");
|
||||||
|
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();
|
||||||
|
|
||||||
|
SignedAndEnvelopedVerifyResult result = verifySignedAndEnvelopedData(cmsData);
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("========== 验签结果汇总 ==========");
|
||||||
|
System.out.println("验签结果: " + (result.signatureValid ? "成功" : "失败"));
|
||||||
|
System.out.println("签名者证书主题: " + result.signerSubjectDN);
|
||||||
|
if (result.messageDigest != null) {
|
||||||
|
System.out.println("原文Hash(SM3): " + bytesToHex(result.messageDigest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("处理失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String bytesToHex(byte[] data) {
|
public static String bytesToHex(byte[] data) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|||||||
@ -1,162 +1,504 @@
|
|||||||
package com.sunyard.cisd.device.tool;
|
package com.sunyard.cisd.device.tool;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class Main {
|
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";
|
||||||
* @param args 命令行参数,第一位为子命令。
|
private static final String PUBLIC_KEY_FILE = DEVICE_DIR + "/dev.fingerprint.pub";
|
||||||
* @return 无返回值。
|
private static final String PUBLIC_KEY_SM3_FILE = DEVICE_DIR + "/dev.fingerprint.pub.sm3";
|
||||||
* @throws Exception 参数错误、密钥读取失败或加解密失败时抛出。
|
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 void main(String[] args) throws Exception {
|
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) {
|
if (args.length == 0) {
|
||||||
printUsage();
|
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<String, String> 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<String, String> dirSm3Map = SM3Util.calculateDirectorySM3(target);
|
||||||
|
for (Map.Entry<String, String> 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<String, String> 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String command = args[0];
|
Map<String, String> savedSm3Map = SM3Util.readSM3File(MAC_LIST_FILE);
|
||||||
if ("genkey".equals(command)) {
|
|
||||||
generateKeyPair();
|
Map<String, String> currentSm3Map = new java.util.LinkedHashMap<>();
|
||||||
} else if ("env-create".equals(command)) {
|
for (String target : MAC_TARGETS) {
|
||||||
createEnvelope(args);
|
File file = new File(target);
|
||||||
} else if ("env-open".equals(command)) {
|
if (!file.exists()) {
|
||||||
openEnvelope(args);
|
continue;
|
||||||
} else if ("signed-env-create".equals(command)) {
|
}
|
||||||
createSignedEnvelope(args);
|
|
||||||
} else if ("signed-env-open".equals(command)) {
|
if (file.isFile()) {
|
||||||
openSignedEnvelope(args);
|
String sm3Value = SM3Util.sm3FileHex(target);
|
||||||
} else {
|
currentSm3Map.put(target, sm3Value);
|
||||||
printUsage();
|
} else if (file.isDirectory()) {
|
||||||
throw new IllegalArgumentException("未知命令: " + command);
|
Map<String, String> dirSm3Map = SM3Util.calculateDirectorySM3(target);
|
||||||
|
for (Map.Entry<String, String> entry : dirSm3Map.entrySet()) {
|
||||||
|
String absolutePath = target + "/" + entry.getKey();
|
||||||
|
currentSm3Map.put(absolutePath, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean allMatch = true;
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> 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 {
|
||||||
* 生成 SM2 密钥对并输出 Base64。
|
System.out.println("=== 生成CMEP文件MAC校验值 ===");
|
||||||
*
|
System.out.println("目标路径:");
|
||||||
* @param 无参数。
|
for (String target : CMEP_TARGETS) {
|
||||||
* @return 无返回值。
|
System.out.println(" " + target);
|
||||||
* @throws Exception 生成密钥失败时抛出。
|
|
||||||
*/
|
|
||||||
private static void generateKeyPair() throws Exception {
|
|
||||||
SM2SignedEnvelopeUtil.SM2KeyPair keyPair = SM2SignedEnvelopeUtil.generateKeyPair();
|
|
||||||
System.out.println("privateKeyBase64=" + keyPair.getPrivateKeyBase64());
|
|
||||||
System.out.println("publicKeyBase64=" + keyPair.getPublicKeyBase64());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建普通 SM2 数字信封。
|
|
||||||
*
|
|
||||||
* @param args 命令行参数,格式为 env-create <publicKeyBase64> <text|@file>。
|
|
||||||
* @return 无返回值,标准输出为信封 Base64。
|
|
||||||
* @throws Exception 参数错误、密钥解析失败或加密失败时抛出。
|
|
||||||
*/
|
|
||||||
private static void createEnvelope(String[] args) throws Exception {
|
|
||||||
requireArgs(args, 3, "env-create <publicKeyBase64> <text|@file>");
|
|
||||||
PublicKey publicKey = SM2SignedEnvelopeUtil.loadPublicKey(Base64.getDecoder().decode(args[1]));
|
|
||||||
byte[] plaintext = readTextArg(args[2]);
|
|
||||||
byte[] envelope = SM2SignedEnvelopeUtil.createEnvelopedData(plaintext, publicKey);
|
|
||||||
System.out.println(Base64.getEncoder().encodeToString(envelope));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解密普通 SM2 数字信封。
|
|
||||||
*
|
|
||||||
* @param args 命令行参数,格式为 env-open <privateKeyBase64> <envelopeBase64|@file>。
|
|
||||||
* @return 无返回值,标准输出为 UTF-8 原文。
|
|
||||||
* @throws Exception 参数错误、密钥解析失败或解密失败时抛出。
|
|
||||||
*/
|
|
||||||
private static void openEnvelope(String[] args) throws Exception {
|
|
||||||
requireArgs(args, 3, "env-open <privateKeyBase64> <envelopeBase64|@file>");
|
|
||||||
PrivateKey privateKey = SM2SignedEnvelopeUtil.loadPrivateKey(Base64.getDecoder().decode(args[1]));
|
|
||||||
byte[] envelope = Base64.getDecoder().decode(new String(readTextArg(args[2]), StandardCharsets.UTF_8).trim());
|
|
||||||
byte[] plaintext = SM2SignedEnvelopeUtil.openEnvelopedData(envelope, privateKey);
|
|
||||||
System.out.println(new String(plaintext, StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建带签名的 SM2 数字信封。
|
|
||||||
*
|
|
||||||
* @param args 命令行参数,格式为 signed-env-create <signPrivateKeyBase64> <encryptPublicKeyBase64> <text|@file>。
|
|
||||||
* @return 无返回值,标准输出为信封 Base64。
|
|
||||||
* @throws Exception 参数错误、密钥解析失败、签名或加密失败时抛出。
|
|
||||||
*/
|
|
||||||
private static void createSignedEnvelope(String[] args) throws Exception {
|
|
||||||
requireArgs(args, 4, "signed-env-create <signPrivateKeyBase64> <encryptPublicKeyBase64> <text|@file>");
|
|
||||||
PrivateKey signPrivateKey = SM2SignedEnvelopeUtil.loadPrivateKey(Base64.getDecoder().decode(args[1]));
|
|
||||||
PublicKey encryptPublicKey = SM2SignedEnvelopeUtil.loadPublicKey(Base64.getDecoder().decode(args[2]));
|
|
||||||
byte[] plaintext = readTextArg(args[3]);
|
|
||||||
byte[] envelope = SM2SignedEnvelopeUtil.createSignedAndEnvelopedData(plaintext, signPrivateKey, encryptPublicKey);
|
|
||||||
System.out.println(Base64.getEncoder().encodeToString(envelope));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解密并验签带签名的 SM2 数字信封。
|
|
||||||
*
|
|
||||||
* @param args 命令行参数,格式为 signed-env-open <decryptPrivateKeyBase64> <signPublicKeyBase64> <envelopeBase64|@file>。
|
|
||||||
* @return 无返回值,标准输出为验签结果和 UTF-8 原文。
|
|
||||||
* @throws Exception 参数错误、密钥解析失败、解密或验签失败时抛出。
|
|
||||||
*/
|
|
||||||
private static void openSignedEnvelope(String[] args) throws Exception {
|
|
||||||
requireArgs(args, 4, "signed-env-open <decryptPrivateKeyBase64> <signPublicKeyBase64> <envelopeBase64|@file>");
|
|
||||||
PrivateKey decryptPrivateKey = SM2SignedEnvelopeUtil.loadPrivateKey(Base64.getDecoder().decode(args[1]));
|
|
||||||
PublicKey signPublicKey = SM2SignedEnvelopeUtil.loadPublicKey(Base64.getDecoder().decode(args[2]));
|
|
||||||
byte[] envelope = Base64.getDecoder().decode(new String(readTextArg(args[3]), StandardCharsets.UTF_8).trim());
|
|
||||||
SM2SignedEnvelopeUtil.DecryptedResult result = SM2SignedEnvelopeUtil.openAndVerifySignedEnvelopedData(envelope, decryptPrivateKey, signPublicKey);
|
|
||||||
System.out.println("signatureValid=" + result.signatureValid);
|
|
||||||
System.out.println(new String(result.plaintext, StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验命令行参数数量。
|
|
||||||
*
|
|
||||||
* @param args 实际参数数组。
|
|
||||||
* @param expected 期望参数数量。
|
|
||||||
* @param usage 当前命令用法说明。
|
|
||||||
* @return 无返回值。
|
|
||||||
*/
|
|
||||||
private static void requireArgs(String[] args, int expected, String usage) {
|
|
||||||
if (args.length != expected) {
|
|
||||||
printUsage();
|
|
||||||
throw new IllegalArgumentException("用法: " + usage);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
Map<String, String> sm3Map = new java.util.LinkedHashMap<>();
|
||||||
* 读取文本参数。
|
|
||||||
*
|
for (String target : CMEP_TARGETS) {
|
||||||
* @param value 文本值;以 @ 开头时表示从文件读取 UTF-8 文本。
|
File file = new File(target);
|
||||||
* @return UTF-8 文本字节。
|
if (!file.exists()) {
|
||||||
* @throws Exception 文件读取失败时抛出。
|
System.out.println("警告: 路径不存在 - " + target);
|
||||||
*/
|
continue;
|
||||||
private static byte[] readTextArg(String value) throws Exception {
|
}
|
||||||
if (value.startsWith("@")) {
|
|
||||||
return Files.readAllBytes(Paths.get(value.substring(1)));
|
if (file.isFile()) {
|
||||||
|
String sm3Value = SM3Util.sm3FileHex(target);
|
||||||
|
sm3Map.put(target, sm3Value);
|
||||||
|
System.out.println("文件: " + target + " -> " + sm3Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return value.getBytes(StandardCharsets.UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
StringBuilder macListContent = new StringBuilder();
|
||||||
* 打印统一入口用法。
|
for (Map.Entry<String, String> entry : sm3Map.entrySet()) {
|
||||||
*
|
macListContent.append(entry.getKey()).append("|").append(entry.getValue()).append("\n");
|
||||||
* @param 无参数。
|
}
|
||||||
* @return 无返回值。
|
|
||||||
*/
|
SM3Util.writeFile(CMEP_MAC_LIST_FILE, macListContent.toString());
|
||||||
private static void printUsage() {
|
System.out.println("\nCMEP MAC列表文件已保存: " + CMEP_MAC_LIST_FILE);
|
||||||
System.out.println("统一入口: java -jar sectool-*-jar-with-dependencies.jar <command> [args]");
|
|
||||||
System.out.println("命令:");
|
byte[] signature = SM2Util.sign(macListContent.toString().getBytes(StandardCharsets.UTF_8), MAC_PRIVATE_KEY_PATH);
|
||||||
System.out.println(" genkey");
|
SM3Util.writeFile(CMEP_MAC_SIGN_FILE, signature);
|
||||||
System.out.println(" env-create <publicKeyBase64> <text|@file>");
|
System.out.println("CMEP MAC签名文件已保存: " + CMEP_MAC_SIGN_FILE);
|
||||||
System.out.println(" env-open <privateKeyBase64> <envelopeBase64|@file>");
|
|
||||||
System.out.println(" signed-env-create <signPrivateKeyBase64> <encryptPublicKeyBase64> <text|@file>");
|
PublicKey publicKey = SM2Util.loadPublicKey(MAC_PUBLIC_KEY_PATH);
|
||||||
System.out.println(" signed-env-open <decryptPrivateKeyBase64> <signPublicKeyBase64> <envelopeBase64|@file>");
|
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<String, String> savedSm3Map = SM3Util.readSM3File(CMEP_MAC_LIST_FILE);
|
||||||
|
|
||||||
|
Map<String, String> 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<String, String> 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,27 +80,6 @@ public class SM2SignedEnvelopeUtil {
|
|||||||
return keyFactory.generatePublic(keySpec);
|
return keyFactory.generatePublic(keySpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ???? SM2 ?????
|
|
||||||
*
|
|
||||||
* @param plaintext ??????
|
|
||||||
* @param encryptionPublicKey ??? SM2 ???
|
|
||||||
* @return ??????????
|
|
||||||
* @throws Exception ???????????
|
|
||||||
*/
|
|
||||||
public static byte[] createEnvelopedData(byte[] plaintext, PublicKey encryptionPublicKey) throws Exception {
|
|
||||||
return encryptContent(plaintext, encryptionPublicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ?????? SM2 ?????
|
|
||||||
*
|
|
||||||
* @param plaintext ??????????
|
|
||||||
* @param signingKey ??? SM2 ???
|
|
||||||
* @param encryptionPublicKey ??? SM2 ???
|
|
||||||
* @return ??????????????
|
|
||||||
* @throws Exception ??????????????
|
|
||||||
*/
|
|
||||||
public static byte[] createSignedAndEnvelopedData(byte[] plaintext, PrivateKey signingKey, PublicKey encryptionPublicKey) throws Exception {
|
public static byte[] createSignedAndEnvelopedData(byte[] plaintext, PrivateKey signingKey, PublicKey encryptionPublicKey) throws Exception {
|
||||||
byte[] sm3Digest = calculateSM3Digest(plaintext);
|
byte[] sm3Digest = calculateSM3Digest(plaintext);
|
||||||
|
|
||||||
@ -121,36 +100,6 @@ public class SM2SignedEnvelopeUtil {
|
|||||||
signature.update(signedAttrsEncoded);
|
signature.update(signedAttrsEncoded);
|
||||||
byte[] signatureValue = signature.sign();
|
byte[] signatureValue = signature.sign();
|
||||||
|
|
||||||
byte[] encryptedContent = encryptContent(plaintext, encryptionPublicKey);
|
|
||||||
|
|
||||||
byte[] finalData = new byte[4 + signatureValue.length + encryptedContent.length];
|
|
||||||
byte[] sigLen = new byte[]{
|
|
||||||
(byte)(signatureValue.length >> 24),
|
|
||||||
(byte)(signatureValue.length >> 16),
|
|
||||||
(byte)(signatureValue.length >> 8),
|
|
||||||
(byte)signatureValue.length
|
|
||||||
};
|
|
||||||
System.arraycopy(sigLen, 0, finalData, 0, 4);
|
|
||||||
System.arraycopy(signatureValue, 0, finalData, 4, signatureValue.length);
|
|
||||||
System.arraycopy(encryptedContent, 0, finalData, 4 + signatureValue.length, encryptedContent.length);
|
|
||||||
|
|
||||||
return finalData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] calculateSM3Digest(byte[] data) throws Exception {
|
|
||||||
MessageDigest digest = MessageDigest.getInstance("SM3", PROVIDER);
|
|
||||||
return digest.digest(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ???????
|
|
||||||
*
|
|
||||||
* @param plaintext ??????
|
|
||||||
* @param encryptionPublicKey ??? SM2 ???
|
|
||||||
* @return ?????????????SM2 ??? SM4 ???IV????
|
|
||||||
* @throws Exception ????????
|
|
||||||
*/
|
|
||||||
private static byte[] encryptContent(byte[] plaintext, PublicKey encryptionPublicKey) throws Exception {
|
|
||||||
SecureRandom random = new SecureRandom();
|
SecureRandom random = new SecureRandom();
|
||||||
byte[] iv = new byte[12];
|
byte[] iv = new byte[12];
|
||||||
random.nextBytes(iv);
|
random.nextBytes(iv);
|
||||||
@ -174,46 +123,26 @@ public class SM2SignedEnvelopeUtil {
|
|||||||
baos.write(encryptedKey);
|
baos.write(encryptedKey);
|
||||||
baos.write(iv);
|
baos.write(iv);
|
||||||
baos.write(encryptedData);
|
baos.write(encryptedData);
|
||||||
return baos.toByteArray();
|
|
||||||
|
byte[] encryptedContent = baos.toByteArray();
|
||||||
|
|
||||||
|
byte[] finalData = new byte[4 + signatureValue.length + encryptedContent.length];
|
||||||
|
byte[] sigLen = new byte[]{
|
||||||
|
(byte)(signatureValue.length >> 24),
|
||||||
|
(byte)(signatureValue.length >> 16),
|
||||||
|
(byte)(signatureValue.length >> 8),
|
||||||
|
(byte)signatureValue.length
|
||||||
|
};
|
||||||
|
System.arraycopy(sigLen, 0, finalData, 0, 4);
|
||||||
|
System.arraycopy(signatureValue, 0, finalData, 4, signatureValue.length);
|
||||||
|
System.arraycopy(encryptedContent, 0, finalData, 4 + signatureValue.length, encryptedContent.length);
|
||||||
|
|
||||||
|
return finalData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static byte[] calculateSM3Digest(byte[] data) throws Exception {
|
||||||
* ???????
|
MessageDigest digest = MessageDigest.getInstance("SM3", PROVIDER);
|
||||||
*
|
return digest.digest(data);
|
||||||
* @param encryptedContent ?????????????SM2 ??? SM4 ???IV????
|
|
||||||
* @param decryptionKey ??? SM2 ???
|
|
||||||
* @return ???????
|
|
||||||
* @throws Exception ???????????????
|
|
||||||
*/
|
|
||||||
private static byte[] decryptContent(byte[] encryptedContent, PrivateKey decryptionKey) throws Exception {
|
|
||||||
if (encryptedContent.length < 14) {
|
|
||||||
throw new IllegalArgumentException("??????????");
|
|
||||||
}
|
|
||||||
|
|
||||||
int keyLen = ((encryptedContent[0] & 0xFF) << 8) | (encryptedContent[1] & 0xFF);
|
|
||||||
if (keyLen <= 0 || encryptedContent.length < 2 + keyLen + 12) {
|
|
||||||
throw new IllegalArgumentException("??????????");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] encryptedKey = new byte[keyLen];
|
|
||||||
System.arraycopy(encryptedContent, 2, encryptedKey, 0, keyLen);
|
|
||||||
|
|
||||||
byte[] iv = new byte[12];
|
|
||||||
System.arraycopy(encryptedContent, 2 + keyLen, iv, 0, 12);
|
|
||||||
|
|
||||||
byte[] encryptedData = new byte[encryptedContent.length - 2 - keyLen - 12];
|
|
||||||
System.arraycopy(encryptedContent, 2 + keyLen + 12, 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);
|
|
||||||
return sm4Cipher.doFinal(encryptedData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class VerifyResult {
|
public static class VerifyResult {
|
||||||
@ -282,48 +211,38 @@ public class SM2SignedEnvelopeUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ???? SM2 ?????
|
|
||||||
*
|
|
||||||
* @param envelopeData ??????????
|
|
||||||
* @param decryptionKey ??? SM2 ???
|
|
||||||
* @return ???????
|
|
||||||
* @throws Exception ???????????????
|
|
||||||
*/
|
|
||||||
public static byte[] openEnvelopedData(byte[] envelopeData, PrivateKey decryptionKey) throws Exception {
|
|
||||||
return decryptContent(envelopeData, decryptionKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ????????? SM2 ?????
|
|
||||||
*
|
|
||||||
* @param envelopeData ??????????????
|
|
||||||
* @param decryptionKey ??? SM2 ???
|
|
||||||
* @param signingPublicKey ??? SM2 ???
|
|
||||||
* @return ???????????????????????
|
|
||||||
* @throws Exception ??????????????????
|
|
||||||
*/
|
|
||||||
public static DecryptedResult openAndVerifySignedEnvelopedData(byte[] envelopeData, PrivateKey decryptionKey, PublicKey signingPublicKey) throws Exception {
|
public static DecryptedResult openAndVerifySignedEnvelopedData(byte[] envelopeData, PrivateKey decryptionKey, PublicKey signingPublicKey) throws Exception {
|
||||||
if (envelopeData.length < 6) {
|
|
||||||
throw new IllegalArgumentException("?????????????");
|
|
||||||
}
|
|
||||||
|
|
||||||
int sigLen = ((envelopeData[0] & 0xFF) << 24) |
|
int sigLen = ((envelopeData[0] & 0xFF) << 24) |
|
||||||
((envelopeData[1] & 0xFF) << 16) |
|
((envelopeData[1] & 0xFF) << 16) |
|
||||||
((envelopeData[2] & 0xFF) << 8) |
|
((envelopeData[2] & 0xFF) << 8) |
|
||||||
(envelopeData[3] & 0xFF);
|
(envelopeData[3] & 0xFF);
|
||||||
|
|
||||||
if (sigLen <= 0 || envelopeData.length < 4 + sigLen) {
|
|
||||||
throw new IllegalArgumentException("?????????????");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] signatureValue = new byte[sigLen];
|
byte[] signatureValue = new byte[sigLen];
|
||||||
System.arraycopy(envelopeData, 4, signatureValue, 0, sigLen);
|
System.arraycopy(envelopeData, 4, signatureValue, 0, sigLen);
|
||||||
|
|
||||||
byte[] encryptedContent = new byte[envelopeData.length - 4 - sigLen];
|
byte[] encryptedContent = new byte[envelopeData.length - 4 - sigLen];
|
||||||
System.arraycopy(envelopeData, 4 + sigLen, encryptedContent, 0, encryptedContent.length);
|
System.arraycopy(envelopeData, 4 + sigLen, encryptedContent, 0, encryptedContent.length);
|
||||||
|
|
||||||
byte[] plaintext = decryptContent(encryptedContent, decryptionKey);
|
int keyLen = ((encryptedContent[0] & 0xFF) << 8) | (encryptedContent[1] & 0xFF);
|
||||||
|
byte[] encryptedKey = new byte[keyLen];
|
||||||
|
System.arraycopy(encryptedContent, 2, encryptedKey, 0, keyLen);
|
||||||
|
|
||||||
|
byte[] iv = new byte[12];
|
||||||
|
System.arraycopy(encryptedContent, 2 + keyLen, iv, 0, 12);
|
||||||
|
|
||||||
|
byte[] encryptedData = new byte[encryptedContent.length - 2 - keyLen - 12];
|
||||||
|
System.arraycopy(encryptedContent, 2 + keyLen + 12, 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[] plaintext = sm4Cipher.doFinal(encryptedData);
|
||||||
|
|
||||||
byte[] messageDigest = calculateSM3Digest(plaintext);
|
byte[] messageDigest = calculateSM3Digest(plaintext);
|
||||||
byte[] signedAttrsEncoded = buildSignedAttrs(messageDigest);
|
byte[] signedAttrsEncoded = buildSignedAttrs(messageDigest);
|
||||||
@ -349,4 +268,58 @@ public class SM2SignedEnvelopeUtil {
|
|||||||
return Hex.decode(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("原文SM3哈希: " + bytesToHex(calculateSM3Digest(plaintext)));
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
System.out.println("创建带签名的数字信封...");
|
||||||
|
byte[] envelope = createSignedAndEnvelopedData(plaintext, signingKeyPair.privateKey, encryptionKeyPair.publicKey);
|
||||||
|
System.out.println("信封已创建,长度: " + envelope.length + " bytes");
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
System.out.println("=== 不解密验证签名 (使用 signedAttrs) ===");
|
||||||
|
byte[] messageDigest = calculateSM3Digest(plaintext);
|
||||||
|
byte[] signedAttrsEncoded = buildSignedAttrs(messageDigest);
|
||||||
|
|
||||||
|
int sigLen = ((envelope[0] & 0xFF) << 24) |
|
||||||
|
((envelope[1] & 0xFF) << 16) |
|
||||||
|
((envelope[2] & 0xFF) << 8) |
|
||||||
|
(envelope[3] & 0xFF);
|
||||||
|
byte[] signatureValue = new byte[sigLen];
|
||||||
|
System.arraycopy(envelope, 4, signatureValue, 0, sigLen);
|
||||||
|
|
||||||
|
boolean sigValid = verifySignatureWithAttrs(signatureValue, signedAttrsEncoded, signingKeyPair.publicKey);
|
||||||
|
System.out.println("签名有效: " + sigValid);
|
||||||
|
System.out.println("提取的消息摘要: " + bytesToHex(extractMessageDigestFromAttrs(signedAttrsEncoded)));
|
||||||
|
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.messageDigest));
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
if (sigValid && result.signatureValid) {
|
||||||
|
System.out.println("=== 全部验证通过 ===");
|
||||||
|
} else {
|
||||||
|
System.out.println("=== 验证失败 ===");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,52 +0,0 @@
|
|||||||
package com.sunyard.cisd;
|
|
||||||
|
|
||||||
import com.sunyard.cisd.device.tool.SM2SignedEnvelopeUtil;
|
|
||||||
import com.sunyard.cisd.device.tool.SM2SignedEnvelopeUtil.DecryptedResult;
|
|
||||||
import com.sunyard.cisd.device.tool.SM2SignedEnvelopeUtil.SM2KeyPair;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
public class SM2EnvelopeTest {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1-1 验证普通 SM2 数字信封可以生成并解密回原文。
|
|
||||||
*
|
|
||||||
* @param 无参数。
|
|
||||||
* @return 无返回值。
|
|
||||||
* @throws Exception 生成密钥、加密或解密失败时抛出。
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void test1_1CreateAndOpenEnvelope() throws Exception {
|
|
||||||
SM2KeyPair encryptionKeyPair = SM2SignedEnvelopeUtil.generateKeyPair();
|
|
||||||
byte[] plaintext = "1-1 普通 SM2 数字信封".getBytes(StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
byte[] envelope = SM2SignedEnvelopeUtil.createEnvelopedData(plaintext, encryptionKeyPair.publicKey);
|
|
||||||
byte[] opened = SM2SignedEnvelopeUtil.openEnvelopedData(envelope, encryptionKeyPair.privateKey);
|
|
||||||
|
|
||||||
Assert.assertArrayEquals(plaintext, opened);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1-2 验证带签名的 SM2 数字信封可以生成、解密并验签通过。
|
|
||||||
*
|
|
||||||
* @param 无参数。
|
|
||||||
* @return 无返回值。
|
|
||||||
* @throws Exception 生成密钥、签名、加密、解密或验签失败时抛出。
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void test1_2CreateAndOpenSignedEnvelope() throws Exception {
|
|
||||||
SM2KeyPair signingKeyPair = SM2SignedEnvelopeUtil.generateKeyPair();
|
|
||||||
SM2KeyPair encryptionKeyPair = SM2SignedEnvelopeUtil.generateKeyPair();
|
|
||||||
byte[] plaintext = "1-2 带签名 SM2 数字信封".getBytes(StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
byte[] envelope = SM2SignedEnvelopeUtil.createSignedAndEnvelopedData(
|
|
||||||
plaintext, signingKeyPair.privateKey, encryptionKeyPair.publicKey);
|
|
||||||
DecryptedResult result = SM2SignedEnvelopeUtil.openAndVerifySignedEnvelopedData(
|
|
||||||
envelope, encryptionKeyPair.privateKey, signingKeyPair.publicKey);
|
|
||||||
|
|
||||||
Assert.assertArrayEquals(plaintext, result.plaintext);
|
|
||||||
Assert.assertTrue(result.signatureValid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user