2 votes

Comment utiliser la bibliothèque XPath de Java en conjonction avec les espaces de noms XML ?

Nous voulons enregistrer l'algorithme utilisé pour signer les réponses SAML au format XML envoyées par l'IdP. Un exemple de réponse ressemble à ceci :

<?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response Destination="https://local.internal.company.de:443/saml/SSO" ID="_f4b74c8bd287c774ff132ad648b74c33"
                 InResponseTo="a15je30i854ji72e54egg30bd3jg622" IssueInstant="2020-08-10T08:54:48.272Z" Version="2.0"
                 xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
        https://local.internal.company.de:4443/idp/shibboleth
    </saml2:Issuer>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:SignedInfo>
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
            <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
            <ds:Reference URI="#_f4b74c8bd287c774ff132ad648b74c33">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
                <ds:DigestValue>+0000000000000000000000000000000000000+0000=</ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>
            00000+00000000000000000000000000000000000000000000000000000000+000000000/000
            000000000000000000000000000000000000000000000/000000000000000000000000000000
            000000000000000000000000000000+0000000000/0000000000+00000000000000000000000
            000000000000000000000000000000000000/000000000000000000000000000000000+00000
            00000000000000000000000000000000000000==
        </ds:SignatureValue>
        <ds:KeyInfo>
            <ds:X509Data>
                <ds:X509Certificate>
                    000000000000000000000000000/000000+00000000000000000000000000000000000000000
                    0000000000000000000000000000000000000000000000000000000000000000000000000000
                    0000000000000000000000000000000000000000000000000000000000000000000000000000
                    00000000000000000000/00000000000000000000000000000000000000/0000000000000000
                    000/00000000000000000+000000000000000000000000/0000000000000/000000000000000
                    0000000000000000000000000000/000000000000000+00000+0000000000000000000000000
                    000000000000000000000000000000000000000000000000000000000000000000000000/000
                    0000000000/00000000000000000000000000000000000000000000000000000/000+0000000
                    0000000000000000000000000000000000000000000000000000000000000000000000000000
                    000000000000000000000000000000000000000000000000000000000000000/00000000000/
                    00000+0000000000000000000/000000000000000000000000000+00000000000000000+0000
                    0000/0000000/00000000000000000/0000000000000000000000000000000000000000/00+0
                    000000000/0/00000000000000000000000000000000+0000000000000000000000000000000
                    0000000000+000000000000000000000000000000000000000000000000000==
                </ds:X509Certificate>
            </ds:X509Data>
        </ds:KeyInfo>
    </ds:Signature>
    <saml2p:Status>
        <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
    </saml2p:Status>
    <saml2:EncryptedAssertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
        <xenc:EncryptedData Id="_00000000000000000000000000000000" Type="http://www.w3.org/2001/04/xmlenc#Element"
                            xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
            <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"
                                   xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <xenc:EncryptedKey Id="_0000000000000000000000000000000" Recipient="de:company:platform"
                                   xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
                    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"
                                           xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
                        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"
                                         xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
                    </xenc:EncryptionMethod>
                    <ds:KeyInfo>
                        <ds:X509Data>
                            <ds:X509Certificate>
                                0000000000000000000000000000000000000000000000000000000000000000000000000000
                                0000000000000000000000000000000000000000000000000000000000000000000000000000
                                0000000000000000000000000000000000000000000000000000000000000000000000000000
                                0000000000000000000000000000000000000000000000000000000000000000000000000000
                                0000000000000000000000000000000000000000000000000000000000000000000000000000
                                0000000000000000000000000000000000000000000000000000000000000000000000000000
                                00000000000000000000000000000000000000000000000000000/0000000000000000000000
                                000000000000000000000000000000000/+00000000000000000000000000000000000000000
                                000000000000000000000000000000000000000000+00000000/000000/00000000000000000
                                000000000000000000000000000000000000/00000000000000000000/00000/000000000000
                                0000000000000000000000000000000000/00000000000000000000000000000000000000+00
                                00000000000000000000000000000/000000000000000000++00000000000000000000+00000
                                0000000000000000000000000000000000000000000000000000000000000000000000000000
                                000000+000000000000000000000000000000000000000000000000000000000000000000000
                                00000000000000000000000000000000000000000000000000/0+00000000000000000000000
                                000000000000000000000+000000000000000000000000000000000000000000000000000000
                                00000000000000000000000000000000000000000000000000000/00000000000/0000000000
                                00000000000000000000000000000000000000000000000+0000000+00000000000000000000
                                000000000000+0000000/0000+0000+000000000000000000000000000/00000000000000000
                                0000000000000000000000000000000000/0000000000000+000000000000000000000000000
                                000/00000000000000000+00000000+0000000000+0000000000000000000000000000000+00
                                0000000000000000000000000000000000000000000000000000000+00000000000000000000
                                0000000000000000000000000000000000/00000000000000000000000000000+00000000000
                                00000000000000000+000000000000000000000000000000000//00000000/00000000000000
                                0000/00/0000000/000000000000+00000000000000000000000000000000000000000000000
                                000+00000000000000000000000000000000000000000000
                            </ds:X509Certificate>
                        </ds:X509Data>
                    </ds:KeyInfo>
                    <xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
                        <xenc:CipherValue>00000000000000000000000000000000000000000000000/000+00/000000000000000000000
                            0000000000000000000000000000+00000000000000000000000000000000000000000000000
                            000000000000/000000000000000000000000000000000000000000000000000000000000000
                            0000000000000000000000000000000000000000000000000000000000000000000000000000
                            0000000000000000000000000000000000000000000000000000000000000000000000000000
                            0000000000000000000000000000000000000000000000+0000000000000000000000000000/
                            0000000000000000000000000000000000000000000000000000000000000000000000000000
                            0000000000000000000000000000000000000000000000000000000000000000000000000000
                            0000+000000000000000000+00000+000000000000000000000000000000000000000000000=
                        </xenc:CipherValue>
                    </xenc:CipherData>
                </xenc:EncryptedKey>
            </ds:KeyInfo>
            <xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
                <xenc:CipherValue>0000000000000000000000/00000000000000000000000000000000000000000000000000000
                    0000000000/000000000000000000000000000000+0000000000000000000000000000000000
                    0000000000000000000000000000000000/00000000000000000000000+00000000000000000
                    00000000000000/000000000000000000000000000/+00000000000000000000000000000000
                    00000000000000000000000000000000000000000000000000000000/0000000000000000000
                    0000000000000000000000000000000000000000000000000000000000000000000000000000
                    0000000/00000000000000000000000000000000000000000000000000000000000000000000
                    0000000000000000000000000000000000000000000000000+00000000000000000000000000
                    0000000000000000000000000000000000000000000000000000000000000000000000+00000
                    00000000000000000+000000000000000000000000/000000000000000000000000000000000
                    000000000000000000==
                </xenc:CipherValue>
            </xenc:CipherData>
        </xenc:EncryptedData>
    </saml2:EncryptedAssertion>
