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); } }
关键注意事项
- 信任锚配置:这是你之前报错的核心!必须把所有需要信任的证书(自签名证书、时间戳根证书)加入信任列表,DSS才会认可它们。
- LTV验证:自签名证书没有CRL/OCSP吊销信息,必须关闭LTV验证,否则会报“LTV验证结果不符合”的错误。
- 时间戳替代方案:测试环境可以用DSS的
MockTSPSource模拟时间戳服务器,不用额外配置时间戳根证书。
内容的提问来源于stack exchange,提问作者iirekm
相关产品推荐
相关产品推荐

