diff --git a/README.md b/README.md new file mode 100644 index 0000000..07bf840 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# 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 +java -jar target/sectool-1.0.3-jar-with-dependencies.jar env-open +java -jar target/sectool-1.0.3-jar-with-dependencies.jar signed-env-create +java -jar target/sectool-1.0.3-jar-with-dependencies.jar signed-env-open +``` + +说明:`@file` 表示从 UTF-8 文件读取输入内容;命令输出的数字信封均为 Base64。 + +## 测试用例 + +- `1-1`:普通 SM2 数字信封生成和解密。 +- `1-2`:带签名 SM2 数字信封生成、解密和验签。 diff --git a/src/main/java/com/sunyard/cisd/device/tool/CMSSignatureUtil.java b/src/main/java/com/sunyard/cisd/device/tool/CMSSignatureUtil.java index 14e0666..4dc7ac9 100644 --- a/src/main/java/com/sunyard/cisd/device/tool/CMSSignatureUtil.java +++ b/src/main/java/com/sunyard/cisd/device/tool/CMSSignatureUtil.java @@ -100,31 +100,4 @@ public class CMSSignatureUtil { 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 index e6061ac..a62df8f 100644 --- a/src/main/java/com/sunyard/cisd/device/tool/CMSUtil.java +++ b/src/main/java/com/sunyard/cisd/device/tool/CMSUtil.java @@ -819,45 +819,6 @@ public class CMSUtil { return false; } - 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(); - - 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) { StringBuilder sb = new StringBuilder(); 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..c52a6e7 --- /dev/null +++ b/src/main/java/com/sunyard/cisd/device/tool/Main.java @@ -0,0 +1,162 @@ +package com.sunyard.cisd.device.tool; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.Base64; + +public class Main { + + /** + * 统一命令行入口。 + * + * @param args 命令行参数,第一位为子命令。 + * @return 无返回值。 + * @throws Exception 参数错误、密钥读取失败或加解密失败时抛出。 + */ + public static void main(String[] args) throws Exception { + if (args.length == 0) { + printUsage(); + return; + } + + String command = args[0]; + if ("genkey".equals(command)) { + generateKeyPair(); + } else if ("env-create".equals(command)) { + createEnvelope(args); + } else if ("env-open".equals(command)) { + openEnvelope(args); + } else if ("signed-env-create".equals(command)) { + createSignedEnvelope(args); + } else if ("signed-env-open".equals(command)) { + openSignedEnvelope(args); + } else { + printUsage(); + throw new IllegalArgumentException("未知命令: " + command); + } + } + + /** + * 生成 SM2 密钥对并输出 Base64。 + * + * @param 无参数。 + * @return 无返回值。 + * @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 。 + * @return 无返回值,标准输出为信封 Base64。 + * @throws Exception 参数错误、密钥解析失败或加密失败时抛出。 + */ + private static void createEnvelope(String[] args) throws Exception { + requireArgs(args, 3, "env-create "); + 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 。 + * @return 无返回值,标准输出为 UTF-8 原文。 + * @throws Exception 参数错误、密钥解析失败或解密失败时抛出。 + */ + private static void openEnvelope(String[] args) throws Exception { + requireArgs(args, 3, "env-open "); + 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 。 + * @return 无返回值,标准输出为信封 Base64。 + * @throws Exception 参数错误、密钥解析失败、签名或加密失败时抛出。 + */ + private static void createSignedEnvelope(String[] args) throws Exception { + requireArgs(args, 4, "signed-env-create "); + 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 。 + * @return 无返回值,标准输出为验签结果和 UTF-8 原文。 + * @throws Exception 参数错误、密钥解析失败、解密或验签失败时抛出。 + */ + private static void openSignedEnvelope(String[] args) throws Exception { + requireArgs(args, 4, "signed-env-open "); + 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); + } + } + + /** + * 读取文本参数。 + * + * @param value 文本值;以 @ 开头时表示从文件读取 UTF-8 文本。 + * @return UTF-8 文本字节。 + * @throws Exception 文件读取失败时抛出。 + */ + private static byte[] readTextArg(String value) throws Exception { + if (value.startsWith("@")) { + return Files.readAllBytes(Paths.get(value.substring(1))); + } + return value.getBytes(StandardCharsets.UTF_8); + } + + /** + * 打印统一入口用法。 + * + * @param 无参数。 + * @return 无返回值。 + */ + private static void printUsage() { + System.out.println("统一入口: java -jar sectool-*-jar-with-dependencies.jar [args]"); + System.out.println("命令:"); + System.out.println(" genkey"); + System.out.println(" env-create "); + System.out.println(" env-open "); + System.out.println(" signed-env-create "); + System.out.println(" signed-env-open "); + } +} diff --git a/src/main/java/com/sunyard/cisd/device/tool/SM2SignedEnvelopeUtil.java b/src/main/java/com/sunyard/cisd/device/tool/SM2SignedEnvelopeUtil.java index 230deda..7b1e60b 100644 --- a/src/main/java/com/sunyard/cisd/device/tool/SM2SignedEnvelopeUtil.java +++ b/src/main/java/com/sunyard/cisd/device/tool/SM2SignedEnvelopeUtil.java @@ -80,6 +80,27 @@ public class SM2SignedEnvelopeUtil { 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 { byte[] sm3Digest = calculateSM3Digest(plaintext); @@ -100,6 +121,36 @@ public class SM2SignedEnvelopeUtil { signature.update(signedAttrsEncoded); 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(); byte[] iv = new byte[12]; random.nextBytes(iv); @@ -123,26 +174,46 @@ public class SM2SignedEnvelopeUtil { baos.write(encryptedKey); baos.write(iv); baos.write(encryptedData); - - 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; + return baos.toByteArray(); } - 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 { @@ -211,38 +282,48 @@ 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 { + if (envelopeData.length < 6) { + throw new IllegalArgumentException("?????????????"); + } + int sigLen = ((envelopeData[0] & 0xFF) << 24) | ((envelopeData[1] & 0xFF) << 16) | ((envelopeData[2] & 0xFF) << 8) | (envelopeData[3] & 0xFF); + if (sigLen <= 0 || envelopeData.length < 4 + sigLen) { + throw new IllegalArgumentException("?????????????"); + } + byte[] signatureValue = new byte[sigLen]; System.arraycopy(envelopeData, 4, signatureValue, 0, sigLen); byte[] encryptedContent = new byte[envelopeData.length - 4 - sigLen]; System.arraycopy(envelopeData, 4 + sigLen, encryptedContent, 0, encryptedContent.length); - 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[] plaintext = decryptContent(encryptedContent, decryptionKey); byte[] messageDigest = calculateSM3Digest(plaintext); byte[] signedAttrsEncoded = buildSignedAttrs(messageDigest); @@ -268,58 +349,4 @@ public class SM2SignedEnvelopeUtil { 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("=== 验证失败 ==="); - } - } } \ No newline at end of file diff --git a/src/test/java/com/sunyard/cisd/SM2EnvelopeTest.java b/src/test/java/com/sunyard/cisd/SM2EnvelopeTest.java new file mode 100644 index 0000000..6e1c846 --- /dev/null +++ b/src/test/java/com/sunyard/cisd/SM2EnvelopeTest.java @@ -0,0 +1,52 @@ +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); + } +}