You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java项目XAdES B-T签名生成与验证完整可运行示例需求

一、先搞定Maven依赖(Gradle可对应转换)

用DSS 5.13.0这个稳定版本,依赖要全但不冗余:

<dependencies>
    <!-- DSS核心模块 -->
    <dependency>
        <groupId>eu.europa.esig.dss</groupId>
        <artifactId>dss-xades</artifactId>
        <version>5.13.0</version>
    </dependency>
    <dependency>
        <groupId>eu.europa.esig.dss</groupId>
        <artifactId>dss-validation</artifactId>
        <version>5.13.0</version>
    </dependency>
    <!-- BouncyCastle用于证书生成 -->
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>1.70</version>
    </dependency>
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcpkix-jdk15on</artifactId>
        <version>1.70</version>
    </dependency>
</dependencies>

二、步骤1:生成自签名密钥对与证书

用DSS自带工具类生成,比直接用BouncyCastle更贴合后续流程:

import eu.europa.esig.dss.model.DSSException;
import eu.europa.esig.dss.model.x509.CertificateToken;
import eu.europa.esig.dss.x509.CertificateUtils;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;

public class SelfSignedCertGenerator {

    public static KeyPair generateRSAKeyPair() throws Exception {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(2048, new SecureRandom());
        return keyGen.generateKeyPair();
    }

    public static X509Certificate generateSelfSignedCertificate(KeyPair keyPair) throws Exception {
        X500Name issuer = new X500Name("CN=Self-Signed Test Signer, OU=Test, O=MyOrg, C=US");
        X500Name subject = issuer; // 自签名,签发者与主体一致
        BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
        Date startDate = new Date();
        Calendar cal = Calendar.getInstance();
        cal.setTime(startDate);
        cal.add(Calendar.YEAR, 1); // 有效期1年
        Date endDate = cal.getTime();

        var contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate());
        var certBuilder = new JcaX509v3CertificateBuilder(issuer, serial, startDate, endDate, subject, keyPair.getPublic());

        // 添加数字签名用途扩展
        certBuilder.addExtension(
                org.bouncycastle.asn1.x509.ExtendedKeyUsage.ID_KP_DigitalSignature,
                false,
                new org.bouncycastle.asn1.x509.ExtendedKeyUsage(org.bouncycastle.asn1.x509.KeyPurposeId.id_kp_emailProtection)
        );

        return new JcaX509CertificateConverter().getCertificate(certBuilder.build(contentSigner));
    }

    // 转换为DSS兼容的CertificateToken
    public static CertificateToken toCertificateToken(X509Certificate cert) throws DSSException {
        return CertificateUtils.generateCertificateToken(cert);
    }
}

三、步骤2:生成XAdES B-T签名

这里用公共时间戳服务器(后续验证需把它的根证书加入信任锚):

import eu.europa.esig.dss.enumerations.DigestAlgorithm;
import eu.europa.esig.dss.enumerations.SignatureLevel;
import eu.europa.esig.dss.enumerations.SignaturePackaging;
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.model.InMemoryDocument;
import eu.europa.esig.dss.service.tsp.OnlineTSPSource;
import eu.europa.esig.dss.signature.xades.XAdESSignatureParameters;
import eu.europa.esig.dss.signature.xades.XAdESService;
import eu.europa.esig.dss.validation.CommonCertificateVerifier;

import java.security.KeyPair;
import java.security.cert.X509Certificate;

public class XAdESSigner {

    public static DSSDocument generateXAdESBTSignature(DSSDocument originalDoc, KeyPair keyPair) throws Exception {
        X509Certificate signingCert = SelfSignedCertGenerator.generateSelfSignedCertificate(keyPair);
        
        // 初始化XAdES签名服务
        var certVerifier = new CommonCertificateVerifier();
        var service = new XAdESService(certVerifier);

        // 配置签名参数
        var params = new XAdESSignatureParameters();
        params.setSignatureLevel(SignatureLevel.XAdES_BASELINE_T); // XAdES B-T级别
        params.setSignaturePackaging(SignaturePackaging.ENVELOPED); // 信封式嵌入XML
        params.setSigningCertificate(SelfSignedCertGenerator.toCertificateToken(signingCert));
        params.setDigestAlgorithm(DigestAlgorithm.SHA256);

        // 配置时间戳服务器
        var tspSource = new OnlineTSPSource("http://timestamp.digicert.com");
        tspSource.setDigestAlgorithm(DigestAlgorithm.SHA256);
        params.setTspSource(tspSource);

        // 生成并返回签名文档
        var signatureValue = service.signDocument(originalDoc, params, keyPair.getPrivate());
        return service.signDocument(originalDoc, params, signatureValue);
    }

