没有 KeyInfo 的 xades4j 验证
xades4j verification without KeyInfo
我们公司使用 Etokens 通过 https 与服务器通信。如何验证 KeyInfo 中没有签名证书的封装 XML 文件?
<?xml version="1.0" encoding="UTF-8"?>
<EDoc><NextMsg ID="Edoc">2019-09-23T16:20:53</NextMsg><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></ds:SignatureMethod>
<ds:Reference Id="xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1-ref0" URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod>
<ds:DigestValue>IUdYs162QE1GdUEKUxqppoFmNvrYMLMBGnduWy6v3rc=</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1-signedprops">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod>
<ds:DigestValue>Uw2b3fkLSJPm+yDeYwXQhJHZhWP+vUNBEeS55LcII00=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1-sigvalue">
eHOQcdUYRRhQa3DV+P5lWnXR32KXpO08n4QI/SIXvJxbjvz3roGNas53E/1hCui8MG3TkZulx4Fw
W3N9qJ3FXciasReaqofrexHtbntyr6O/tzQh2akcJzo3TPH+j4PxozjFUxCxcaJRSqCE0hWdBtuI
S8rn+EKpes7ohgtlsVg=
</ds:SignatureValue>
<ds:Object><xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" Target="#xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1"><xades:SignedProperties xmlns:ns3="http://uri.etsi.org/01903/v1.4.1#" Id="xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1-signedprops"><xades:SignedSignatureProperties><xades:SigningTime>2019-09-23T16:20:53+03:00</xades:SigningTime><xades:SigningCertificate><xades:Cert><xades:CertDigest><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>woG3fsImDUeqxznickzLkpeY9R4=</ds:DigestValue></xades:CertDigest><xades:IssuerSerial><ds:X509IssuerName>cn=LB-LITAS-CA,ou=MSD,o=Lietuvos bankas,l=Vilnius,c=LT</ds:X509IssuerName><ds:X509SerialNumber>105704079740755226136574</ds:X509SerialNumber></xades:IssuerSerial></xades:Cert></xades:SigningCertificate></xades:SignedSignatureProperties></xades:SignedProperties></xades:QualifyingProperties></ds:Object>
</ds:Signature></EDoc>
默认verify
函数搜索KeyInfo
public static void verifyBes(KeyStore ksaa, String path)
throws javax.xml.parsers.ParserConfigurationException, org.xml.sax.SAXException,
java.security.NoSuchAlgorithmException, xades4j.utils.XadesProfileResolutionException,
xades4j.XAdES4jException, java.io.IOException, java.security.NoSuchProviderException,
java.security.cert.CertificateException, java.security.cert.CRLException,
java.security.cert.CertStoreException, java.security.KeyStoreException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(new File(path));
Element root = doc.getDocumentElement();
Element idChild = (Element) root.getFirstChild();
DOMHelper.useIdAsXmlId(idChild);
String filename =
System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
FileInputStream is = new FileInputStream(filename);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "changeit";
ks.load(is, password.toCharArray());
X509Certificate validate_cert = null;
FileSystemDirectoryCertStore fsCertStore = new FileSystemDirectoryCertStore("../certStore");
Collection<? extends Certificate> allCerts = fsCertStore.getStore().getCertificates(null);
// search for specific certificate to test agains
for (Certificate c : allCerts) {
X509Certificate pool_cert = (X509Certificate) c;
System.out.println(pool_cert.getSubjectDN().getName());
if (-1 != pool_cert.getSubjectDN().getName().indexOf("TEST CERTIFICATE"))
validate_cert = pool_cert;
}
CertificateValidationProvider validationProviderMySigs =
new PKIXCertificateValidationProvider(ks, false, fsCertStore.getStore());
XadesVerificationProfile instance = new XadesVerificationProfile(validationProviderMySigs);
XadesVerifier verifier = instance.newVerifier();
Element sig = (Element) doc.getElementsByTagName("ds:Signature").item(0);
XAdESVerificationResult r = verifier.verify(sig, null);
}
xades4j.verification.InvalidKeyInfoDataException: 没有 X509Data 来识别叶子证书
在 xades4j.verification.SignatureUtils.processKeyInfo(SignatureUtils.java:79)
在 xades4j.verification.XadesVerifierImpl.verify(XadesVerifierImpl.java:184)
在 com.mycompany.app.App.verifyBes(App.java:993)
在 com.mycompany.app.App.main(App.java:460)
TL,DR;
目前,没有办法做到这一点。
原因
- xadesj4j 基于 XAdES 1.4.1 标准。在该标准中,
SigningCertificate
属性 要求在 属性 中标识签名证书,但如果有多个证书引用,则不会说明哪一项对应于签名证书在 属性。 xades4j 尝试使用 KeyInfo
上的数据来弄清楚。
- xades4j 首先进行核心 XML-DSIG/cryptographic 验证(这需要歌唱证书)并且仅在检查每个合格 属性 之后。这意味着在尝试识别证书时不考虑
SigningCertificate
属性。也许这可能会有所不同,特别是如果 属性 中只有一个证书引用(上一项中提到的问题不适用)。
想法
- 识别签名证书的目的是提供
X509CertSelector
到 CertificateValidationProvider.validate()
。如果没有其他更改,如果在 KeyInfo
不存在时提供 "empty" 证书选择器,则意味着 CertificateValidationProvider
需要知道如何识别签名证书。内置 PKIXCertificateValidationProvider
会失败,这并不酷。但是,由于此方案当前失败,这意味着更改不会破坏现有应用程序。
- 当没有
KeyInfo
时,尝试查找 SigningCertificate
属性 如果它包含单个证书引用,请使用其中的 issuer/serial 来配置X509CertSelector
。鉴于当前的体系结构,这具有在核心签名验证之前访问 属性 数据的缺点。
我不喜欢任何选项。我还添加了 a reference to this question in an existing issue 关于 X509CertSelector
的创建。我会再考虑一下。欢迎提出更多想法或尝试实施。
我们公司使用 Etokens 通过 https 与服务器通信。如何验证 KeyInfo 中没有签名证书的封装 XML 文件?
<?xml version="1.0" encoding="UTF-8"?>
<EDoc><NextMsg ID="Edoc">2019-09-23T16:20:53</NextMsg><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></ds:SignatureMethod>
<ds:Reference Id="xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1-ref0" URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod>
<ds:DigestValue>IUdYs162QE1GdUEKUxqppoFmNvrYMLMBGnduWy6v3rc=</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1-signedprops">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod>
<ds:DigestValue>Uw2b3fkLSJPm+yDeYwXQhJHZhWP+vUNBEeS55LcII00=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1-sigvalue">
eHOQcdUYRRhQa3DV+P5lWnXR32KXpO08n4QI/SIXvJxbjvz3roGNas53E/1hCui8MG3TkZulx4Fw
W3N9qJ3FXciasReaqofrexHtbntyr6O/tzQh2akcJzo3TPH+j4PxozjFUxCxcaJRSqCE0hWdBtuI
S8rn+EKpes7ohgtlsVg=
</ds:SignatureValue>
<ds:Object><xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" Target="#xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1"><xades:SignedProperties xmlns:ns3="http://uri.etsi.org/01903/v1.4.1#" Id="xmldsig-5d0f41cd-6e98-488d-9415-28b6329b34d1-signedprops"><xades:SignedSignatureProperties><xades:SigningTime>2019-09-23T16:20:53+03:00</xades:SigningTime><xades:SigningCertificate><xades:Cert><xades:CertDigest><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>woG3fsImDUeqxznickzLkpeY9R4=</ds:DigestValue></xades:CertDigest><xades:IssuerSerial><ds:X509IssuerName>cn=LB-LITAS-CA,ou=MSD,o=Lietuvos bankas,l=Vilnius,c=LT</ds:X509IssuerName><ds:X509SerialNumber>105704079740755226136574</ds:X509SerialNumber></xades:IssuerSerial></xades:Cert></xades:SigningCertificate></xades:SignedSignatureProperties></xades:SignedProperties></xades:QualifyingProperties></ds:Object>
</ds:Signature></EDoc>
默认verify
函数搜索KeyInfo
public static void verifyBes(KeyStore ksaa, String path)
throws javax.xml.parsers.ParserConfigurationException, org.xml.sax.SAXException,
java.security.NoSuchAlgorithmException, xades4j.utils.XadesProfileResolutionException,
xades4j.XAdES4jException, java.io.IOException, java.security.NoSuchProviderException,
java.security.cert.CertificateException, java.security.cert.CRLException,
java.security.cert.CertStoreException, java.security.KeyStoreException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(new File(path));
Element root = doc.getDocumentElement();
Element idChild = (Element) root.getFirstChild();
DOMHelper.useIdAsXmlId(idChild);
String filename =
System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
FileInputStream is = new FileInputStream(filename);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "changeit";
ks.load(is, password.toCharArray());
X509Certificate validate_cert = null;
FileSystemDirectoryCertStore fsCertStore = new FileSystemDirectoryCertStore("../certStore");
Collection<? extends Certificate> allCerts = fsCertStore.getStore().getCertificates(null);
// search for specific certificate to test agains
for (Certificate c : allCerts) {
X509Certificate pool_cert = (X509Certificate) c;
System.out.println(pool_cert.getSubjectDN().getName());
if (-1 != pool_cert.getSubjectDN().getName().indexOf("TEST CERTIFICATE"))
validate_cert = pool_cert;
}
CertificateValidationProvider validationProviderMySigs =
new PKIXCertificateValidationProvider(ks, false, fsCertStore.getStore());
XadesVerificationProfile instance = new XadesVerificationProfile(validationProviderMySigs);
XadesVerifier verifier = instance.newVerifier();
Element sig = (Element) doc.getElementsByTagName("ds:Signature").item(0);
XAdESVerificationResult r = verifier.verify(sig, null);
}
xades4j.verification.InvalidKeyInfoDataException: 没有 X509Data 来识别叶子证书 在 xades4j.verification.SignatureUtils.processKeyInfo(SignatureUtils.java:79) 在 xades4j.verification.XadesVerifierImpl.verify(XadesVerifierImpl.java:184) 在 com.mycompany.app.App.verifyBes(App.java:993) 在 com.mycompany.app.App.main(App.java:460)
TL,DR;
目前,没有办法做到这一点。
原因
- xadesj4j 基于 XAdES 1.4.1 标准。在该标准中,
SigningCertificate
属性 要求在 属性 中标识签名证书,但如果有多个证书引用,则不会说明哪一项对应于签名证书在 属性。 xades4j 尝试使用KeyInfo
上的数据来弄清楚。 - xades4j 首先进行核心 XML-DSIG/cryptographic 验证(这需要歌唱证书)并且仅在检查每个合格 属性 之后。这意味着在尝试识别证书时不考虑
SigningCertificate
属性。也许这可能会有所不同,特别是如果 属性 中只有一个证书引用(上一项中提到的问题不适用)。
想法
- 识别签名证书的目的是提供
X509CertSelector
到CertificateValidationProvider.validate()
。如果没有其他更改,如果在KeyInfo
不存在时提供 "empty" 证书选择器,则意味着CertificateValidationProvider
需要知道如何识别签名证书。内置PKIXCertificateValidationProvider
会失败,这并不酷。但是,由于此方案当前失败,这意味着更改不会破坏现有应用程序。 - 当没有
KeyInfo
时,尝试查找SigningCertificate
属性 如果它包含单个证书引用,请使用其中的 issuer/serial 来配置X509CertSelector
。鉴于当前的体系结构,这具有在核心签名验证之前访问 属性 数据的缺点。
我不喜欢任何选项。我还添加了 a reference to this question in an existing issue 关于 X509CertSelector
的创建。我会再考虑一下。欢迎提出更多想法或尝试实施。