Xades4j验证签名Xades - EPES:错误的XML签名

时间:2017-12-01 02:10:16

标签: java xades4j

我正在使用Xades4J开发一个简单的Java程序,用XADES-EPES签署和验证XML。签名过程似乎正常工作并生成有效的签名XML(虽然我们不需要SignaturePolicyIdentifier部分)。

* options参数是从joptsimple库到解析参数

这是我的签名功能:

void signXML(OptionSet options) {
    KeyingDataProvider kp;
    try {

        SignaturePolicyInfoProvider policyInfoProvider = new SignaturePolicyInfoProvider() {
            public SignaturePolicyBase getSignaturePolicy() {
                return new SignaturePolicyIdentifierProperty(
                        new ObjectIdentifier("oid:/1.2.4.0.9.4.5", IdentifierType.OIDAsURI, "Policy description"),
                        new ByteArrayInputStream("Test policy input stream".getBytes()))
                                .withLocationUrl("http://www.example.com/policy");
            }
        };

        kp = new FileSystemKeyStoreKeyingDataProvider("pkcs12", options.valueOf("certificate").toString(),
                new FirstCertificateSelector(), new DirectPasswordProvider(options.valueOf("password").toString()),
                new DirectPasswordProvider(options.valueOf("password").toString()), false);

        // SignaturePolicyInfoProvider spi = new
        XadesSigningProfile p = new XadesEpesSigningProfile(kp, policyInfoProvider);
        p.withBasicSignatureOptionsProvider(new SignatureOptionsProvider());
        XadesSigner signer = p.newSigner();

        // open file
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = null;
        builder = factory.newDocumentBuilder();
        Document doc1 = builder.parse(new File(options.valueOf("input").toString()));
        Element elemToSign = doc1.getDocumentElement();

        // sign whole document
        new Enveloped(signer).sign(elemToSign);

        // save output file
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        Result output = new StreamResult(new File(options.valueOf("output").toString()));
        Source input = new DOMSource(doc1);
        transformer.transform(input, output);

    } catch (KeyStoreException | ParserConfigurationException | SAXException | IOException | XAdES4jException
            | TransformerException e) {

        e.printStackTrace();
    }
}

这是它创建的示例签名:(在
在base64值的每一行的末尾是否正常?)

