农行验签不过

This commit is contained in:
cheney 2026-05-22 10:17:02 +08:00
parent 970c631e52
commit 72eff5f4a4
4 changed files with 900 additions and 135 deletions

27
.gitignore vendored
View File

@ -1,26 +1 @@
/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
/target/

View File

@ -1,15 +1,17 @@
package com.sunyard.cisd.device.tool;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.cms.*;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.x509.Certificate;
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.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
@ -129,6 +131,568 @@ public class CMSUtil {
return (X509Certificate) cf.generateCertificate(new java.io.ByteArrayInputStream(certHolder.getEncoded()));
}
public static class SignedAndEnvelopedVerifyResult {
public final boolean signatureValid;
public final byte[] messageDigest;
public final X509Certificate signerCertificate;
public final String signerSubjectDN;
public SignedAndEnvelopedVerifyResult(boolean signatureValid, byte[] messageDigest,
X509Certificate signerCertificate, String signerSubjectDN) {
this.signatureValid = signatureValid;
this.messageDigest = messageDigest;
this.signerCertificate = signerCertificate;
this.signerSubjectDN = signerSubjectDN;
}
}
public static SignedAndEnvelopedVerifyResult verifySignedAndEnvelopedData(String base64CmsData) throws Exception {
byte[] cmsData = Base64.getDecoder().decode(base64CmsData);
return verifySignedAndEnvelopedData(cmsData);
}
private static X509Certificate tryParseCertificate(ASN1Encodable elem) {
try {
if (elem instanceof ASN1Sequence) {
Certificate cert = Certificate.getInstance(elem);
byte[] certEncoded = cert.getEncoded();
return loadCertificateFromBytes(certEncoded);
}
} catch (Exception e) {
}
return null;
}
public static SignedAndEnvelopedVerifyResult verifySignedAndEnvelopedData(byte[] cmsData) throws Exception {
System.out.println("[步骤1] 解析 ContentInfo...");
ASN1InputStream asn1InputStream = new ASN1InputStream(cmsData);
ContentInfo contentInfo = ContentInfo.getInstance(asn1InputStream.readObject());
System.out.println(" ContentInfo 解析成功");
System.out.println(" OID: " + contentInfo.getContentType().getId());
if (!contentInfo.getContentType().equals(CMSObjectIdentifiers.signedAndEnvelopedData)) {
throw new Exception("不是 SignedAndEnvelopedData 格式");
}
System.out.println("[步骤2] 解析 SignedAndEnvelopedData 结构...");
ASN1Primitive contentPrimitive = contentInfo.getContent().toASN1Primitive();
System.out.println(" Content 类型: " + contentPrimitive.getClass().getSimpleName());
ASN1Sequence saeSequence = ASN1Sequence.getInstance(contentPrimitive);
System.out.println(" SignedAndEnvelopedData 包含 " + saeSequence.size() + " 个元素");
int index = 0;
ASN1Integer version = ASN1Integer.getInstance(saeSequence.getObjectAt(index++).toASN1Primitive());
System.out.println(" 版本: " + version.getValue());
ASN1Set recipientInfos = ASN1Set.getInstance(saeSequence.getObjectAt(index++).toASN1Primitive());
System.out.println(" RecipientInfos 数量: " + recipientInfos.size());
System.out.println(" 检查后续元素,寻找证书和签名信息...");
X509Certificate signerCert = null;
byte[] signedAttrsEncoded = null;
byte[] signatureValue = null;
for (int i = index; i < saeSequence.size(); i++) {
ASN1Encodable elemEncodable = saeSequence.getObjectAt(i);
ASN1Primitive elem = elemEncodable.toASN1Primitive();
System.out.println("\n 检查第" + (i+1) + "个元素,类型: " + elem.getClass().getSimpleName());
if (elem instanceof ASN1Set) {
ASN1Set set = ASN1Set.getInstance(elem);
System.out.println(" DLSet 包含 " + set.size() + " 个元素");
for (int si = 0; si < set.size(); si++) {
ASN1Encodable setElem = set.getObjectAt(si);
System.out.println(" DLSet[" + si + "] 类型: " + setElem.toASN1Primitive().getClass().getSimpleName());
X509Certificate cert = tryParseCertificate(setElem);
if (cert != null) {
signerCert = cert;
System.out.println(" 从DLSet[" + si + "] 找到证书!");
System.out.println(" 证书主题: " + signerCert.getSubjectX500Principal().getName());
break;
}
if (setElem.toASN1Primitive() instanceof ASN1Sequence) {
ASN1Sequence innerSeq = ASN1Sequence.getInstance(setElem);
for (int j = 0; j < innerSeq.size(); j++) {
cert = tryParseCertificate(innerSeq.getObjectAt(j));
if (cert != null) {
signerCert = cert;
System.out.println(" 从DLSet[" + si + "]的嵌套序列找到证书!");
System.out.println(" 证书主题: " + signerCert.getSubjectX500Principal().getName());
break;
}
}
}
}
}
if (elem instanceof ASN1Sequence) {
ASN1Sequence seq = ASN1Sequence.getInstance(elem);
System.out.println(" 序列包含 " + seq.size() + " 个元素");
for (int j = 0; j < seq.size(); j++) {
ASN1Encodable subElem = seq.getObjectAt(j);
ASN1Primitive subPrim = subElem.toASN1Primitive();
System.out.println(" 子元素 " + j + ": " + subPrim.getClass().getSimpleName());
X509Certificate cert = tryParseCertificate(subElem);
if (cert != null) {
signerCert = cert;
System.out.println(" 从子元素 " + j + " 找到证书!");
System.out.println(" 证书主题: " + signerCert.getSubjectX500Principal().getName());
break;
}
if (subPrim instanceof ASN1Sequence) {
ASN1Sequence subSeq = ASN1Sequence.getInstance(subPrim);
for (int k = 0; k < subSeq.size(); k++) {
cert = tryParseCertificate(subSeq.getObjectAt(k));
if (cert != null) {
signerCert = cert;
System.out.println(" 从子元素 " + j + " 的嵌套序列[" + k + "]找到证书!");
System.out.println(" 证书主题: " + signerCert.getSubjectX500Principal().getName());
break;
}
}
}
if (signerCert != null) break;
}
}
if (elem instanceof ASN1TaggedObject) {
ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(elem);
System.out.println(" TaggedObject 标签编号: " + tagged.getTagNo());
ASN1Primitive innerPrim = tagged.getObject();
System.out.println(" 内部对象类型: " + innerPrim.getClass().getSimpleName());
X509Certificate cert = tryParseCertificate(tagged.getObject());
if (cert != null) {
signerCert = cert;
System.out.println(" 从TaggedObject找到证书!");
System.out.println(" 证书主题: " + signerCert.getSubjectX500Principal().getName());
}
if (innerPrim instanceof ASN1Sequence) {
ASN1Sequence innerSeq = ASN1Sequence.getInstance(innerPrim);
for (int j = 0; j < innerSeq.size(); j++) {
cert = tryParseCertificate(innerSeq.getObjectAt(j));
if (cert != null) {
signerCert = cert;
System.out.println(" 从TaggedObject的内部序列[" + j + "]找到证书!");
System.out.println(" 证书主题: " + signerCert.getSubjectX500Principal().getName());
break;
}
}
}
}
if (signerCert != null) break;
}
if (signerCert == null) {
throw new Exception("数字信封中未找到有效的签名者证书");
}
System.out.println("\n[步骤3] 提取签名者证书 - 成功");
System.out.println(" 签名者主题: " + signerCert.getSubjectX500Principal().getName());
System.out.println(" 公钥算法: " + signerCert.getPublicKey().getAlgorithm());
System.out.println(" 证书签名算法: " + signerCert.getSigAlgName());
System.out.println("\n[步骤4] 解析 SignerInfo...");
for (int i = index; i < saeSequence.size(); i++) {
ASN1Primitive elem = saeSequence.getObjectAt(i).toASN1Primitive();
if (elem instanceof ASN1Set) {
ASN1Set signerInfosSet = ASN1Set.getInstance(elem);
System.out.println(" SignerInfos 包含 " + signerInfosSet.size() + " 个元素");
for (int si = 0; si < signerInfosSet.size(); si++) {
System.out.println("\n 处理第 " + (si+1) + " 个 SignerInfo...");
ASN1Encodable signerInfoEncodable = signerInfosSet.getObjectAt(si);
ASN1Primitive signerInfoPrim = signerInfoEncodable.toASN1Primitive();
System.out.println(" SignerInfo 原始类型: " + signerInfoPrim.getClass().getSimpleName());
String signerInfoPrimClassName = signerInfoPrim.getClass().getName();
System.out.println(" SignerInfo 原始类型: " + signerInfoPrimClassName);
System.out.println(" 是 ASN1Sequence: " + (signerInfoPrim instanceof ASN1Sequence));
System.out.println(" 是 ASN1TaggedObject: " + (signerInfoPrim instanceof ASN1TaggedObject));
System.out.println(" 类名包含 TaggedObject: " + signerInfoPrimClassName.contains("TaggedObject"));
if (signerInfoPrim instanceof ASN1Sequence) {
ASN1Sequence signerInfoSeq = ASN1Sequence.getInstance(signerInfoPrim);
System.out.println(" SignerInfo 包含 " + signerInfoSeq.size() + " 个元素");
for (int el = 0; el < signerInfoSeq.size(); el++) {
ASN1Encodable elEnc = signerInfoSeq.getObjectAt(el);
System.out.println(" 元素 " + el + ": " + elEnc.toASN1Primitive().getClass().getSimpleName());
}
if (signerInfoSeq.size() < 4) {
System.out.println(" SignerInfo 元素数量不足,跳过");
continue;
}
int signerIndex = 0;
ASN1Integer signerVersion = ASN1Integer.getInstance(signerInfoSeq.getObjectAt(signerIndex++).toASN1Primitive());
System.out.println(" Signer 版本: " + signerVersion.getValue());
System.out.println(" SignerIdentifier 解析成功");
ASN1Sequence digestAlgorithm = ASN1Sequence.getInstance(signerInfoSeq.getObjectAt(signerIndex++).toASN1Primitive());
System.out.println(" DigestAlgorithm 解析成功");
ASN1Encodable authAttrsData = null;
System.out.println(" 检查 AuthenticatedAttributes (signedAttrs)...");
boolean foundAuthenticatedAttributes = false;
while (signerIndex < signerInfoSeq.size() && !foundAuthenticatedAttributes) {
ASN1Encodable authAttrsObj = signerInfoSeq.getObjectAt(signerIndex);
String elemClassName = authAttrsObj.getClass().getName();
boolean isTaggedObject = elemClassName.contains("TaggedObject");
boolean isSet = (authAttrsObj instanceof ASN1Set);
boolean isSequence = (authAttrsObj instanceof ASN1Sequence);
System.out.println(" 检查元素 " + signerIndex + " 类型: " + elemClassName);
if (isSet) {
authAttrsData = authAttrsObj;
System.out.println(" 发现 SET 类型 AuthenticatedAttributes");
foundAuthenticatedAttributes = true;
} else if (isTaggedObject) {
ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(authAttrsObj);
int tagNo = tagged.getTagNo();
System.out.println(" 发现 TaggedObject标签: " + tagNo);
ASN1Encodable innerObj = tagged.getObject();
System.out.println(" 内部对象类型: " + innerObj.getClass().getName());
if (innerObj instanceof ASN1Set) {
authAttrsData = innerObj;
System.out.println(" 从 TaggedObject[" + tagNo + "] 提取 SET");
foundAuthenticatedAttributes = true;
} else if (innerObj instanceof ASN1Sequence) {
ASN1Sequence innerSeq = ASN1Sequence.getInstance(innerObj);
System.out.println(" TaggedObject[" + tagNo + "] 内部是 Sequence包含: " + innerSeq.size());
if (tagNo == 0 && innerSeq.size() > 0) {
System.out.println(" 从标签[0]的 Sequence 提取 AuthenticatedAttributes包含: " + innerSeq.size() + " 个属性");
// 使用原始的 DLTaggedObject 获取 DER 编码而不是从 innerSeq 重新编码
authAttrsData = tagged;
foundAuthenticatedAttributes = true;
signerIndex++;
System.out.println(" 找到 AuthenticatedAttributessignerIndex 递增到: " + signerIndex);
} else {
System.out.println(" 标签[" + tagNo + "] 不是 AuthenticatedAttributes (应为[0]),跳过");
}
}
if (!foundAuthenticatedAttributes) {
signerIndex++;
}
} else if (isSequence) {
System.out.println(" Sequence 类型: " + elemClassName + ",跳过");
signerIndex++;
} else {
System.out.println(" 未知类型: " + elemClassName + ",跳过");
signerIndex++;
}
}
if (signerIndex >= signerInfoSeq.size()) {
System.out.println(" AuthenticatedAttributes 为空,跳过");
continue;
}
ASN1Sequence digestEncryptionAlgorithm = ASN1Sequence.getInstance(signerInfoSeq.getObjectAt(signerIndex++).toASN1Primitive());
System.out.println(" DigestEncryptionAlgorithm 解析成功");
if (signerIndex >= signerInfoSeq.size()) {
System.out.println(" EncryptedDigest 不存在");
continue;
}
ASN1OctetString encryptedDigest = ASN1OctetString.getInstance(signerInfoSeq.getObjectAt(signerIndex));
System.out.println(" EncryptedDigest 长度: " + encryptedDigest.getOctets().length + " bytes");
if (authAttrsData == null) {
System.out.println(" AuthenticatedAttributes 为空,跳过验签");
continue;
}
System.out.println("\n[步骤5] 提取待签名数据 (signedAttrs DER 编码)...");
signedAttrsEncoded = authAttrsData.toASN1Primitive().getEncoded(ASN1Encoding.DER);
System.out.println(" signedAttrs DER 编码长度: " + signedAttrsEncoded.length + " bytes");
System.out.println(" signedAttrs Hex: " + bytesToHex(signedAttrsEncoded));
System.out.println("\n[步骤6] 提取原文 Hash (message-digest 属性,仅打印不参与运算)...");
byte[] messageDigest = null;
ASN1EncodableVector authAttrsVector = new ASN1EncodableVector();
if (authAttrsData instanceof ASN1Set) {
ASN1Set authSet = (ASN1Set) authAttrsData;
for (int j = 0; j < authSet.size(); j++) {
authAttrsVector.add(authSet.getObjectAt(j));
}
} else if (authAttrsData instanceof ASN1Sequence) {
ASN1Sequence authSeq = (ASN1Sequence) authAttrsData;
for (int j = 0; j < authSeq.size(); j++) {
authAttrsVector.add(authSeq.getObjectAt(j));
}
} else if (authAttrsData instanceof ASN1TaggedObject) {
ASN1TaggedObject tagged = (ASN1TaggedObject) authAttrsData;
ASN1Primitive inner = tagged.getObject();
if (inner instanceof ASN1Sequence) {
ASN1Sequence authSeq = (ASN1Sequence) inner;
for (int j = 0; j < authSeq.size(); j++) {
authAttrsVector.add(authSeq.getObjectAt(j));
}
}
}
for (int j = 0; j < authAttrsVector.size(); j++) {
ASN1Sequence attrSeq = ASN1Sequence.getInstance(authAttrsVector.get(j).toASN1Primitive());
if (attrSeq.size() < 2) {
continue;
}
ASN1ObjectIdentifier attrType = ASN1ObjectIdentifier.getInstance(attrSeq.getObjectAt(0));
if (attrType.equals(CMSAttributes.messageDigest)) {
ASN1Encodable attrValues = attrSeq.getObjectAt(1);
ASN1Primitive attrValuesPrim = attrValues.toASN1Primitive();
if (attrValuesPrim instanceof ASN1Set) {
ASN1Set valuesSet = ASN1Set.getInstance(attrValuesPrim);
if (valuesSet.size() > 0) {
messageDigest = ASN1OctetString.getInstance(valuesSet.getObjectAt(0)).getOctets();
System.out.println(" message-digest 值 (Hex): " + bytesToHex(messageDigest));
System.out.println(" message-digest 长度: " + messageDigest.length + " bytes");
}
} else if (attrValuesPrim instanceof ASN1OctetString) {
messageDigest = ((ASN1OctetString) attrValuesPrim).getOctets();
System.out.println(" message-digest 值 (Hex): " + bytesToHex(messageDigest));
System.out.println(" message-digest 长度: " + messageDigest.length + " bytes");
}
}
}
signatureValue = encryptedDigest.getOctets();
System.out.println("\n[步骤7] 使用 SM2 算法验签 signedAttrs DER 编码...");
System.out.println(" DigestEncryptionAlgorithm OID: " +
((ASN1ObjectIdentifier)digestEncryptionAlgorithm.getObjectAt(0)).getId());
System.out.println(" 签名值长度: " + signatureValue.length + " bytes");
System.out.println(" 签名值 (Hex): " + bytesToHex(signatureValue));
System.out.println(" signedAttrs 数据类型: " + authAttrsData.getClass().getName());
String[] algNames = {"SM3withSM2", "SHA256withSM2", "SM2"};
boolean valid = false;
for (String algName : algNames) {
try {
Signature signature = Signature.getInstance(algName, "BC");
signature.initVerify(signerCert.getPublicKey());
signature.update(signedAttrsEncoded);
valid = signature.verify(signatureValue);
System.out.println(" 算法 " + algName + " 验签结果: " + (valid ? "成功" : "失败"));
if (valid) break;
} catch (Exception e) {
System.out.println(" 算法 " + algName + " 失败: " + e.getMessage());
}
}
if (!valid) {
System.out.println("\n [调试] 尝试使用原始签名值格式验签...");
try {
Signature signature = Signature.getInstance("SM3withSM2", "BC");
signature.initVerify(signerCert.getPublicKey());
signature.update(signedAttrsEncoded);
byte[] rawSig = new byte[64];
if (signatureValue.length >= 65 && signatureValue[0] == 0x30) {
int offset = signatureValue[1] >= 0x81 ? (signatureValue[1] == 0x81 ? 3 : 2) : 2;
if (signatureValue.length >= offset + 32 + 2) {
System.arraycopy(signatureValue, offset + 1, rawSig, 0, 32);
System.arraycopy(signatureValue, offset + 33, rawSig, 32, 32);
valid = signature.verify(rawSig);
System.out.println(" 原始格式验签结果: " + (valid ? "成功" : "失败"));
}
}
} catch (Exception e) {
System.out.println(" 原始格式验签失败: " + e.getMessage());
}
}
return new SignedAndEnvelopedVerifyResult(valid, messageDigest, signerCert,
signerCert.getSubjectX500Principal().getName());
} else if (signerInfoPrim instanceof ASN1TaggedObject) {
ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(signerInfoPrim);
System.out.println(" TaggedObject 标签: " + tagged.getTagNo());
System.out.println(" 内部对象类型: " + tagged.getObject().getClass().getSimpleName());
ASN1Primitive inner = tagged.getObject();
if (inner instanceof ASN1Sequence) {
ASN1Sequence signerInfoSeq = ASN1Sequence.getInstance(inner);
System.out.println(" SignerInfo 包含 " + signerInfoSeq.size() + " 个元素");
if (signerInfoSeq.size() >= 4) {
int signerIndex = 0;
ASN1Integer signerVersion = ASN1Integer.getInstance(signerInfoSeq.getObjectAt(signerIndex++).toASN1Primitive());
System.out.println(" Signer 版本: " + signerVersion.getValue());
System.out.println(" SignerIdentifier 解析成功");
ASN1Sequence digestAlgorithm = ASN1Sequence.getInstance(signerInfoSeq.getObjectAt(signerIndex++).toASN1Primitive());
System.out.println(" DigestAlgorithm 解析成功");
ASN1Set authenticatedAttributes = null;
System.out.println(" 检查 AuthenticatedAttributes (signedAttrs)...");
while (signerIndex < signerInfoSeq.size()) {
ASN1Encodable authAttrsObj = signerInfoSeq.getObjectAt(signerIndex);
ASN1Primitive authAttrsPrim = authAttrsObj.toASN1Primitive();
System.out.println(" 检查元素 " + signerIndex + " 类型: " + authAttrsPrim.getClass().getName());
if (authAttrsPrim instanceof ASN1TaggedObject || authAttrsPrim.getClass().getName().contains("TaggedObject")) {
ASN1TaggedObject authTagged = ASN1TaggedObject.getInstance(authAttrsObj);
System.out.println(" 标签编号: " + authTagged.getTagNo());
ASN1Primitive authInner = authTagged.getObject();
System.out.println(" 内部对象类型: " + authInner.getClass().getName());
if (authInner instanceof ASN1Set) {
authenticatedAttributes = ASN1Set.getInstance(authInner);
System.out.println(" AuthenticatedAttributes (tagged) 发现 " + authenticatedAttributes.size() + " 个属性");
signerIndex++;
break;
} else if (authInner instanceof ASN1Sequence) {
authenticatedAttributes = ASN1Set.getInstance(authInner);
System.out.println(" AuthenticatedAttributes (tagged sequence) 发现 " + authenticatedAttributes.size() + " 个属性");
signerIndex++;
break;
}
} else if (authAttrsPrim instanceof ASN1Set) {
authenticatedAttributes = ASN1Set.getInstance(authAttrsPrim);
System.out.println(" AuthenticatedAttributes 发现 " + authenticatedAttributes.size() + " 个属性");
signerIndex++;
break;
} else if (authAttrsPrim instanceof ASN1Sequence) {
authenticatedAttributes = ASN1Set.getInstance(authAttrsPrim);
System.out.println(" AuthenticatedAttributes (sequence) 发现 " + authenticatedAttributes.size() + " 个属性");
signerIndex++;
break;
} else {
System.out.println(" 不是 AuthenticatedAttributes 类型: " + authAttrsPrim.getClass().getName() + ",跳过");
signerIndex++;
}
}
if (signerIndex >= signerInfoSeq.size()) {
System.out.println(" AuthenticatedAttributes 为空,跳过");
continue;
}
ASN1Sequence digestEncryptionAlgorithm = ASN1Sequence.getInstance(signerInfoSeq.getObjectAt(signerIndex++).toASN1Primitive());
System.out.println(" DigestEncryptionAlgorithm 解析成功");
if (signerIndex >= signerInfoSeq.size()) {
System.out.println(" EncryptedDigest 不存在");
continue;
}
ASN1Encodable sigElement = signerInfoSeq.getObjectAt(signerIndex);
ASN1Primitive sigPrim = sigElement.toASN1Primitive();
System.out.println(" 签名元素类型: " + sigPrim.getClass().getSimpleName());
byte[] encryptedDigestBytes = null;
if (sigPrim instanceof ASN1OctetString) {
encryptedDigestBytes = ((ASN1OctetString) sigPrim).getOctets();
} else if (sigPrim instanceof ASN1TaggedObject) {
ASN1TaggedObject sigTagged = ASN1TaggedObject.getInstance(sigPrim);
if (sigTagged.getObject() instanceof ASN1OctetString) {
encryptedDigestBytes = ((ASN1OctetString) sigTagged.getObject()).getOctets();
}
} else if (sigPrim instanceof ASN1Sequence) {
ASN1Sequence sigSeq = ASN1Sequence.getInstance(sigPrim);
if (sigSeq.size() > 0) {
ASN1Encodable lastElem = sigSeq.getObjectAt(sigSeq.size() - 1);
if (lastElem instanceof ASN1OctetString) {
encryptedDigestBytes = ((ASN1OctetString) lastElem).getOctets();
}
}
}
if (encryptedDigestBytes == null) {
System.out.println(" 无法解析签名值");
continue;
}
System.out.println(" EncryptedDigest 长度: " + encryptedDigestBytes.length + " bytes");
if (authenticatedAttributes == null) {
System.out.println(" AuthenticatedAttributes 为空,跳过验签");
continue;
}
System.out.println("\n[步骤5] 提取待签名数据 (signedAttrs DER 编码)...");
signedAttrsEncoded = authenticatedAttributes.getEncoded(ASN1Encoding.DER);
System.out.println(" signedAttrs DER 编码长度: " + signedAttrsEncoded.length + " bytes");
System.out.println(" signedAttrs Hex: " + bytesToHex(signedAttrsEncoded));
System.out.println("\n[步骤6] 提取原文 Hash (message-digest 属性,仅打印不参与运算)...");
byte[] messageDigest = null;
for (int j = 0; j < authenticatedAttributes.size(); j++) {
ASN1Sequence attrSeq = ASN1Sequence.getInstance(authenticatedAttributes.getObjectAt(j).toASN1Primitive());
if (attrSeq.size() < 2) {
continue;
}
ASN1ObjectIdentifier attrType = ASN1ObjectIdentifier.getInstance(attrSeq.getObjectAt(0));
if (attrType.equals(CMSAttributes.messageDigest)) {
ASN1Encodable attrValues = attrSeq.getObjectAt(1);
ASN1Primitive attrValuesPrim = attrValues.toASN1Primitive();
if (attrValuesPrim instanceof ASN1Set) {
ASN1Set valuesSet = ASN1Set.getInstance(attrValuesPrim);
if (valuesSet.size() > 0) {
messageDigest = ASN1OctetString.getInstance(valuesSet.getObjectAt(0)).getOctets();
System.out.println(" message-digest 值 (Hex): " + bytesToHex(messageDigest));
System.out.println(" message-digest 长度: " + messageDigest.length + " bytes");
}
} else if (attrValuesPrim instanceof ASN1OctetString) {
messageDigest = ((ASN1OctetString) attrValuesPrim).getOctets();
System.out.println(" message-digest 值 (Hex): " + bytesToHex(messageDigest));
System.out.println(" message-digest 长度: " + messageDigest.length + " bytes");
}
}
}
signatureValue = encryptedDigestBytes;
System.out.println("\n[步骤7] 使用 SM2 算法验签 signedAttrs DER 编码...");
Signature signature = Signature.getInstance("SM3withSM2", "BC");
signature.initVerify(signerCert.getPublicKey());
signature.update(signedAttrsEncoded);
System.out.println(" 签名值长度: " + signatureValue.length + " bytes");
System.out.println(" 签名值 (Hex): " + bytesToHex(signatureValue));
boolean valid = signature.verify(signatureValue);
System.out.println(" 验签结果: " + (valid ? "成功" : "失败"));
return new SignedAndEnvelopedVerifyResult(valid, messageDigest, signerCert,
signerCert.getSubjectX500Principal().getName());
}
}
} else {
System.out.println(" 未知类型,跳过");
}
}
}
}
throw new Exception("未找到有效的签名信息");
}
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("用法: java CMSUtil <base64_cms_data>");
@ -151,7 +715,16 @@ public class CMSUtil {
System.out.println("这是一个数字信封(EnvelopedData),需要私钥解密后才能验证签名");
} else if (type.equals("SignedAndEnvelopedData")) {
System.out.println("这是一个SignedAndEnvelopedData同时签名和加密");
System.out.println("需要使用对应的私钥解密后才能验证签名");
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) {
@ -159,4 +732,12 @@ public class CMSUtil {
e.printStackTrace();
}
}
public static String bytesToHex(byte[] data) {
StringBuilder sb = new StringBuilder();
for (byte b : data) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}

