830 lines
49 KiB
Java
830 lines
49 KiB
Java
package com.sunyard.cisd.device.tool;
|
||
|
||
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.*;
|
||
import java.security.cert.CertificateFactory;
|
||
import java.security.cert.X509Certificate;
|
||
import java.util.Base64;
|
||
import java.util.Collection;
|
||
|
||
public class CMSUtil {
|
||
|
||
static {
|
||
Security.addProvider(new BouncyCastleProvider());
|
||
}
|
||
|
||
public static String detectCMSType(String base64CmsData) throws Exception {
|
||
byte[] cmsData = Base64.getDecoder().decode(base64CmsData);
|
||
return detectCMSType(cmsData);
|
||
}
|
||
|
||
public static String detectCMSType(byte[] cmsData) throws Exception {
|
||
try (ASN1InputStream asn1InputStream = new ASN1InputStream(cmsData)) {
|
||
ContentInfo contentInfo = ContentInfo.getInstance(asn1InputStream.readObject());
|
||
String typeOid = contentInfo.getContentType().getId();
|
||
|
||
if (typeOid.equals(CMSObjectIdentifiers.signedData.getId())) {
|
||
return "SignedData";
|
||
} else if (typeOid.equals(CMSObjectIdentifiers.envelopedData.getId())) {
|
||
return "EnvelopedData";
|
||
} else if (typeOid.equals(CMSObjectIdentifiers.signedAndEnvelopedData.getId())) {
|
||
return "SignedAndEnvelopedData";
|
||
} else if (typeOid.equals(CMSObjectIdentifiers.digestedData.getId())) {
|
||
return "DigestedData";
|
||
} else if (typeOid.equals(CMSObjectIdentifiers.encryptedData.getId())) {
|
||
return "EncryptedData";
|
||
} else {
|
||
return "Unknown: " + typeOid;
|
||
}
|
||
}
|
||
}
|
||
|
||
public static boolean verifySignedData(String base64CmsData) throws Exception {
|
||
byte[] cmsData = Base64.getDecoder().decode(base64CmsData);
|
||
return verifySignedData(cmsData);
|
||
}
|
||
|
||
public static boolean verifySignedData(byte[] cmsData) throws Exception {
|
||
CMSSignedData cmsSignedData = new CMSSignedData(cmsData);
|
||
return verifySignedData(cmsSignedData);
|
||
}
|
||
|
||
public static boolean verifySignedData(CMSSignedData cmsSignedData) throws Exception {
|
||
Store certStore = cmsSignedData.getCertificates();
|
||
SignerInformationStore signerStore = cmsSignedData.getSignerInfos();
|
||
|
||
for (Object signerObj : signerStore.getSigners()) {
|
||
SignerInformation signer = (SignerInformation) signerObj;
|
||
Collection certCollection = certStore.getMatches(signer.getSID());
|
||
|
||
if (certCollection.isEmpty()) {
|
||
System.err.println("未找到签名证书");
|
||
continue;
|
||
}
|
||
|
||
X509CertificateHolder certHolder = (X509CertificateHolder) certCollection.iterator().next();
|
||
X509Certificate cert = convertCertificate(certHolder);
|
||
|
||
try {
|
||
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
|
||
return true;
|
||
}
|
||
} catch (Exception e) {
|
||
System.err.println("签名验证失败: " + e.getMessage());
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public static boolean verifySignedDataWithCertificate(String base64CmsData, X509Certificate publicCert) throws Exception {
|
||
byte[] cmsData = Base64.getDecoder().decode(base64CmsData);
|
||
CMSSignedData cmsSignedData = new CMSSignedData(cmsData);
|
||
return verifySignedDataWithCertificate(cmsSignedData, publicCert);
|
||
}
|
||
|
||
public static boolean verifySignedDataWithCertificate(CMSSignedData cmsSignedData, X509Certificate publicCert) throws Exception {
|
||
SignerInformationStore signerStore = cmsSignedData.getSignerInfos();
|
||
|
||
for (Object signerObj : signerStore.getSigners()) {
|
||
SignerInformation signer = (SignerInformation) signerObj;
|
||
try {
|
||
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(publicCert))) {
|
||
return true;
|
||
}
|
||
} catch (Exception e) {
|
||
System.err.println("使用指定证书验证失败: " + e.getMessage());
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public static byte[] extractSignedContent(String base64CmsData) throws Exception {
|
||
byte[] cmsData = Base64.getDecoder().decode(base64CmsData);
|
||
CMSSignedData cmsSignedData = new CMSSignedData(cmsData);
|
||
return (byte[]) cmsSignedData.getSignedContent().getContent();
|
||
}
|
||
|
||
public static X509Certificate loadCertificateFromBytes(byte[] certBytes) throws Exception {
|
||
CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
|
||
return (X509Certificate) cf.generateCertificate(new java.io.ByteArrayInputStream(certBytes));
|
||
}
|
||
|
||
public static X509Certificate loadCertificateFromBase64(String base64Cert) throws Exception {
|
||
byte[] certBytes = Base64.getDecoder().decode(base64Cert);
|
||
return loadCertificateFromBytes(certBytes);
|
||
}
|
||
|
||
private static X509Certificate convertCertificate(X509CertificateHolder certHolder) throws Exception {
|
||
CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
|
||
return (X509Certificate) cf.generateCertificate(new java.io.ByteArrayInputStream(certHolder.getEncoded()));
|
||
}
|
||
|
||
public static 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();
|
||
|
||
byte[] signerInfoDer = signerInfoPrim.getEncoded(ASN1Encoding.DER);
|
||
System.out.println(" SignerInfo DER 编码长度: " + signerInfoDer.length + " bytes");
|
||
System.out.println(" SignerInfo DER 编码: " + bytesToHex(signerInfoDer));
|
||
|
||
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(" 找到 AuthenticatedAttributes,signerIndex 递增到: " + 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());
|
||
|
||
byte[] signingData = signedAttrsEncoded;
|
||
boolean valid = false;
|
||
|
||
if (authAttrsData instanceof ASN1TaggedObject) {
|
||
System.out.println(" [GMT 0010-2012] 尝试使用两种方式验签:");
|
||
|
||
System.out.println("\n 方式1: 使用带标签的完整编码 (包含 [0] IMPLICIT 标签)");
|
||
valid = verifySM2Signature(signerCert.getPublicKey(), signingData, signatureValue);
|
||
|
||
if (!valid) {
|
||
System.out.println("\n 方式2: 使用标签内的原始数据 (不包含标签)");
|
||
ASN1TaggedObject tagged = (ASN1TaggedObject) authAttrsData;
|
||
ASN1Primitive inner = tagged.getObject();
|
||
byte[] innerEncoded = inner.getEncoded(ASN1Encoding.DER);
|
||
System.out.println(" 内部数据长度: " + innerEncoded.length + " bytes");
|
||
System.out.println(" 内部数据 Hex: " + bytesToHex(innerEncoded));
|
||
valid = verifySM2Signature(signerCert.getPublicKey(), innerEncoded, signatureValue);
|
||
}
|
||
} else {
|
||
valid = verifySM2Signature(signerCert.getPublicKey(), signingData, signatureValue);
|
||
}
|
||
|
||
if (!valid) {
|
||
System.out.println("\n [调试] 尝试调整签名值格式...");
|
||
valid = tryAlternativeFormats(signerCert.getPublicKey(), signingData, signatureValue);
|
||
}
|
||
|
||
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("未找到有效的签名信息");
|
||
}
|
||
|
||
private static boolean verifySM2Signature(PublicKey publicKey, byte[] data, byte[] signature) {
|
||
try {
|
||
System.out.println(" ┌─────────────────────────────────────────────────────");
|
||
System.out.println(" │ 验签输入信息");
|
||
System.out.println(" ├─────────────────────────────────────────────────────");
|
||
System.out.println(" │ [输入1] 原始数据 (DER编码):");
|
||
System.out.println(" │ 长度: " + data.length + " bytes");
|
||
System.out.println(" │ Hex: " + bytesToHex(data));
|
||
System.out.println(" ├─────────────────────────────────────────────────────");
|
||
System.out.println(" │ [输入2] 公钥:");
|
||
System.out.println(" │ 算法: " + publicKey.getAlgorithm());
|
||
System.out.println(" │ 格式: " + publicKey.getFormat());
|
||
System.out.println(" │ 编码长度: " + publicKey.getEncoded().length + " bytes");
|
||
System.out.println(" │ Hex: " + bytesToHex(truncate(publicKey.getEncoded(), 64)));
|
||
System.out.println(" ├─────────────────────────────────────────────────────");
|
||
System.out.println(" │ [输入3] 签名值:");
|
||
System.out.println(" │ 长度: " + signature.length + " bytes");
|
||
System.out.println(" │ Hex: " + bytesToHex(signature));
|
||
System.out.println(" ├─────────────────────────────────────────────────────");
|
||
|
||
Signature sig = Signature.getInstance("SM3withSM2", "BC");
|
||
sig.initVerify(publicKey);
|
||
sig.update(data);
|
||
boolean result = sig.verify(signature);
|
||
System.out.println(" │ [输出] 验签结果: " + (result ? "成功" : "失败"));
|
||
System.out.println(" └─────────────────────────────────────────────────────");
|
||
|
||
if (!result && data.length > 0 && data[0] == 0x30) {
|
||
byte[] setData = convertSequenceToSet(data);
|
||
if (setData != null) {
|
||
System.out.println(" ┌─────────────────────────────────────────────────────");
|
||
System.out.println(" │ [GMT 0010-2012] 验签输入信息 (SET格式转换)");
|
||
System.out.println(" ├─────────────────────────────────────────────────────");
|
||
System.out.println(" │ [输入1] 原始数据 (SET格式):");
|
||
System.out.println(" │ 长度: " + setData.length + " bytes");
|
||
System.out.println(" │ Hex: " + bytesToHex(setData));
|
||
System.out.println(" ├─────────────────────────────────────────────────────");
|
||
System.out.println(" │ [输入2] 公钥:");
|
||
System.out.println(" │ 算法: " + publicKey.getAlgorithm());
|
||
System.out.println(" │ 格式: " + publicKey.getFormat());
|
||
System.out.println(" │ 编码长度: " + publicKey.getEncoded().length + " bytes");
|
||
System.out.println(" │ Hex: " + bytesToHex(truncate(publicKey.getEncoded(), 64)));
|
||
System.out.println(" ├─────────────────────────────────────────────────────");
|
||
System.out.println(" │ [输入3] 签名值:");
|
||
System.out.println(" │ 长度: " + signature.length + " bytes");
|
||
System.out.println(" │ Hex: " + bytesToHex(signature));
|
||
System.out.println(" ├─────────────────────────────────────────────────────");
|
||
sig = Signature.getInstance("SM3withSM2", "BC");
|
||
sig.initVerify(publicKey);
|
||
sig.update(setData);
|
||
result = sig.verify(signature);
|
||
System.out.println(" │ [输出] 验签结果: " + (result ? "成功" : "失败"));
|
||
System.out.println(" └─────────────────────────────────────────────────────");
|
||
}
|
||
}
|
||
|
||
return result;
|
||
} catch (Exception e) {
|
||
System.out.println(" ┌─────────────────────────────────────────────────────");
|
||
System.out.println(" │ [输出] 验签失败: " + e.getMessage());
|
||
System.out.println(" └─────────────────────────────────────────────────────");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private static byte[] truncate(byte[] data, int maxLength) {
|
||
if (data.length <= maxLength) {
|
||
return data;
|
||
}
|
||
byte[] truncated = new byte[maxLength];
|
||
System.arraycopy(data, 0, truncated, 0, maxLength);
|
||
return truncated;
|
||
}
|
||
|
||
private static byte[] convertSequenceToSet(byte[] sequenceData) {
|
||
try {
|
||
if (sequenceData.length < 2 || sequenceData[0] != 0x30) {
|
||
return null;
|
||
}
|
||
|
||
byte[] setData = new byte[sequenceData.length];
|
||
System.arraycopy(sequenceData, 0, setData, 0, sequenceData.length);
|
||
setData[0] = 0x31;
|
||
return setData;
|
||
} catch (Exception e) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
private static boolean tryAlternativeFormats(PublicKey publicKey, byte[] data, byte[] signature) {
|
||
try {
|
||
if (signature.length >= 65 && signature[0] == 0x30) {
|
||
System.out.println(" 尝试解析 ASN.1 编码的签名值...");
|
||
int offset = 2;
|
||
if (signature[1] >= 0x81) {
|
||
offset = signature[1] == 0x81 ? 3 : 2 + (signature[1] & 0xFF);
|
||
}
|
||
|
||
if (signature[offset] == 0x02) {
|
||
int rLen = signature[offset + 1];
|
||
byte[] r = new byte[rLen];
|
||
System.arraycopy(signature, offset + 2, r, 0, rLen);
|
||
offset += 2 + rLen;
|
||
|
||
if (signature[offset] == 0x02) {
|
||
int sLen = signature[offset + 1];
|
||
byte[] s = new byte[sLen];
|
||
System.arraycopy(signature, offset + 2, s, 0, sLen);
|
||
|
||
byte[] rawSig = new byte[64];
|
||
int rStart = r[0] == 0x00 ? 1 : 0;
|
||
int sStart = s[0] == 0x00 ? 1 : 0;
|
||
System.arraycopy(r, rStart, rawSig, 32 - r.length + rStart, r.length - rStart);
|
||
System.arraycopy(s, sStart, rawSig, 64 - s.length + sStart, s.length - sStart);
|
||
|
||
Signature sig = Signature.getInstance("SM3withSM2", "BC");
|
||
sig.initVerify(publicKey);
|
||
sig.update(data);
|
||
boolean result = sig.verify(rawSig);
|
||
System.out.println(" 原始格式(R+S)验签结果: " + (result ? "成功" : "失败"));
|
||
return result;
|
||
}
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
System.out.println(" 尝试其他格式失败: " + e.getMessage());
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
public static String bytesToHex(byte[] data) {
|
||
StringBuilder sb = new StringBuilder();
|
||
for (byte b : data) {
|
||
sb.append(String.format("%02x", b));
|
||
}
|
||
return sb.toString();
|
||
}
|
||
} |