<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="xmldsig-f5700b65-334e-4905-96f6-ca6156139686">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference Id="xmldsig-f5700b65-334e-4905-96f6-ca6156139686-ref0" URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>h20v8PSCwU5ymBKMj5o8scN3FyMfGCmN8OA2bnRHTnI=</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#xmldsig-f5700b65-334e-4905-96f6-ca6156139686-signedprops">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>tEyMfk39qnevJRyLbxQJIGpEBJuCcYRSZNNjm7HfaaA=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="xmldsig-f5700b65-334e-4905-96f6-ca6156139686-sigvalue">
ZhdQPEz7KwYoLTc72rxqUN2rlMfRGmqHd0MVSEeHRf/kmoqAGAqit5tw6k9k8oFCyuPDS/+DoXHJ&#13;
rooMDEMr+cuvbwwDwy77kHT+8JeUuIAamyqP/OByLytxXm/oKHVAraMhHNsGHvhcsYzULHl+n6JB&#13;
fhsYeR6rZdksS+QkgeUYohEktodl20/kyug4ymZjbJ7rCtH6+nRRR1nqc8oVT2ZpgfEaPHqWO06V&#13;
QURYYkuQEPOsNgFUu+YiWAX9pz94tlsOlLu6efBUP4lDZbCSq75J7nVZonmTbj/+mPl/oxpLCAgG&#13;
utWpL1RwyWn454G+Rd/AG3+MLWnbPVVSWUTcNK2gv4S2MVmosGxBnKwA71Zu30S+KaDwoktzZ62X&#13;
ViDLMmf6YbTKhiCIZYdjXPXr9QwSOHr+2B5GNUwo/CoOENJY/sXNHiHXxw+d+cyoowN4faJoVb5C&#13;
YFT7DACHyRd/d6TGdVEviHToTak+xPIK0HGnLvmkqzpICX8SFuMybur5Tttex4hqJNmXI7XbI9pk&#13;
YGLESBvKmzp4O9wLTJdABsLLxZSwU1plagu+5nb8ujIzmngVEHx4yBRprRVr3cBrHKoQBOHAiaGQ&#13;
96qUVTFogIX3vxN/1Q8xKDfB99FlzDdqnc2SW5lgYHnkuHHStJhoaCKRraUWVtU1miT/gMES+YM=
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
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
</ds:X509Certificate>
<ds:X509SubjectName>CN=Qprotex,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU</ds:X509SubjectName>
<ds:X509IssuerSerial>
<ds:X509IssuerName>CN=Qprotex,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU</ds:X509IssuerName>
<ds:X509SerialNumber>18213463010308326959</ds:X509SerialNumber>
</ds:X509IssuerSerial>
</ds:X509Data>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>
t1+ksAGGc1kvhs3AjTfIyBRRBb3sB5C+IlGplS1lOpViUsYOZKT/t0u1PWTxWnTNYpEChSg4ECnb&#13;
aNjenuyIQct5AS+MMMD2R8SSGGfvlhwr/yypEzcIfsOjFMhnV95Kto0abqLDucOyZotZfbaQQcwK&#13;
7ATI/TrumoUX6lRaryeIYBhENRCvmTjcR3ecinL+FSEpEOdXSh0jtHRtoV/HCzHdgNvXXLPD2Yky&#13;
jY/0D9rvHmVmKXtRfnLsiNcc2vMuE0XlfBz/WbP+lOpMKpka5F7ocnDeMJ2Tb24tL60M+DS7IYWO&#13;
J4wmVpb9FsBI3mDZ1AndsTIp5lljLIT7CVvgIvIbCxlOx6vcRxjOhpzNLmCJuMuI2dLtg5qn6q4Q&#13;
oLrE6LAQZ8Zt9ljK2s33Vc84FZi+nZx6A1enTnLyHjds6BdrzvKLOJeUtLlTbufxNrkQzWSIhI2Z&#13;
bfhYMn4yC1REj4kH30AKD6YkN028TasJDnv30g9n9eSbZ0fq2buT2DawGAA9GP02daaaOyTmBVJM&#13;
onpixSD7lN7iR3zvDUxtjIoOi15XZw+2k28VqkwVdOwo3xkz5zVhSxm7Xv1NHdiUO2AUzvWMdBNM&#13;
TS2t0rr31B232UQvuqDHW1ws6em1iA+Lu1X3Z59Mer6Hg2mVclNKSBrYxUSwSv9poZqxXlGH6R0=
</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
<ds:Object><xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#" Target="#xmldsig-f5700b65-334e-4905-96f6-ca6156139686"><xades:SignedProperties Id="xmldsig-f5700b65-334e-4905-96f6-ca6156139686-signedprops"><xades:SignedSignatureProperties><xades:SigningTime>2017-11-30T20:58:18.009-05:00</xades:SigningTime><xades:SigningCertificate><xades:Cert><xades:CertDigest><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>JkEubWWYvvoh7kOnuAvYeXE4cFamHJ5FOBYt3b3dCj4=</ds:DigestValue></xades:CertDigest><xades:IssuerSerial><ds:X509IssuerName>CN=Qprotex,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU</ds:X509IssuerName><ds:X509SerialNumber>18213463010308326959</ds:X509SerialNumber></xades:IssuerSerial></xades:Cert></xades:SigningCertificate><xades:SignaturePolicyIdentifier><xades:SignaturePolicyId><xades:SigPolicyId><xades:Identifier Qualifier="OIDAsURI">oid:/1.2.4.0.9.4.5</xades:Identifier><xades:Description>Policy description</xades:Description></xades:SigPolicyId><xades:SigPolicyHash><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>MaW9PDxJruPHhjBVAMWyrF9zP+kMnNqkoQOUXxDDGAk=</ds:DigestValue></xades:SigPolicyHash><xades:SigPolicyQualifiers><xades:SigPolicyQualifier><xades:SPURI>http://www.example.com/policy</xades:SPURI></xades:SigPolicyQualifier></xades:SigPolicyQualifiers></xades:SignaturePolicyId></xades:SignaturePolicyIdentifier></xades:SignedSignatureProperties></xades:SignedProperties></xades:QualifyingProperties></ds:Object>
</ds:Signature>

这是我打印错误XML签名的验证码。

