Из теста apache santuario xmlsignature
Я провожу тест: добавляю подпись в XML-файл, затем сохраняю ее как файл, после этого считываю ее из этого файла и проверяю подпись.
См. комментарии в коде.
Обычный XML-файл:
spade
shovel
Dig PLC, 1 First Ave, Dublin 1, Ireland
Dig PLC, 1 First Ave, Dublin 1, Ireland
Foo B Baz
1234 567890 12345
У меня есть несколько вопросов:
Проверьте подпись, используя сертификат в XML. Если это работает, XML-файл интерпретируется автоматически, зачем нам еще нужен сертификат, загруженный из хранилища ключей?
Я не могу отформатировать сгенерированный XML, содержащий подпись. Если я изменю какую-то часть XML-файла, это приведет к сбою проверки. Как мне отформатировать XML-файл? Или просто оставить его там?
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.xml.namespace.QName;
import org.apache.xml.security.signature.XMLSignature;
import org.junit.Assert;
import org.w3c.dom.Document;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Element;
/**
* This tests using the DOM API of Apache Santuario - XML Security for Java for XML Signature.
*/
public class SignatureDOMTest extends org.junit.Assert {
// Sign + Verify an XML Document using the DOM API
@org.junit.Test
public void testSignatureUsingDOMAPI() throws Exception {
// Read in plaintext document
InputStream sourceDocument =
new FileInputStream(new File("plaintext.xml"));
Document document = XMLUtils.read(sourceDocument, true);
// Set up the Key
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(
this.getClass().getClassLoader().getResource("clientstore.jks").openStream(),
"cspass".toCharArray()
);
Key key = keyStore.getKey("myclientkey", "ckpass".toCharArray());
X509Certificate cert = (X509Certificate)keyStore.getCertificate("myclientkey");
// Sign using DOM
List namesToSign = new ArrayList();
namesToSign.add(new QName("urn:example:po", "PaymentInfo"));
SignatureUtils.signUsingDOM(
document, namesToSign, "http://www.w3.org/2000/09/xmldsig#rsa-sha1", key, cert
);
// Verify using DOM, here it use the cert load from Keystore.
SignatureUtils.verifyUsingDOM(document, namesToSign, cert);
// Those code are my test:
// I write the signed xml to a file, then read from the file.
XMLUtils.outputDOM(document, new FileOutputStream(new File("plaintext_signed.xml")));
// read from the signed file.
Document signedDocument = XMLUtils.read(new FileInputStream(new File("plaintext_signed.xml")), true);
// Verify using DOM
List namesToSign1 = new ArrayList();
namesToSign1.add(new QName("urn:example:po", "PaymentInfo"));
Element sigElement = SignatureUtils.getSignatureElement(signedDocument);
Assert.assertNotNull(sigElement);
SignatureUtils.findElementsToVerify(signedDocument, namesToSign1);
// try to get cert from the node in the xml.
XMLSignature signature = new XMLSignature(sigElement, "");
X509Certificate cert_in_xml = signature.getKeyInfo().getX509Certificate();
// verify the signature by using the cert in the xml.
// If it works, why do we need a cert loaded from the keystore?
SignatureUtils.verifyUsingDOM(signedDocument, namesToSign1, cert_in_xml);
}
}
Включите функции в SignatureUtils.java для обеспечения целостности:
import org.apache.xml.security.Init;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.transforms.Transforms;
import org.junit.Assert;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.security.Key;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.UUID;
/**
* Some utility methods for signing/verifying documents
*/
public final class SignatureUtils {
static {
Init.init();
}
private SignatureUtils() {
// complete
}
/**
* Sign the document using the DOM API of Apache Santuario - XML Security for Java.
* It signs a list of QNames that it finds in the Document via XPath.
*/
public static void signUsingDOM(
Document document,
List namesToSign,
String algorithm,
Key signingKey,
X509Certificate signingCert
) throws Exception {
XMLSignature sig =
new XMLSignature(document, "", algorithm, "http://www.w3.org/2001/10/xml-exc-c14n#");
Element root = document.getDocumentElement();
root.appendChild(sig.getElement());
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
xpath.setNamespaceContext(new DSNamespaceContext());
for (QName nameToSign : namesToSign) {
String expression = "//*[local-name()='" + nameToSign.getLocalPart() + "']";
NodeList elementsToSign =
(NodeList) xpath.evaluate(expression, document, XPathConstants.NODESET);
for (int i = 0; i < elementsToSign.getLength(); i++) {
Element elementToSign = (Element)elementsToSign.item(i);
Assert.assertNotNull(elementToSign);
String id = UUID.randomUUID().toString();
elementToSign.setAttributeNS(null, "Id", id);
elementToSign.setIdAttributeNS(null, "Id", true);
Transforms transforms = new Transforms(document);
transforms.addTransform("http://www.w3.org/2001/10/xml-exc-c14n#");
sig.addDocument("#" + id, transforms, "http://www.w3.org/2000/09/xmldsig#sha1");
}
}
sig.sign(signingKey);
// Find the Signature Element
Element sigElement = getSignatureElement(document);
Assert.assertNotNull(sigElement);
if (signingCert != null) {
sig.addKeyInfo(signingCert);
}
}
/**
* Verify the document using the DOM API of Apache Santuario - XML Security for Java.
* It finds a list of QNames via XPath and uses the DOM API to mark them as having an
* "Id".
*/
public static void verifyUsingDOM(
Document document,
List namesToSign,
X509Certificate cert
) throws Exception {
// Find the Signature Element
Element sigElement = getSignatureElement(document);
Assert.assertNotNull(sigElement);
findElementsToVerify(document, namesToSign);
XMLSignature signature = new XMLSignature(sigElement, "");
// Check we have a KeyInfo
KeyInfo ki = signature.getKeyInfo();
Assert.assertNotNull(ki);
// Check the Signature value
Assert.assertTrue(signature.checkSignatureValue(cert));
}
public static void findElementsToVerify(Document document, List namesToSign) throws XPathExpressionException {
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
xpath.setNamespaceContext(new DSNamespaceContext());
for (QName nameToSign : namesToSign) {
String expression = "//*[local-name()='" + nameToSign.getLocalPart() + "']";
Element signedElement =
(Element) xpath.evaluate(expression, document, XPathConstants.NODE);
Assert.assertNotNull(signedElement);
if (signedElement.hasAttributeNS(null, "Id")) {
signedElement.setIdAttributeNS(null, "Id", true);
}
}
}
public static Element getSignatureElement(Document document) throws XPathExpressionException {
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
xpath.setNamespaceContext(new DSNamespaceContext());
// Find the Signature Element
String expression = "//dsig:Signature[1]";
Element sigElement =
(Element) xpath.evaluate(expression, document, XPathConstants.NODE);
return sigElement;
}
}
Сгенерированный XML-файл с подписью. Он не очень хорошо отформатирован. Я хотел бы его отформатировать, но затем проверка не удалась.
Исходный XML-файл хорошо отформатирован. В настоящее время только узел ds:Signature некорректно отформатирован.
При обработке подписи с помощью библиотеки Apache Как мы узнаем, что XML-файл уже отформатирован? Поскольку XML-файл теперь представлен как объекты Java.
Из теста apache santuario xmlsignature Я провожу тест: добавляю подпись в XML-файл, затем сохраняю ее как файл, после этого считываю ее из этого файла и проверяю подпись. См. комментарии в коде. Обычный XML-файл: [code]
spade
shovel
Dig PLC, 1 First Ave, Dublin 1, Ireland
Dig PLC, 1 First Ave, Dublin 1, Ireland
Foo B Baz 1234 567890 12345
[/code] У меня есть несколько вопросов: [list] [*]Проверьте подпись, используя сертификат в XML. Если это работает, XML-файл интерпретируется автоматически, зачем нам еще нужен сертификат, загруженный из хранилища ключей? [*]Я не могу отформатировать сгенерированный XML, содержащий подпись. Если я изменю какую-то часть XML-файла, это приведет к сбою проверки. Как мне отформатировать XML-файл? Или просто оставить его там? [/list] [code]
/** * This tests using the DOM API of Apache Santuario - XML Security for Java for XML Signature. */ public class SignatureDOMTest extends org.junit.Assert {
// Sign + Verify an XML Document using the DOM API @org.junit.Test public void testSignatureUsingDOMAPI() throws Exception {
// Read in plaintext document InputStream sourceDocument = new FileInputStream(new File("plaintext.xml")); Document document = XMLUtils.read(sourceDocument, true);
// Set up the Key KeyStore keyStore = KeyStore.getInstance("jks"); keyStore.load( this.getClass().getClassLoader().getResource("clientstore.jks").openStream(), "cspass".toCharArray() ); Key key = keyStore.getKey("myclientkey", "ckpass".toCharArray()); X509Certificate cert = (X509Certificate)keyStore.getCertificate("myclientkey");
// Sign using DOM List namesToSign = new ArrayList(); namesToSign.add(new QName("urn:example:po", "PaymentInfo"));
SignatureUtils.signUsingDOM( document, namesToSign, "http://www.w3.org/2000/09/xmldsig#rsa-sha1", key, cert ); // Verify using DOM, here it use the cert load from Keystore. SignatureUtils.verifyUsingDOM(document, namesToSign, cert);
// Those code are my test: // I write the signed xml to a file, then read from the file. XMLUtils.outputDOM(document, new FileOutputStream(new File("plaintext_signed.xml")));
// read from the signed file. Document signedDocument = XMLUtils.read(new FileInputStream(new File("plaintext_signed.xml")), true);
// Verify using DOM List namesToSign1 = new ArrayList(); namesToSign1.add(new QName("urn:example:po", "PaymentInfo"));
Element sigElement = SignatureUtils.getSignatureElement(signedDocument); Assert.assertNotNull(sigElement);
// verify the signature by using the cert in the xml. // If it works, why do we need a cert loaded from the keystore? SignatureUtils.verifyUsingDOM(signedDocument, namesToSign1, cert_in_xml); } } [/code] Включите функции в SignatureUtils.java для обеспечения целостности: [code] import org.apache.xml.security.Init; import org.apache.xml.security.keys.KeyInfo; import org.apache.xml.security.signature.XMLSignature; import org.apache.xml.security.transforms.Transforms; import org.junit.Assert; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import javax.xml.namespace.QName; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.security.Key; import java.security.cert.X509Certificate; import java.util.List; import java.util.UUID;
/** * Some utility methods for signing/verifying documents */ public final class SignatureUtils {
static { Init.init(); }
private SignatureUtils() { // complete }
/** * Sign the document using the DOM API of Apache Santuario - XML Security for Java. * It signs a list of QNames that it finds in the Document via XPath. */ public static void signUsingDOM( Document document, List namesToSign, String algorithm, Key signingKey, X509Certificate signingCert ) throws Exception { XMLSignature sig = new XMLSignature(document, "", algorithm, "http://www.w3.org/2001/10/xml-exc-c14n#"); Element root = document.getDocumentElement(); root.appendChild(sig.getElement());
// Find the Signature Element Element sigElement = getSignatureElement(document); Assert.assertNotNull(sigElement);
if (signingCert != null) { sig.addKeyInfo(signingCert); } }
/** * Verify the document using the DOM API of Apache Santuario - XML Security for Java. * It finds a list of QNames via XPath and uses the DOM API to mark them as having an * "Id". */ public static void verifyUsingDOM( Document document, List namesToSign, X509Certificate cert ) throws Exception { // Find the Signature Element Element sigElement = getSignatureElement(document); Assert.assertNotNull(sigElement);
findElementsToVerify(document, namesToSign);
XMLSignature signature = new XMLSignature(sigElement, "");
// Check we have a KeyInfo KeyInfo ki = signature.getKeyInfo(); Assert.assertNotNull(ki);
// Check the Signature value Assert.assertTrue(signature.checkSignatureValue(cert)); }
public static void findElementsToVerify(Document document, List namesToSign) throws XPathExpressionException { XPathFactory xpf = XPathFactory.newInstance(); XPath xpath = xpf.newXPath(); xpath.setNamespaceContext(new DSNamespaceContext());
public static Element getSignatureElement(Document document) throws XPathExpressionException { XPathFactory xpf = XPathFactory.newInstance(); XPath xpath = xpf.newXPath(); xpath.setNamespaceContext(new DSNamespaceContext());
// Find the Signature Element String expression = "//dsig:Signature[1]"; Element sigElement = (Element) xpath.evaluate(expression, document, XPathConstants.NODE); return sigElement; } } [/code] Сгенерированный XML-файл с подписью. Он не очень хорошо отформатирован. Я хотел бы его отформатировать, но затем проверка не удалась. [code]
[/code] Обновление 1: [list] [*]Исходный XML-файл хорошо отформатирован. В настоящее время только узел ds:Signature некорректно отформатирован. [*]При обработке подписи с помощью библиотеки Apache Как мы узнаем, что XML-файл уже отформатирован? Поскольку XML-файл теперь представлен как объекты Java. [*]Обычно содержимое между > и