Xades4j Проверка подписи Xades — EPES: неверная подпись XML

Я использую Xades4J для разработки простой Java-программы, которая подписывает и проверяет XML с помощью XADES-EPES. Процесс подписи, кажется, работает правильно и генерирует действительный подписанный XML (хотя нам не нужен раздел SignaturePolicyIdentifier).

*аргументы параметров взяты из библиотеки 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>

И это мой код проверки, который печатает подпись Bad 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

Я нашел этот пост, в котором упоминается та же ошибка, и после включения 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, я изменил свою функцию подписи, чтобы использовать XadesBesSigningProfile вместо XadesEpesSigningProfile. Единственная проблема, которая у меня есть сейчас, заключается в том, что она работает, только если я использую KeyStore.getInstance("Windows-ROOT")

Это работает:

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

            CertificateValidationProvider provider = new PKIXCertificateValidationProvider(trustAnchors, false);

Но это не работает, и я получаю исключение «xades4j.providers.CannotBuildCertificationPathException: Trust привязки KeyStore не имеет записей доверенных сертификатов».

            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);

person Miguel Febres    schedule 01.12.2017    source источник
comment
Вы решили это? У меня похожая проблема stackoverflow.com/questions/51874419/   -  person Michael Chourdakis    schedule 16.08.2018


Ответы (1)


  • Возможно, вы нашли ошибку в библиотеке. Я создал задачу на Github, чтобы отслеживать это. Подпишитесь на это, если хотите.
  • Если это действительно ошибка, она должна быть представлена ​​в версии 1.4.0, так что пока вы можете попробовать использовать версию 1.3.2.
  • Поскольку вы говорите, что вам не нужен SignaturePolicyIdentifier, почему вы используете XAdES-EPES? Вместо этого вы можете использовать XadesBesSigningProfile и создать подпись XAdES-BES.
  • Предполагая, что вам все еще нужен XAdES-EPES, обратите внимание, что вам нужно будет указать, как получить документы политики подписи для проверки, используя withPolicyDocumentProvider на XadesVerificationProfile.
  • Я не уверен насчет закодированных символов после строк base64, но давайте подождем, чтобы увидеть, является ли это проблемой.

После ОБНОВЛЕНИЯ 2:

Сообщение об исключении кажется ясным. Ваша цель — использовать сертификат подписи в качестве якоря доверия? Тип записи в хранилище ключей должен быть «якорем доверия». Я думаю, вы должны добавить 2 записи: одну с сертификатом/ключом (введите «сертификат»), а другую только с сертификатом (введите «якорь доверия»). Или используйте отдельные хранилища ключей.

person lgoncalves    schedule 03.12.2017
comment
Спасибо за вашу помощь Igoncalves. Я изменил свою функцию подписи, чтобы использовать XadesBesSigningProfile, и теперь она работает, но у меня есть небольшая проблема. Пожалуйста, проверьте мое обновление 2. Спасибо. - person Miguel Febres; 04.12.2017