</saml2p:Response>

Pour ce faire, nous utilisons la fonctionnalité XPath intégrée de Java :

private XPathExpression setupXPathExpression(String xPath) throws XPathExpressionException {
    XPath xPathTmp = XPathFactory.newInstance().newXPath();
    xPathTmp.setNamespaceContext(new NamespaceContext() {
        @Override
        public String getNamespaceURI(String prefix) {
            // as per https://coderanch.com/t/649195/java/XPath-escape
            if (prefix == null) {
                throw new NullPointerException("Null prefix");
            } else if ("saml2p".equals(prefix)) {
                return "urn:oasis:names:tc:SAML:2.0:protocol";
            } else if ("saml2".equals(prefix)) {
                return "urn:oasis:names:tc:SAML:2.0:assertion";
            } else if ("ds".equals(prefix)) {
                return "http://www.w3.org/2000/09/xmldsig#";
            } else if ("xenc".equals(prefix)) {
                return "http://www.w3.org/2001/04/xmlenc#";
            }

            return XMLConstants.NULL_NS_URI;
        }

        @Override
        public String getPrefix(String namespaceURI) {
            throw new UnsupportedOperationException();
        }

        @Override
        public Iterator<String> getPrefixes(String namespaceURI) {
            throw new UnsupportedOperationException();
        }
    });

    return xPathTmp.compile(xPath);
}

protected String extractResponseSignatureAlgorithm(String samlResponse) throws IOException, SAXException, XPathExpressionException, ParserConfigurationException {
//   String xPath= "//*";
    String xPath = "//ds:SignatureMethod";
    DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    XPathExpression xPathExpression = setupXPathExpression(xPath);

    InputSource is = new InputSource();
    is.setCharacterStream(new StringReader(samlResponse));
    Document xmlDocument = documentBuilder.parse(is);
    NodeList matches = (NodeList) xPathExpression.evaluate(xmlDocument, XPathConstants.NODESET);

    // manually traverse example XML to verify via debugger that the content is actually there
    Node signatureMethodNode = xmlDocument.getFirstChild().getFirstChild().getNextSibling().getNextSibling().getNextSibling().getFirstChild().getNextSibling().getFirstChild().getNextSibling().getNextSibling().getNextSibling();
    // is in fact "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" when debugging unit test
    String algo = signatureMethodNode.getAttributes().getNamedItem("Algorithm").getNodeValue();

    // is 0 for xPath = "//ds:SignatureMethod";
    // is 32 for xPath= "//*";
    int len = matches.getLength();

    return matches.toString();
}

J'ai développé et vérifié le simple XPath //ds:SignatureMethod en utilisant freeformatter.com . Malheureusement, cela ne correspond à aucun résultat en Java. Quelques recherches sur le web ont suggéré de configurer un contexte d'espace de nom personnalisé que vous pouvez voir dans le fichier setupXPathExpression .

Voici mon test unitaire pour exécuter et déboguer mon code :

@Test
public void testExtractResponseSignatureAlgorithm() throws IOException, SAXException, XPathExpressionException, ParserConfigurationException {
    String samlResponse = IOUtils.toString(this.getClass().getResourceAsStream("/saml/sha256-response.xml"));

    String actual = filter.extractResponseSignatureAlgorithm(samlResponse);
    String expected = "http://www.w3.org/2001/04/xmlenc#sha256";

    Assert.assertEquals(expected, actual);
}

Je ne sais pas comment déboguer davantage le processus de correspondance ou pourquoi ce simple XPath ne correspond pas au nœud qui existe clairement dans les données XML.

2voto

kjhughes Points 5581

En appelant setNamespaceContext() pourrait sembler suffire à indiquer l'intention d'utiliser des espaces de noms, mais en réalité, il faut aussi faire appel à la fonction DocumentBuilderFactory.setNamespaceAware(true) .

Voir aussi Comment XPath traite-t-il les espaces de noms XML ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X