Подтвердить что ты не робот

Неправильное значение дайджест для подписи xml с использованием API цифровой подписи Java XML

Мне нужно отправить подписанный XML файл в правительственное агентство в Бразилии. Проблема в том, что дайджест, рассчитанный по моему Java-коду (используя Java XML Digital Signature API, отличается от того, который был сгенерирован другим инструментом, например XMLSEC.

Вот код, который я использую для создания XML-подписи для некоторого XML node:

private synchronized void sign(XmlObject obj) throws Exception {
        initKeystore();
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
        List<Transform> transformList = new ArrayList<Transform>();
        Transform envelopedTransform = fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null);
        Transform c14NTransform = fac.newTransform("http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
                (TransformParameterSpec) null);
        transformList.add(envelopedTransform);
        transformList.add(c14NTransform);
        Reference ref = fac.newReference("", fac.newDigestMethod(DigestMethod.SHA1, null),
                Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null,
                null);
        SignedInfo si = fac.newSignedInfo(
                fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null),
                fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(new FileInputStream(System.getProperty("javax.net.ssl.keyStore")),
                System.getProperty("javax.net.ssl.keyStorePassword").toCharArray());
        KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry("entry",
                new KeyStore.PasswordProtection(System.getProperty("javax.net.ssl.keyStorePassword").toCharArray()));

        X509Certificate cert = (X509Certificate) keyEntry.getCertificate();

        // Create the KeyInfo containing the X509Data.
        KeyInfoFactory kif = fac.getKeyInfoFactory();
        X509Data xd = kif.newX509Data(Collections.singletonList(cert));
        KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
        // Instantiate the document to be signed.

        Element el = (Element) obj.getDomNode().getFirstChild();
        String id = el.getAttribute("Id");

        DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), el);
        // Create the XMLSignature, but don't sign it yet.
        XMLSignature signature = fac.newXMLSignature(si, ki);
        // Marshal, generate, and sign the enveloped signature.
        signature.sign(dsc);

    }

Если я попытаюсь проверить сгенерированный XML с помощью xmlsec, я получаю следующую ошибку:

$ xmlsec1 --verify consulta.xml 
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=229:obj=sha1:subj=unknown:error=12:invalid data:data and digest do not match
FAIL

Но если я попытаюсь подписать тот же самый файл (consult.xml) с помощью xmlsec (используя тот же закрытый ключ), эта ошибка исчезнет:

xmlsec1 --sign --output doc-signed.xml --privkey-pem cert.pem consulta.xml

Различия между consult.xml и doc-signed.xml(сгенерированными xmlsec) - это содержимое тегов SignatureValue и DigestValue:

consulta.xml:

<DigestValue>Ajn+tfX7JQc0HPNJ8KbTy7Q2f8I=</DigestValue>
...
<SignatureValue>Q1Ys0Rtj8yL2SA2NaQWQPtmNuHKK8q2anPiyLWlH7mOIjwOs0GEcD0WLUM/BZU0Q
T0kSbDTuJeTR2Ec9wu+hqXXbJ76FpX9/IyHrdyx2hLg0VhB5RRCdyBEuGlmnsFDf
XCyBotP+ZyEzolbTCN9TjCUnXNDWtFP1YapMxAIA0sth0lTpYgGJd8CSvFlHdFj+
ourf8ZGiDmSTkVkKnqDsj8O0ZLmbZfJpH2CBKicX+Ct7MUz2sqVli4XAHs6WXX+E
HJpbOKthS3WCcpG3Kw4K50yIYGTkTbWCYFxOVsMfiVy4W/Qz15Vxb8chD8LM58Ep
m/szmvnTAESxv/piDr7hyw==</SignatureValue>

док-signed.xml:

<DigestValue>w6xElXJrZw3G86OsNkWav+pcKJo=</DigestValue>
...
<SignatureValue>YmUsnlnAY9uLhlfVBLhB8K8ArxMOkOKZJoQ6zgz55ggU6vJCO9+HWJCKQJp6Rvn/w5PCAFY0KJRb
r6/WhHML0Z+Q6TSuIL8OTvJ3iPoROAK6uy07YAflKOUklqk4uxgfMkR+hWMCyfITJVCVZo/MXmPy
g7YwmztoSlGH+p6+ND5n2u47Y2k6SpIvw3CUxwAVQkD0Hsj3G58cbUbrFCoyPVGOe4zJ9c1HPsMW
KzBEFe3QETzPJ8I1B7EEVi5oDvzXE2rMTH4K7zvNGnXpBNGwnSjEOticlqKVP5wyUD7CPwgF1Wgy
Z0njvlaW3K8YmAY8fc70v/+wSO6Fu+0zj18Xeg==</SignatureValue>

Я не буду публиковать остальные файлы, потому что они равны и сделают этот пост еще более подробным.

Из того, что я могу собрать, веб-приложение, которое получает этот XML файл, является .NET-приложением, и оно вычисляет другой дайджест подписи, который мой код Java (как и xmlsec). Любые идеи?

4b9b3361

Ответ 1

Если еще не поздно ответить:

Вы создаете 2 преобразования в коде (envelopedTransform и c14NTransform), но не используете их.

Вы создаете ссылку с помощью одного нового Transform.ENVELOPED. http://www.w3.org/TR/2001/REC-xml-c14n-20010315 (C14N) преобразование не применяется.

Теперь я не знаю точно, что в стандарте безопасности XML говорится, что поведение должно быть в этом случае. Возможно, другие инструменты автоматически применяют преобразование C14N.

Я точно знаю, если вы НЕ укажете какое-либо преобразование. JDK будет применять хотя бы преобразование C14N.

В основном измените, что fac.newReference("",...) и передайте transformList в него вместо Collections.singletonList().

Ответ 2

В идеале элемент DigestValue содержит фактическое значение digest, закодированное в base64, в API подписи Java XML. Не могли бы вы подтвердить, что ваше значение дайджест, созданное из XMLSec, также закодировано в base64.