    public static void main(String[] args) throws Exception {
        // 生成密钥对
        KeyPair keyPair = SelfSignedCertGenerator.generateRSAKeyPair();
        
        // 准备待签名XML
        String xmlContent = "<test><content>Hello XAdES B-T</content></test>";
        DSSDocument originalDoc = new InMemoryDocument(xmlContent.getBytes(), "test.xml");

        // 生成签名并保存
        DSSDocument signedDoc = generateXAdESBTSignature(originalDoc, keyPair);
        signedDoc.save("signed_xades_bt.xml");
        System.out.println("XAdES B-T签名已生成:signed_xades_bt.xml");
    }
}

四、步骤3:验证XAdES B-T签名

核心:必须把自签名证书和时间戳根证书加入信任锚,同时关闭LTV验证(自签名证书无吊销信息):

import eu.europa.esig.dss.diagnostic.DiagnosticData;
import eu.europa.esig.dss.diagnostic.SignatureWrapper;
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.model.InMemoryDocument;
import eu.europa.esig.dss.model.x509.CertificateToken;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.validation.CommonCertificateVerifier;
import eu.europa.esig.dss.validation.SignedDocumentValidator;
import eu.europa.esig.dss.validation.reports.Reports;

import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

public class XAdESVerifier {

    public static boolean verifyXAdESBTSignature(DSSDocument signedDoc, X509Certificate trustedSigningCert) throws Exception {
        // 初始化验证器
        var validator = SignedDocumentValidator.fromDocument(signedDoc);

        // 配置证书验证器,添加信任锚
        var certVerifier = new CommonCertificateVerifier();
        List<CertificateToken> trustAnchors = new ArrayList<>();
        
        // 添加自签名证书为信任锚
        trustAnchors.add(SelfSignedCertGenerator.toCertificateToken(trustedSigningCert));
        
        // 添加Digicert时间戳根证书(需自行下载保存到resources目录)
        var digicertRootDoc = new InMemoryDocument(Utils.toByteArray(XAdESVerifier.class.getResourceAsStream("/digicert_root.cer")));
        trustAnchors.add(CertificateUtils.generateCertificateToken(digicertRootDoc));

        certVerifier.setTrustedCertificates(trustAnchors);
        certVerifier.setEnableLTVValidation(false); // 关闭LTV验证(自签名无吊销信息)

        validator.setCertificateVerifier(certVerifier);

        // 执行验证并输出结果
        Reports reports = validator.validateDocument();
        DiagnosticData diagnosticData = reports.getDiagnosticData();

        for (SignatureWrapper signature : diagnosticData.getSignatures()) {
            boolean sigValid = diagnosticData.isSignatureValid(signature.getId());
            boolean tsValid = diagnosticData.isTimestampValid(signature.getContentTimestamps().get(0).getId());
            
            System.out.println("签名有效性:" + sigValid);
            System.out.println("时间戳有效性:" + tsValid);
            
            if (!sigValid || !tsValid) {
                System.out.println("错误详情:" + diagnosticData.getErrorMessages());
                return false;
            }
        }
        System.out.println("XAdES B-T签名验证通过!");
        return true;
    }

    public static void main(String[] args) throws Exception {
        // 加载自签名证书
        KeyPair keyPair = SelfSignedCertGenerator.generateRSAKeyPair();
        X509Certificate cert = SelfSignedCertGenerator.generateSelfSignedCertificate(keyPair);
        
        // 加载签名文档
        DSSDocument signedDoc = new InMemoryDocument(Utils.toByteArray(XAdESVerifier.class.getResourceAsStream("/signed_xades_bt.xml")));

        // 执行验证
        verifyXAdESBTSignature(signedDoc, cert);
    }
}

关键注意事项

  1. 信任锚配置:这是你之前报错的核心!必须把所有需要信任的证书(自签名证书、时间戳根证书)加入信任列表,DSS才会认可它们。
  2. LTV验证:自签名证书没有CRL/OCSP吊销信息,必须关闭LTV验证,否则会报“LTV验证结果不符合”的错误。
  3. 时间戳替代方案:测试环境可以用DSS的MockTSPSource模拟时间戳服务器,不用额外配置时间戳根证书。

内容的提问来源于stack exchange,提问作者iirekm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:39:26