View File

@ -1,12 +1,9 @@
package com.sunyard.cisd.device.tool;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.cms.*;
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;
@ -84,30 +81,25 @@ public class SM2SignedEnvelopeUtil {
}
public static byte[] createSignedAndEnvelopedData(byte[] plaintext, PrivateKey signingKey, PublicKey encryptionPublicKey) throws Exception {
byte[] signature = signData(plaintext, signingKey);
byte[] sm3Digest = calculateSM3Digest(plaintext);
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();
ASN1EncodableVector signedAttrsVector = new ASN1EncodableVector();
Attribute messageDigestAttr = new Attribute(CMSAttributes.messageDigest,
new DERSet(new DEROctetString(sm3Digest)));
signedAttrsVector.add(messageDigestAttr);
byte[] encryptedContent = encryptData(signedContent, encryptionPublicKey);
Attribute contentTypeAttr = new Attribute(CMSAttributes.contentType,
new DERSet(CMSObjectIdentifiers.data));
signedAttrsVector.add(contentTypeAttr);
return encryptedContent;
}
DERSet signedAttrs = new DERSet(signedAttrsVector);
byte[] signedAttrsEncoded = signedAttrs.getEncoded(ASN1Encoding.DER);
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();
}
signature.initSign(signingKey);
signature.update(signedAttrsEncoded);
byte[] signatureValue = signature.sign();
private static byte[] encryptData(byte[] data, PublicKey publicKey) throws Exception {
SecureRandom random = new SecureRandom();
byte[] iv = new byte[12];
random.nextBytes(iv);
@ -119,53 +111,127 @@ public class SM2SignedEnvelopeUtil {
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);
byte[] encryptedData = sm4Cipher.doFinal(plaintext);
Cipher ecCipher = Cipher.getInstance("ECIES", PROVIDER);
ecCipher.init(Cipher.ENCRYPT_MODE, publicKey);
ecCipher.init(Cipher.ENCRYPT_MODE, encryptionPublicKey);
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((byte)(encryptedKey.length >> 8));
baos.write((byte)encryptedKey.length);
baos.write(encryptedKey);
baos.write(iv);
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);
}
public static class VerifyResult {
public final boolean signatureValid;
public final byte[] messageDigest;
public final String digestAlgorithm;
public VerifyResult(boolean signatureValid, byte[] messageDigest, String digestAlgorithm) {
this.signatureValid = signatureValid;
this.messageDigest = messageDigest;
this.digestAlgorithm = digestAlgorithm;
}
}
public static boolean verifySignatureWithAttrs(byte[] signatureValue, byte[] signedAttrsEncoded, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM, PROVIDER);
signature.initVerify(publicKey);
signature.update(signedAttrsEncoded);
return signature.verify(signatureValue);
}
public static byte[] buildSignedAttrs(byte[] messageDigest) {
ASN1EncodableVector signedAttrsVector = new ASN1EncodableVector();
Attribute contentTypeAttr = new Attribute(CMSAttributes.contentType,
new DERSet(CMSObjectIdentifiers.data));
signedAttrsVector.add(contentTypeAttr);
Attribute messageDigestAttr = new Attribute(CMSAttributes.messageDigest,
new DERSet(new DEROctetString(messageDigest)));
signedAttrsVector.add(messageDigestAttr);
DERSet signedAttrs = new DERSet(signedAttrsVector);
try {
return signedAttrs.getEncoded(ASN1Encoding.DER);
} catch (Exception e) {
return null;
}
}
public static byte[] extractMessageDigestFromAttrs(byte[] signedAttrsEncoded) throws Exception {
ASN1InputStream asn1InputStream = new ASN1InputStream(signedAttrsEncoded);
ASN1Set signedAttrs = ASN1Set.getInstance(asn1InputStream.readObject());
for (int i = 0; i < signedAttrs.size(); i++) {
Attribute attr = Attribute.getInstance(signedAttrs.getObjectAt(i));
if (attr.getAttrType().equals(CMSAttributes.messageDigest)) {
return ASN1OctetString.getInstance(
attr.getAttrValues().getObjectAt(0)).getOctets();
}
}
return null;
}
public static class DecryptedResult {
public final byte[] plaintext;
public final byte[] signature;
public final boolean signatureValid;
public final byte[] messageDigest;
public DecryptedResult(byte[] plaintext, byte[] signature, boolean signatureValid) {
public DecryptedResult(byte[] plaintext, byte[] signature, boolean signatureValid, byte[] messageDigest) {
this.plaintext = plaintext;
this.signature = signature;
this.signatureValid = signatureValid;
this.messageDigest = messageDigest;
}
}
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;
int sigLen = ((envelopeData[0] & 0xFF) << 24) |
((envelopeData[1] & 0xFF) << 16) |
((envelopeData[2] & 0xFF) << 8) |
(envelopeData[3] & 0xFF);
byte[] encryptedKey = new byte[encryptedKeyLen];
System.arraycopy(envelopeData, offset, encryptedKey, 0, encryptedKeyLen);
offset += encryptedKeyLen;
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(envelopeData, offset, iv, 0, 12);
offset += 12;
System.arraycopy(encryptedContent, 2 + keyLen, iv, 0, 12);
byte[] encryptedData = new byte[envelopeData.length - offset];
System.arraycopy(envelopeData, offset, encryptedData, 0, encryptedData.length);
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);
@ -176,29 +242,14 @@ public class SM2SignedEnvelopeUtil {
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);
byte[] plaintext = sm4Cipher.doFinal(encryptedData);
int signatureLength = ((signedContent[0] & 0xFF) << 24) |
((signedContent[1] & 0xFF) << 16) |
((signedContent[2] & 0xFF) << 8) |
(signedContent[3] & 0xFF);
byte[] messageDigest = calculateSM3Digest(plaintext);
byte[] signedAttrsEncoded = buildSignedAttrs(messageDigest);
byte[] signature = new byte[signatureLength];
byte[] plaintext = new byte[signedContent.length - 4 - signatureLength];
boolean signatureValid = verifySignatureWithAttrs(signatureValue, signedAttrsEncoded, signingPublicKey);
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);
return new DecryptedResult(plaintext, signatureValue, signatureValid, messageDigest);
}
public static String toBase64(byte[] data) {
@ -233,26 +284,42 @@ public class SM2SignedEnvelopeUtil {
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("信封Base64: " + toBase64(envelope).substring(0, 80) + "...");
System.out.println();
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.signature));
System.out.println("消息摘要: " + bytesToHex(result.messageDigest));
System.out.println();
if (result.signatureValid) {
System.out.println("=== 验签成功 ===");
if (sigValid && result.signatureValid) {
System.out.println("=== 全部验证通过 ===");
} else {
System.out.println("=== 验签失败 ===");
System.out.println("=== 验失败 ===");
}
}
}