void verifyXML(OptionSet options) {
    try {
        File file = new File(options.valueOf("certificate").toString());
        FileSystemDirectoryCertStore store = new FileSystemDirectoryCertStore(file.getAbsoluteFile().getParent());
        FileInputStream fis = new FileInputStream(options.valueOf("certificate").toString());
        KeyStore ks = KeyStore.getInstance("pkcs12");
        ks.load(fis, options.valueOf("password").toString().toCharArray());                         
        fis.close();            
        CertificateValidationProvider provider = new PKIXCertificateValidationProvider(ks, false, store.getStore());

        XadesVerifier verifier = new XadesVerificationProfile(provider).newVerifier();

        Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(options.valueOf("input").toString()));

        doc.getDocumentElement().normalize();

        Element sig = (Element)doc.getElementsByTagName("ds:Signature").item(0);
        PrintWriter out = null;
        try {
            out = new PrintWriter(options.valueOf("output").toString());
            XAdESVerificationResult result = verifier.verify(sig, null);
            out.println("true");
        } catch (Exception e) {
            System.out.println(e.getMessage());
            out.println("false");
        }
        out.close();


    } catch (Exception ex) {
        System.err.println("Error: " + ex.getMessage());
    }

如果有人能指出问题所在,我将不胜感激。谢谢!

更新 检查堆栈跟踪后,我发现以下异常:

    xades4j.xml.unmarshalling.UnmarshalException: Bad XML signature
    at xades4j.verification.XadesVerifierImpl.verify(XadesVerifierImpl.java:130)
    at com.qprotex.Main.verifyXML(Main.java:190)
    at com.qprotex.Main.<init>(Main.java:99)
    at com.qprotex.Main.main(Main.java:81)
Caused by: org.apache.xml.security.exceptions.XMLSecurityException: Cannot create a null:null from a http://www.w3.org/2000/09/xmldsig#:Signature element

我发现this post提到同样的错误,在验证方法中启用factory.setNamespaceAware(true)后,我得到以下异常:

java.lang.NullPointerException
at xades4j.xml.unmarshalling.FromXmlSignaturePolicyConverter.getLocationUrl(FromXmlSignaturePolicyConverter.java:71)
at xades4j.xml.unmarshalling.FromXmlSignaturePolicyConverter.convertFromObjectTree(FromXmlSignaturePolicyConverter.java:64)
at xades4j.xml.unmarshalling.FromXmlSignaturePolicyConverter.convertFromObjectTree(FromXmlSignaturePolicyConverter.java:1)
at xades4j.xml.unmarshalling.UnmarshallerModule.convertProperties(UnmarshallerModule.java:64)
at xades4j.xml.unmarshalling.DefaultQualifyingPropertiesUnmarshaller.unmarshalProperties(DefaultQualifyingPropertiesUnmarshaller.java:83)
at xades4j.verification.XadesVerifierImpl.verify(XadesVerifierImpl.java:175)
at com.qprotex.Main.verifyXML(Main.java:190)
at com.qprotex.Main.<init>(Main.java:99)
at com.qprotex.Main.main(Main.java:81)

更新2 由于我没有使用SignaturePolicyIdentifier,我确实更改了我的sign函数以使用XadesBesSigningProfile而不是XadesEpesSigningProfile。我现在唯一的问题是它只有在我使用KeyStore.getInstance时才有效(&#34; Windows-ROOT&#34;)

这有效:

            KeyStore trustAnchors = KeyStore.getInstance("Windows-ROOT"); 
            trustAnchors.load(null);

            CertificateValidationProvider provider = new PKIXCertificateValidationProvider(trustAnchors, false);

但这不起作用,我找回了&#34; xades4j.providers.CannotBuildCertificationPathException:信任锚KeyStore没有可信任的证书条目&#34;异常。

            FileInputStream fis = new FileInputStream(options.valueOf("certificate").toString());
            KeyStore ks = KeyStore.getInstance("pkcs12");
            ks.load(fis, options.valueOf("password").toString().toCharArray());                         
            fis.close();
            CertificateValidationProvider provider = new PKIXCertificateValidationProvider(ks, false);

1 个答案:

答案 0 :(得分:1)

  • 您可能在库中发现了一个错误。我创建了an issue on Github来跟踪这个。如果你愿意,可以订阅。
  • 如果它确实是一个错误,它必须已在v1.4.0中引入,所以现在,你可以尝试使用v1.3.2。
  • 既然你说你不需要SignaturePolicyIdentifier,你为什么要使用XAdES-EPES?您可以使用XadesBesSigningProfile代替并生成XAdES-BES签名。
  • 假设您仍需要XAdES-EPES,请注意您需要使用withPolicyDocumentProvider上的XadesVerificationProfile指定如何获取签名政策文档以进行验证。
  • 我不确定base64字符串之后的编码字符,但让我们等一下,看看这是不是问题。

更新后2:

异常消息似乎很清楚。您的目标是使用签名证书作为信任锚吗?密钥库上的条目类型必须是“信任锚”。我想你应该添加两个条目:一个带有cert / key(类型为“certificate”),另一个带有cert(类型为“trust anchor”)。或者使用单独的密钥库。