XAdES4j 2.2.0扩展XAdES-XL时HttpTsa配置异常求助
问题
使用XAdES4j 2.2.0进行XML文档签名,从1.5.1版本升级适配代码后,XADES-BES、XADES-T和XADES-C均可正常签名,但从XAdES-C扩展到XADES-XL时抛出异常:HttpTsaConfiguration must be configured in the profile in order to use an HTTP-based time-stamp token provider。
相关代码
// Certificate for signing is stored in PKCS12 keystore in file. KeyingDataProvider keyingDataProvider = FileSystemKeyStoreKeyingDataProvider .builder("PKCS12", sKeyStorePath, new SigningCertificateSelector.single()) .storePassword(new DirectPasswordProvider(sKeyStorePassword)) .entryPassword(new DirectPasswordProvider(sKeyStorePassword)) .fullChain(true) .build(); // KeyStore and CertStore for validating signatures and timestamps. CertificateValidationProvider validationProvider = PKIXCertificateValidationProvider .builder(keyStore) .checkRevocation(true) .intermediateCertStores(certStore) .build(); // Read document from file on disk. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder db = dbf.newDocumentBuilder(); doc = db.parse(new File(sFilePath)); // Sign document with XAdES-C. signDocumentC(keyingDataProvider, doc, validationProvider); // Sign document with XAdES-XL afterwards. signDocumentXL(keyingDataProvider, doc, validationProvider); ... /** Method for signing with XAdES-C. */ private void signDocumentC(KeyingDataProvider keyingDataProvider, Document doc, CertificateValidationProvider validationProvider) throws Exception { Element elemToSign = doc.getDocumentElement(); DOMHelper.useIdAsXmlId(elemToSign); ValidationDataProvider vdp = new ValidationDataFromCertValidationProvider(validationProvider); XadesSigner signer = new XadesCSigningProfile(keyingDataProvider, vdp) .with(new HttpTimeStampTokenProvider(new DefaultMessageDigestProvider(), new HttpTsaConfiguration("http://freetsa.org/tsr"))) .newSigner(); // Skip check for countersigning, just sign. new Enveloped(signer).sign(elemToSign); } /** Method for signing with XAdES-XL. */ private void signDocumentXL(KeyingDataProvider keyingProvider, Document doc, CertificateValidationProvider validationProvider) throws Exception { Element elemToSign = doc.getDocumentElement(); DOMHelper.useIdAsXmlId(elemToSign); NodeList signatures = doc.getElementsByTagNameNS(Constants.SignatureSpecNS, Constants._TAG_SIGNATURE); Element signatureNode = (Element)signatures.item(signatures.getLength() - 1); XadesVerificationProfile nistVerificationProfile = new XadesVerificationProfile(validationProvider); XadesSignatureFormatExtender formExt = new XadesFormatExtenderProfile().getFormatExtender(); XAdESVerificationResult res = nistVerificationProfile.newVerifier().verify(signatureNode, null, formExt, XAdESForm.X_L); }
异常堆栈信息
[2023-01-23 18:22:29] INFO: [] ESignatureXML.signDocumentXL: Signing xml document XAdES-XL... xades4j.production.PropertyDataGeneratorNotAvailableException: Property data generation failed for SigAndRefsTimeStamp: data object generator cannot be created at xades4j.production.PropertyDataGeneratorsMapperImpl.getGenerator(PropertyDataGeneratorsMapperImpl.java:51) at xades4j.production.PropertiesDataObjectsGeneratorImpl.doGenPropsData(PropertiesDataObjectsGeneratorImpl.java:87) at xades4j.production.PropertiesDataObjectsGeneratorImpl.genPropsData(PropertiesDataObjectsGeneratorImpl.java:73) at xades4j.production.PropertiesDataObjectsGeneratorImpl.generateUnsignedPropertiesData(PropertiesDataObjectsGeneratorImpl.java:64) at xades4j.production.XadesSignatureFormatExtenderImpl.enrichSignature(XadesSignatureFormatExtenderImpl.java:79) at xades4j.verification.XadesVerifierImpl.verify(XadesVerifierImpl.java:497) at com.my.ESignatureXML.signDocumentXL(ESignatureXML.java:709) at com.my.ESignatureXML.signDocument(ESignatureXML.java:482) at com.my.ESignatureXML.performSigning(ESignatureXML.java:236) at com.my.ESignatureXML.sign(ESignatureXML.java:187) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ... at java.base/java.lang.Thread.run(Thread.java:835) Caused by: com.google.inject.ProvisionException: Unable to provision, see the following errors: 1) [Guice/ErrorInCustomProvider]: IllegalStateException: HttpTsaConfiguration must be configured in the profile in order to use an HTTP-based time-stamp token provider. at DefaultProductionBindingsModule.configure(DefaultProductionBindingsModule.java:80) \_ installed by: Modules$OverrideModule -> DefaultProductionBindingsModule at HttpTimeStampTokenProvider.<init>(HttpTimeStampTokenProvider.java:44) \_ for 2nd parameter while locating HttpTimeStampTokenProvider at DataGenSigAndRefsTimeStamp.<init>(DataGenSigAndRefsTimeStamp.java:51) \_ for 2nd parameter while locating DataGenSigAndRefsTimeStamp while locating PropertyDataObjectGenerator<SigAndRefsTimeStampProperty> 1 error ====================== Full classname legend: ====================== DataGenSigAndRefsTimeStamp: "xades4j.production.DataGenSigAndRefsTimeStamp" DefaultProductionBindingsModule: "xades4j.production.DefaultProductionBindingsModule" HttpTimeStampTokenProvider: "xades4j.providers.impl.HttpTimeStampTokenProvider" Modules$OverrideModule: "com.google.inject.util.Modules$OverrideModule" PropertyDataObjectGenerator: "xades4j.production.PropertyDataObjectGenerator" SigAndRefsTimeStampProperty: "xades4j.properties.SigAndRefsTimeStampProperty" ======================== End of classname legend: ======================== at com.google.inject.internal.InternalProvisionException.toProvisionException(InternalProvisionException.java:251) at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1104) at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1134) at xades4j.production.PropertyDataGeneratorsMapperImpl.getGenerator(PropertyDataGeneratorsMapperImpl.java:48) ... 58 more Caused by: java.lang.IllegalStateException: HttpTsaConfiguration must be configured in the profile in order to use an HTTP-based time-stamp token provider. at xades4j.production.DefaultProductionBindingsModule.lambda$configure$0(DefaultProductionBindingsModule.java:81) at com.google.inject.internal.ProviderInternalFactory.provision(ProviderInternalFactory.java:86) at com.google.inject.internal.InternalFactoryToInitializableAdapter.provision(InternalFactoryToInitializableAdapter.java:57) at com.google.inject.internal.ProviderInternalFactory.circularGet(ProviderInternalFactory.java:60) at com.google.inject.internal.InternalFactoryToInitializableAdapter.get(InternalFactoryToInitializableAdapter.java:47) at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:40) at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:60) at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:113) at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91) at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300) at com.google.inject.internal.FactoryProxy.get(FactoryProxy.java:60) at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:40) at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:60) at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:113) at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91) at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300) at com.google.inject.internal.FactoryProxy.get(FactoryProxy.java:60) at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1101) ... 60 more
排查发现,PropertiesDataObjectsGeneratorImpl的doGenPropsData方法中,xades4j.properties.SigAndRefsTimeStampProperty的time值为null,该异常仅在扩展到XAdES-XL时出现。
解决方案
问题根源是:扩展到XAdES-XL时需要添加SigAndRefsTimeStamp属性(新的时间戳),但当前的XadesFormatExtenderProfile没有配置时间戳服务(TSA)提供者。之前XAdES-C签名时配置的TSA只作用于C格式的签名器,和签名扩展器完全无关。
修改signDocumentXL方法,给XadesFormatExtenderProfile配置HTTP时间戳提供者,和XAdES-C签名时的配置保持一致:
/** Method for signing with XAdES-XL. */ private void signDocumentXL(KeyingDataProvider keyingProvider, Document doc, CertificateValidationProvider validationProvider) throws Exception { Element elemToSign = doc.getDocumentElement(); DOMHelper.useIdAsXmlId(elemToSign); NodeList signatures = doc.getElementsByTagNameNS(Constants.SignatureSpecNS, Constants._TAG_SIGNATURE); Element signatureNode = (Element)signatures.item(signatures.getLength() - 1); XadesVerificationProfile nistVerificationProfile = new XadesVerificationProfile(validationProvider); // 给格式扩展器配置TSA提供者,和XAdES-C签名时的配置一致 XadesSignatureFormatExtender formExt = new XadesFormatExtenderProfile() .with(new HttpTimeStampTokenProvider(new DefaultMessageDigestProvider(), new HttpTsaConfiguration("http://freetsa.org/tsr"))) .getFormatExtender(); XAdESVerificationResult res = nistVerificationProfile.newVerifier().verify(signatureNode, null, formExt, XAdESForm.X_L); }
补充说明
XAdES-XL扩展过程需要生成新的时间戳属性来满足格式要求,这个过程依赖独立的TSA配置。之前的XAdES-C签名配置不会自动传递给扩展器,必须单独给XadesFormatExtenderProfile配置TSA提供者,才能生成所需的SigAndRefsTimeStamp属性,解决time值为null的问题。
内容的提问来源于stack exchange,提问作者predrags

