Ответ 1
Как я могу найти фактическую строку XML, подписанную API, я попытался прочитать поток входных данных после включения javax.xml.crypto.dsig.cacheReference, но это не сработало при подписании, оно работало только при проверке?
К счастью, реализация по умолчанию, предполагая, что используется, предлагает некоторые возможности отладки. Здесь приведен пример настроек для входа в консоль с использованием детализации FINEST
, хотя FINE
кажется достаточно.
handlers= java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = FINER
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
org.jcp.xml.dsig.internal.level = FINER
com.sun.org.apache.xml.internal.security.level = FINER
Вы можете сохранить это как файл logging.properties
и указать свой путь через опцию командной строки -Djava.util.logging.config.file
, хотя это, возможно, также возможно сделать программным путем.
Таким образом, на самом деле будет отображаться подписанный канонизированный XML-код, который отвечает на это:
Является ли пространство имен фактически опущено из расчета дайджеста полезной нагрузки при добавлении элемента в документ с помощью createElementNS и appendChild?
В самом деле, это так. Запустив код, я увидел это в протоколе как канонизированный XML для подписания.
<DataPDU xmlns="urn:swift:saa:xsd:saa.2.0"><Revision>2.0.6</Revision><Saa:LAU></Saa:LAU></DataPDU>
Обратите внимание, что существует префикс Saa
, но декларация пространства имен отсутствует. Тем не менее, информация находится там, потому что после преобразования в результате появляется объявление с префиксом. Это объясняет, почему изменения в URI пространства имен не приводят к другому дайджесту, но изменение префикса. Не знаю, почему это происходит. Похоже, что этого не должно быть, но правила для того, что включено в каноническую версию, настолько запутывают в спецификациях, что либо я что-то упускаю, либо это ошибка в реализации. Обход для этого заключается в добавлении этой строки после создания LAUElement
:
LAUElement.setAttribute("xmlns:Saa", "urn:swift:saa:xsd:saa.2.0");
Я подумал, что DOMResult использовал создатель документа, который не был настроен на использование пространства имен, но я пробовал это без разницы:
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder docBuilder = domFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
DOMResult domResult = new DOMResult(doc);
marshaller.marshal(myDataPDU, domResult);
Это может быть ошибка самого DOMResult. Обходной путь немного испорчен, но он выполняет эту работу. Это фактически меняет дайджест. Я пробовал это, предоставив как LAUElement
, так и корень документа как вход для DOMSignContext
, который в обоих случаях приводит к одной и той же сигнатуре (дайджесту и значению), хотя в последнем случае подпись добавляется к root, а не элемент.
Это также учит нас, что весь документ используется как вход для подписания, даже когда вы поставляете элемент и используете метод исключительной канонизации. Он изменяется только там, где размещена подпись. Решение URI находит предков до корневого элемента и всего под ним. Это немного меня удивило.
Выполняя вышеуказанное, мне удалось правильно проверить подпись, используя приведенный ниже код:
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");
NodeList sigList = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (sigList.getLength() == 0) {
throw new Exception("Cannot find Signature element");
}
String secretKey = "Abcd1234abcd1234Abcd1234abcd1234";
SecretKeySpec secret_key = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
DOMValidateContext valContext = new DOMValidateContext(secret_key, sigList.item(0));
XMLSignature signature = factory.unmarshalXMLSignature(valContext);
System.out.println("Core validity: " + signature.validate(valContext));
Кажется, что проблема заключалась в том, что при отмене объявления пространства имен сгенерированный дайджест отличался от того, который был сгенерирован при проверке.
Есть ли способ предоставить через JAXB префикс для одного и того же корневого пространства имен только для одного элемента?
Короткий ответ: нет тривиального способа сделать это. Длинный ответ можно найти здесь: fooobar.com/info/419715/...
EDIT:
Дополнительная заметка. Будьте осторожны с трансформатором для вывода подписанного документа в файл. Если он установил для отступов вывод, в результате файл фактически имел бы другой дайджест. Там пробелы, которые можно игнорировать (например, в <empty />
vs <empty/>
), но внутри элементов, которые обычно рассматриваются как текстовые узлы и часть канонизированной версии, по которой вычисляется дайджест.