Как подписать только определенную часть XML

Я пытаюсь сделать некоторую подпись XML, подписывая только части XML, однако после долгих поисков мне не удалось найти решение.

Я использую Java для подписи XML с использованием преобразования Xpath2 и ЭКСКЛЮЗИВНОЙ канонизации. Если у меня есть следующий XML

<?xml version="1.0" encoding="UTF-8"?>
<msg xmlns="http://someaddress/ad/m1" xmlns:ns1="http://someotheraddres/ad/m2" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">
<header>
    <id>wsfrwerwerwer</id>
    <name>addr</name>
    <somenode>
        <trace>ND</trace>
    </somenode>
</header>
<payload><ns0:addr xmlns:ns0="http://someaddres/ad/m3"><ns2:data xmlns:ns2="http://someaddres/ad/m3">
            <ns2:name>somevalue</ns2:name>
            <ns2:value>354</ns2:value>
        </ns2:data>
    </ns0:addr>
</payload>
</msg>

И подписать его, я получаю следующий вывод (реальные данные заменены на пустышки)

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<msg xmlns="http://someaddress/ad/m1" xmlns:ns1="http://someotheraddres/ad/m2" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">
<header>
    <id>wsfrwerwerwer</id>
    <name>addr</name>
    <somenode>
        <trace>ND</trace>
    </somenode>
</header>
<payload>
    <ns0:addr xmlns:ns0="http://someaddres/ad/m3">
        <ns2:data xmlns:ns2="http://someaddres/ad/m3">
            <ns2:name>somevalue</ns2:name>
            <ns2:value>354</ns2:value>
        </ns2:data>
    </ns0:addr>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
            <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
            <Reference URI="">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2002/06/xmldsig-filter2">
                        <XPath xmlns="http://www.w3.org/2002/06/xmldsig-filter2" xmlns:ns0="http://someaddres/ad/m3" Filter="intersect">//*[local-name()='addr']/*</XPath>
                    </Transform>
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
                <DigestValue>sdlfjdeklsdfngf</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>femhjgklnlkl</SignatureValue>
        <KeyInfo>
            <X509Data>
                <X509Certificate>swerwerwrwerwerwe</X509Certificate>
            </X509Data>
        </KeyInfo>
    </Signature>
</payload>
</msg>

Если я проверяю подпись, все в порядке, но проблема здесь в том, что сразу после этого я выполняю XSLT в XML, который вносит некоторые изменения в некоторые элементы, но не в подписанный элемент (ns0:addr) который остается нетронутым. Даже если я прямо говорю, что только элемент "addr" должен быть подписан, если я пытаюсь внести изменения в любого из его родителей (payload, msg или же addr), то он не проходит подпись, когда (в соответствии с моим пониманием) это не должно. Если я внесу изменения в другие элементы, такие как что-либо внутри заголовка, подпись все еще действительна.

Я проверил выражение XPath (//*[local-name()='addr']/*) и выбирает правильные данные для подписи (ns2:data) но, похоже, он также принимает все элементы, ведущие к нему, начиная с корневого элемента (msg, addr).

Я также пытался использовать различные преобразования, такие как UNION, но это не работает вообще.

Кто-нибудь знает, в чем может быть проблема? Есть ли способ в Java, чтобы увидеть, что именно подписывается при подписи XML для целей отладки?

РЕДАКТИРОВАТЬ:

После запуска xslt будут происходить такие вещи, как перемещение пространств имен из элемента ns0:addr в корневой элемент (msg), а также изменение имени основного элемента и пространства имен с msg на newmsg (и другое пространство имен по умолчанию), но при этом остается подписанные данные (ns2:data) нетронутым

Код, используемый для подписи, является более или менее кодом, упомянутым здесь http://docs.oracle.com/javase/7/docs/technotes/guides/security/xmldsig/XMLDigitalSignature.html

За исключением того, что вместо преобразования ENVELOPED я использую преобразование XPATH2:

Map<String, String> namespaceMap = new HashMap<String, String>(0);
namespaceMap.put("ns0", "http://someaddres/ad/m3");
XPathType xPathType = new XPathType(xPathParameters, Filter.INTERSECT, namespaceMap);
List<XPathType> xPathList = new ArrayList<XPathType>(0);
xPathList.add(xPathType)
XPathFilter2ParameterSpec xPathFilter2ParameterSpec = new XPathFilter2ParameterSpec(xPathList);
transform = fac.newTransform(CanonicalizationMethod.XPATH2, xPathFilter2ParameterSpec);

И также вместо ENVELOPED я использую ЭКСКЛЮЗИВ

canonicalisationMethod = fac.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null);

EDIT2:

Мне удалось включить более точную отладку процесса подписи XML и получил следующее:

FINER: Pre-digested input: 21-Sep-2012 10:51:39 org.jcp.xml.dsig.internal.DigesterOutputStream write FINER: <ns2:data xmlns="http://someaddress/ad/m1" xmlns:ns0="http://someaddres/ad/m3" xmlns:ns1="http://someotheraddres/ad/m2" xmlns:ns2="http://someaddres/ad/m3"> <ns2:name>somevalue</ns2:name> <ns2:value>354</ns2:value> </ns2:data>

Кажется, что он подписывает правильные данные, однако он также добавляет некоторые дополнительные пространства имен к подписи, что заставляет меня задуматься, не в этом ли проблема, поскольку эти пространства имен взяты из родительских элементов.

Кто-нибудь знает, как сделать так, чтобы все дополнительные пространства имен не добавлялись?

2 ответа

Решение

После долгой борьбы с XML-подписями я наконец нашел приемлемое решение (хотя и не идеальное).

Как оказалось, Эксклюзивной канонизации недостаточно. Вам также необходимо добавить Эксклюзивное преобразование после всех других преобразователей. Следуя фрагментам кода, которые я написал выше:

List<Transform> transforms = new ArrayList<Transform>()
transforms.add(transform)
fac.newTransform(CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec) null)

Это сделает так, что любые другие пространства имен вне подписанных элементов не будут приняты во внимание (хотя это имеет дополнительный эффект, что допускается вставка пространства имен внутри подписанного элемента).

Также кажется, что любой элемент в xpath к подписанному элементу будет принят во внимание, поэтому, если у вас есть следующий xpath /root/A/B он будет подписывать тег B, однако вы не сможете изменить имя тега ни A, ни корневых элементов.

Это можно преодолеть с помощью xpath с меньшим количеством элементов, таких как //B,

Я верю, что, возможно, удастся преодолеть и эту проблему, хотя пока я не смог.

Существуют параметры, связанные с пространством имен, которые могут быть переданы в Exclusive XML Canonicalization, которые описывают PrefixList InclusiveNamespaces.

Вы можете попробовать передать ExcC14NParameterSpec в newCanonicalizationMethod(), используя список префиксов, чтобы проверить, влияет ли это на канонизацию пространств имен.

Другие вопросы по тегам