package li.strolch.utils.helper;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import li.strolch.utils.helper.XmlDomSigner;
public class XmlSignHelperTest {
private static XmlDomSigner helper;
@BeforeClass
public static void beforeClass() {
helper = new XmlDomSigner(new File("src/test/resources/test.jks"), "client", "server",
"changeit".toCharArray());
}
@Test
public void shouldSign() {
Document document = createDoc();
helper.sign(document);
assertSignatureElemExists(document);
}
@Test
public void shouldSignWithNamespaces() {
Document document = createDocWithNamespaces();
// hack for signing with namespaces problem
document = XmlDomSigner.parse(XmlDomSigner.transformToBytes(document));
helper.sign(document);
assertSignatureElemExists(document);
}
private void assertSignatureElemExists(Document document) {
NodeList nl = document.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
assertEquals("Expected exactly one Signature element!", 1, nl.getLength());
}
@Test
public void shouldValidate() {
File signedXmlFile = new File("src/test/resources/SignedXmlFile.xml");
Document document = XmlDomSigner.parse(signedXmlFile);
setIdAttr(document);
helper.validate(document);
}
@Test
public void shouldValidateWithNamespaces() {
File signedXmlFile = new File("src/test/resources/SignedXmlFileWithNamespaces.xml");
Document document = XmlDomSigner.parse(signedXmlFile);
setIdAttrNs(document);
helper.validate(document);
}
private void setIdAttr(Document document) {
NodeList authnRequestNodes = document.getElementsByTagName("AuthnRequest");
if (authnRequestNodes.getLength() != 1)
throw new IllegalStateException("Multiple or no AuthnRequest Node found in document!");
Element authnRequestNode = (Element) authnRequestNodes.item(0);
authnRequestNode.setIdAttribute("ID", true);
}
private void setIdAttrNs(Document document) {
NodeList authnRequestNodes = document.getElementsByTagNameNS("urn:oasis:names:tc:SAML:2.0:protocol",
"AuthnRequest");
if (authnRequestNodes.getLength() != 1)
throw new IllegalStateException("Multiple or no AuthnRequest Node found in document!");
Element authnRequestNode = (Element) authnRequestNodes.item(0);
authnRequestNode.setIdAttribute("ID", true);
}
@Test
public void shouldSignAndValidate() {
Document document = createDoc();
helper.sign(document);
helper.validate(document);
}
@Test
public void shouldSignAndValidateWithNamespaces() {
Document document = createDocWithNamespaces();
// hack for signing with namespaces problem
document = XmlDomSigner.parse(XmlDomSigner.transformToBytes(document));
helper.sign(document);
helper.validate(document);
}
public static Document createDoc() {
String issuer = "test";
String destination = "test";
String assertionConsumerServiceUrl = "test";
Calendar issueInstant = Calendar.getInstance();
// create dates
SimpleDateFormat simpleDf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
simpleDf.setTimeZone(TimeZone.getTimeZone("UTC"));
String issueInstantS = simpleDf.format(issueInstant.getTime());
String notBeforeS = issueInstantS;
issueInstant.add(Calendar.SECOND, 10);
String notOnOrAfterS = simpleDf.format(issueInstant.getTime());
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder docBuilder;
try {
docBuilder = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException("Failed to configure document builder!", e);
}
Document doc = docBuilder.newDocument();
Element authnReqE = doc.createElement("AuthnRequest");
authnReqE.setAttribute("Version", "2.0");
authnReqE.setAttribute("IssueInstant", issueInstantS);
authnReqE.setAttribute("ProtocolBinding", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
authnReqE.setAttribute("AssertionConsumerServiceURL", assertionConsumerServiceUrl);
authnReqE.setAttribute("Destination", destination);
doc.appendChild(authnReqE);
Element issuerE = doc.createElement("Issuer");
issuerE.setTextContent(issuer);
authnReqE.appendChild(issuerE);
Element conditionsE = doc.createElement("Conditions");
conditionsE.setAttribute("NotBefore", notBeforeS);
conditionsE.setAttribute("NotOnOrAfter", notOnOrAfterS);
authnReqE.appendChild(conditionsE);
return doc;
}
public static Document createDocWithNamespaces() {
String issuer = "test";
String destination = "test";
String assertionConsumerServiceUrl = "test";
Calendar issueInstant = Calendar.getInstance();
// create dates
SimpleDateFormat simpleDf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
simpleDf.setTimeZone(TimeZone.getTimeZone("UTC"));
String issueInstantS = simpleDf.format(issueInstant.getTime());
String notBeforeS = issueInstantS;
issueInstant.add(Calendar.SECOND, 10);
String notOnOrAfterS = simpleDf.format(issueInstant.getTime());
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder docBuilder;
try {
docBuilder = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException("Failed to configure document builder!", e);
}
Document doc = docBuilder.newDocument();
Element authnReqE = doc.createElementNS("urn:oasis:names:tc:SAML:2.0:protocol", "AuthnRequest");
authnReqE.setPrefix("samlp");
authnReqE.setAttribute("Version", "2.0");
authnReqE.setAttribute("IssueInstant", issueInstantS);
authnReqE.setAttribute("ProtocolBinding", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
authnReqE.setAttribute("AssertionConsumerServiceURL", assertionConsumerServiceUrl);
authnReqE.setAttribute("Destination", destination);
doc.appendChild(authnReqE);
Element issuerE = doc.createElementNS("urn:oasis:names:tc:SAML:2.0:assertion", "Issuer");
issuerE.setPrefix("saml");
issuerE.setTextContent(issuer);
authnReqE.appendChild(issuerE);
Element conditionsE = doc.createElementNS("urn:oasis:names:tc:SAML:2.0:assertion", "Conditions");
conditionsE.setPrefix("saml");
conditionsE.setAttribute("NotBefore", notBeforeS);
conditionsE.setAttribute("NotOnOrAfter", notOnOrAfterS);
authnReqE.appendChild(conditionsE);
return doc;
}
}