View File

@ -1,51 +1,193 @@
package com.sunyard.cisd;
import com.sunyard.cisd.device.tool.CMSUtil;
import com.sunyard.cisd.device.tool.CMSUtil.SignedAndEnvelopedVerifyResult;
import com.sunyard.cisd.device.tool.SM2SignedEnvelopeUtil;
import com.sunyard.cisd.device.tool.SM2SignedEnvelopeUtil.DecryptedResult;
import com.sunyard.cisd.device.tool.SM2SignedEnvelopeUtil.SM2KeyPair;
import com.sunyard.cisd.device.tool.SM3Util;
public class CMSSignatureTest {
public static void main(String[] args) {
try {
System.out.println("=== CMS 签名验签测试 ===");
System.out.println("=== CMS 签名验签测试套件 ===");
System.out.println();
String cbcCmsData = SM3Util.readFile("d:/workbench/sectool/cbc.cms").trim();
String icbcCmsData = SM3Util.readFile("d:/workbench/sectool/icbc.cms").trim();
// testSM2KeyPairGeneration();
// testCreateAndVerifyEnvelope();
testCMSFilesVerification();
System.out.println("--- 验证 cbc.cms ---");
String cbcType = CMSUtil.detectCMSType(cbcCmsData);
System.out.println("cbc.cms CMS类型: " + cbcType);
boolean cbcVerified = false;
if ("SignedData".equals(cbcType)) {
cbcVerified = CMSUtil.verifySignedData(cbcCmsData);
System.out.println("cbc.cms 验签结果: " + (cbcVerified ? "成功" : "失败"));
} else if ("SignedAndEnvelopedData".equals(cbcType)) {
System.out.println("cbc.cms 是SignedAndEnvelopedData类型同时签名和加密");
System.out.println("需要使用对应的私钥解密后才能验证签名");
}
System.out.println();
System.out.println("--- 验证 icbc.cms ---");
String icbcType = CMSUtil.detectCMSType(icbcCmsData);
System.out.println("icbc.cms CMS类型: " + icbcType);
boolean icbcVerified = false;
if ("SignedData".equals(icbcType)) {
icbcVerified = CMSUtil.verifySignedData(icbcCmsData);
System.out.println("icbc.cms 验签结果: " + (icbcVerified ? "成功" : "失败"));
} else if ("SignedAndEnvelopedData".equals(icbcType)) {
System.out.println("icbc.cms 是SignedAndEnvelopedData类型同时签名和加密");
System.out.println("需要使用对应的私钥解密后才能验证签名");
}
System.out.println();
System.out.println("=== 测试完成 ===");
System.out.println("=== 所有测试完成 ===");
} catch (Exception e) {
System.err.println("测试失败: " + e.getMessage());
System.err.println("\n测试失败: " + e.getMessage());
e.printStackTrace();
}
}
private static void testSM2KeyPairGeneration() throws Exception {
System.out.println("=== 1. SM2 密钥对生成测试 ===");
System.out.println();
SM2KeyPair keyPair = SM2SignedEnvelopeUtil.generateKeyPair();
System.out.println("密钥对生成成功");
System.out.println("私钥长度: " + keyPair.privateKeyBytes.length + " bytes");
System.out.println("公钥长度: " + keyPair.publicKeyBytes.length + " bytes");
System.out.println("私钥(Base64): " + truncate(keyPair.getPrivateKeyBase64(), 60));
System.out.println("公钥(Base64): " + truncate(keyPair.getPublicKeyBase64(), 60));
System.out.println();
}
private static void testCreateAndVerifyEnvelope() throws Exception {
System.out.println("=== 2. 数字信封创建与验证测试 ===");
System.out.println();
SM2KeyPair signingKeyPair = SM2SignedEnvelopeUtil.generateKeyPair();
SM2KeyPair encryptionKeyPair = SM2SignedEnvelopeUtil.generateKeyPair();
String testData = "这是测试数据 - Hello World! 你好世界!";
byte[] plaintext = testData.getBytes("UTF-8");
System.out.println("测试数据: " + testData);
System.out.println("数据长度: " + plaintext.length + " bytes");
System.out.println("数据SM3哈希: " + SM2SignedEnvelopeUtil.bytesToHex(
SM3Util.sm3Digest(plaintext)));
System.out.println();
System.out.println("创建带签名的数字信封...");
byte[] envelope = SM2SignedEnvelopeUtil.createSignedAndEnvelopedData(
plaintext, signingKeyPair.privateKey, encryptionKeyPair.publicKey);
System.out.println("信封创建成功,长度: " + envelope.length + " bytes");
System.out.println("信封(Base64): " + truncate(SM2SignedEnvelopeUtil.toBase64(envelope), 80));
System.out.println();
System.out.println("--- 不解密验证签名 (使用 signedAttrs) ---");
byte[] messageDigest = SM3Util.sm3Digest(plaintext);
byte[] signedAttrsEncoded = SM2SignedEnvelopeUtil.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 = SM2SignedEnvelopeUtil.verifySignatureWithAttrs(
signatureValue, signedAttrsEncoded, signingKeyPair.publicKey);
System.out.println("签名有效: " + (sigValid ? "" : ""));
byte[] extractedDigest = SM2SignedEnvelopeUtil.extractMessageDigestFromAttrs(signedAttrsEncoded);
System.out.println("提取的消息摘要: " + SM2SignedEnvelopeUtil.bytesToHex(extractedDigest));
System.out.println();
System.out.println("--- 解密并验证 ---");
DecryptedResult result = SM2SignedEnvelopeUtil.openAndVerifySignedEnvelopedData(
envelope, encryptionKeyPair.privateKey, signingKeyPair.publicKey);
System.out.println("解密后原文: " + new String(result.plaintext, "UTF-8"));
System.out.println("签名验证: " + (result.signatureValid ? "通过" : "失败"));
System.out.println("消息摘要: " + SM2SignedEnvelopeUtil.bytesToHex(result.messageDigest));
boolean integrityOk = java.util.Arrays.equals(result.messageDigest, messageDigest);
System.out.println("数据完整性: " + (integrityOk ? "通过" : "失败"));
System.out.println();
if (sigValid && result.signatureValid && integrityOk) {
System.out.println("✓ 数字信封测试全部通过");
} else {
System.out.println("✗ 数字信封测试失败");
}
System.out.println();
}
private static void testCMSFilesVerification() throws Exception {
System.out.println("=== 3. CMS 文件验证测试 ===");
System.out.println();
String cbcCmsData = null;
String icbcCmsData = null;
try {
cbcCmsData = SM3Util.readFile("d:/workbench/sectool/cbc.cms").trim();
System.out.println("已加载 cbc.cms");
} catch (Exception e) {
System.out.println("未找到 cbc.cms: " + e.getMessage());
}
try {
icbcCmsData = SM3Util.readFile("d:/workbench/sectool/icbc.cms").trim();
System.out.println("已加载 icbc.cms");
} catch (Exception e) {
System.out.println("未找到 icbc.cms: " + e.getMessage());
}
System.out.println();
if (cbcCmsData != null) {
System.out.println("--- 验证 cbc.cms ---");
try {
String cbcType = CMSUtil.detectCMSType(cbcCmsData);
System.out.println("CMS类型: " + cbcType);
if ("SignedData".equals(cbcType)) {
boolean verified = CMSUtil.verifySignedData(cbcCmsData);
System.out.println("验签结果: " + (verified ? "成功" : "失败"));
} else if ("SignedAndEnvelopedData".equals(cbcType)) {
System.out.println("类型: SignedAndEnvelopedData同时签名和加密");
System.out.println("说明: 数字信封内包含签名证书,可直接提取公钥验签");
System.out.println();
SignedAndEnvelopedVerifyResult result = CMSUtil.verifySignedAndEnvelopedData(cbcCmsData);
System.out.println("验签结果: " + (result.signatureValid ? "成功" : "失败"));
System.out.println("签名者证书主题: " + result.signerSubjectDN);
if (result.messageDigest != null) {
System.out.println("消息摘要(SM3): " + CMSUtil.bytesToHex(result.messageDigest));
}
} else {
System.out.println("未知类型: " + cbcType);
}
} catch (Exception e) {
System.err.println("验证失败: " + e.getMessage());
e.printStackTrace();
}
System.out.println();
}
if (icbcCmsData != null) {
System.out.println("--- 验证 icbc.cms ---");
try {
String icbcType = CMSUtil.detectCMSType(icbcCmsData);
System.out.println("CMS类型: " + icbcType);
if ("SignedData".equals(icbcType)) {
boolean verified = CMSUtil.verifySignedData(icbcCmsData);
System.out.println("验签结果: " + (verified ? "成功" : "失败"));
} else if ("SignedAndEnvelopedData".equals(icbcType)) {
System.out.println("类型: SignedAndEnvelopedData同时签名和加密");
System.out.println("说明: 数字信封内包含签名证书,可直接提取公钥验签");
System.out.println();
SignedAndEnvelopedVerifyResult result = CMSUtil.verifySignedAndEnvelopedData(icbcCmsData);
System.out.println("验签结果: " + (result.signatureValid ? "成功" : "失败"));
System.out.println("签名者证书主题: " + result.signerSubjectDN);
if (result.messageDigest != null) {
System.out.println("消息摘要(SM3): " + CMSUtil.bytesToHex(result.messageDigest));
}
} else {
System.out.println("未知类型: " + icbcType);
}
} catch (Exception e) {
System.err.println("验证失败: " + e.getMessage());
e.printStackTrace();
}
System.out.println();
}
}
private static String truncate(String str, int maxLength) {
if (str == null) return "";
if (str.length() <= maxLength) return str;
return str.substring(0, maxLength) + "...";
}
}