diff --git a/README.md b/README.md
index e6cf476c..f2087dd0 100644
--- a/README.md
+++ b/README.md
@@ -49,4 +49,4 @@ The following information needs to be available on the account:
`accountSettings.setCertificate`: The x509 certificate fingerprint. This is provided from the identity provider when setting up the relationship, for this version the certificate must be 1024-bit.
-In OneLogin, for this sample project, you'll want to set the SAML Consumer URL to "http://localhost:8080" and the SAML Audience and SAML Recipient to "http://localhost:8080/consume.jsp"
+The DEV instance of Sungard SSO server is preconfigured for this sample project. The SAML Consumer URL is set to `http://localhost:8080` whereas the Audience and Recipient have been preconfigured to `http://localhost:8080/consume.jsp`. All the SP metadata used to configure the system are available under the `sample/metadata/` folder in the source tree.
diff --git a/sample/metadata/sp_mywebapp_extdata.xml b/sample/metadata/sp_mywebapp_extdata.xml
new file mode 100755
index 00000000..bb0a15a2
--- /dev/null
+++ b/sample/metadata/sp_mywebapp_extdata.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+ SAML2_mywebapp_SungardAS
+
+
+
+
+
+ false
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+ false
+
+
+ false
+
+
+
+
+
+
+
+ false
+
+
+
+
+ false
+
+
+
+
diff --git a/sample/metadata/sp_mywebapp_metadata.xml b/sample/metadata/sp_mywebapp_metadata.xml
new file mode 100755
index 00000000..3315b3fb
--- /dev/null
+++ b/sample/metadata/sp_mywebapp_metadata.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+
+
+
diff --git a/sample/src/main/webapp/consume.jsp b/sample/src/main/webapp/consume.jsp
index aa60ac68..6e126163 100644
--- a/sample/src/main/webapp/consume.jsp
+++ b/sample/src/main/webapp/consume.jsp
@@ -1,5 +1,5 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
-<%@ page import="com.onelogin.*,com.onelogin.saml.*" %>
+<%@ page import="java.util.*,com.onelogin.*,com.onelogin.saml.*" %>
@@ -8,43 +8,48 @@
<%
- String certificateS ="MIIDGTCCAoKgAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzET"+
-"MBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYD"+
-"VQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2"+
-"MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQI"+
-"DApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9u"+
-"ZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIGfMA0GCSqGSIb3DQEB"+
-"AQUAA4GNADCBiQKBgQCsalDL15zSKeEGy9c0Hao7+G02x6k/MlZuCwEvkPKUcl9Q"+
-"dGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxv"+
-"Z2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEANZvz"+
-"lB1Aq84AdOvsn2XKxBB/PmNZLqnM1VWRPaNcvjafx7eHd5qayXFNQz+bOLujENmg"+
-"Am5padbydG89SeefpOGcY2TMsVt0RUzxTnN3Zq5G6Ja2fAKOEX01ejdoPPMmStqq"+
-"Sw8k1wPUU8uLYJG5wmjf0rCb8RVaeAwMc+wcEIA=";
+ String cert =
+ "MIICQDCCAakCBEeNB0swDQYJKoZIhvcNAQEEBQAwZzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh"+
+ "bGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMQwwCgYDVQQKEwNTdW4xEDAOBgNVBAsTB09w"+
+ "ZW5TU08xDTALBgNVBAMTBHRlc3QwHhcNMDgwMTE1MTkxOTM5WhcNMTgwMTEyMTkxOTM5WjBnMQsw"+
+ "CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExDDAK"+
+ "BgNVBAoTA1N1bjEQMA4GA1UECxMHT3BlblNTTzENMAsGA1UEAxMEdGVzdDCBnzANBgkqhkiG9w0B"+
+ "AQEFAAOBjQAwgYkCgYEArSQc/U75GB2AtKhbGS5piiLkmJzqEsp64rDxbMJ+xDrye0EN/q1U5Of+"+
+ "RkDsaN/igkAvV1cuXEgTL6RlafFPcUX7QxDhZBhsYF9pbwtMzi4A4su9hnxIhURebGEmxKW9qJNY"+
+ "Js0Vo5+IgjxuEWnjnnVgHTs1+mq5QYTA7E6ZyL8CAwEAATANBgkqhkiG9w0BAQQFAAOBgQB3Pw/U"+
+ "QzPKTPTYi9upbFXlrAKMwtFf2OW4yvGWWvlcwcNSZJmTJ8ARvVYOMEVNbsT4OFcfu2/PeYoAdiDA"+
+ "cGy/F2Zuj8XJJpuQRSE6PtQqBuDEHjjmOQJ0rV/r8mO1ZCtHRhpZ5zYRjhRC9eCbjx9VrFax0JDC"+
+ "/FfwWigmrW0Y0Q==";
// user account specific settings. Import the certificate here
AccountSettings accountSettings = new AccountSettings();
- accountSettings.setCertificate(certificateS);
+ accountSettings.setCertificate(cert);
Response samlResponse = new Response(accountSettings);
samlResponse.loadXmlFromBase64(request.getParameter("SAMLResponse"));
samlResponse.setDestinationUrl(request.getRequestURL().toString());
- if (samlResponse.isValid()) {
-
+ if (samlResponse.isValid())
+ {
// the signature of the SAML Response is valid. The source is trusted
- java.io.PrintWriter writer = response.getWriter();
- writer.write("OK!");
- String nameId = samlResponse.getNameId();
- writer.write(nameId);
- writer.flush();
-
- } else {
-
+ java.io.PrintWriter writer = response.getWriter();
+ writer.write("OK!");
+ String nameId = samlResponse.getNameId();
+ writer.write("
Hello " + nameId);
+ writer.write("! You have been authenticated. The SSO server returned the following attributes:
");
+ Map attrs = samlResponse.getAttributes();
+ for(String attr : attrs.keySet())
+ {
+ writer.write("
*** " + attr + ": " + attrs.get(attr));
+ }
+ writer.flush();
+ }
+ else
+ {
// the signature of the SAML Response is not valid
- java.io.PrintWriter writer = response.getWriter();
- writer.write("Failed");
- writer.flush();
-
+ java.io.PrintWriter writer = response.getWriter();
+ writer.write("FAILED: The SAML Response has expired or invalid.");
+ writer.flush();
}
%>
diff --git a/sample/src/main/webapp/index.jsp b/sample/src/main/webapp/index.jsp
index cc4a8801..4e2f7dff 100644
--- a/sample/src/main/webapp/index.jsp
+++ b/sample/src/main/webapp/index.jsp
@@ -22,11 +22,28 @@
AccountSettings accSettings = new AccountSettings();
// The URL at the Identity Provider where to the authentication request should be sent
- accSettings.setIdpSsoTargetUrl("https://app.onelogin.com/saml/signon/20956");
+ accSettings.setIdpSsoTargetUrl("https://qa-sungard.sso.sungardas.io/service/saml2/idp");
+
+ String cert =
+ "MIICQDCCAakCBEeNB0swDQYJKoZIhvcNAQEEBQAwZzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh"+
+ "bGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMQwwCgYDVQQKEwNTdW4xEDAOBgNVBAsTB09w"+
+ "ZW5TU08xDTALBgNVBAMTBHRlc3QwHhcNMDgwMTE1MTkxOTM5WhcNMTgwMTEyMTkxOTM5WjBnMQsw"+
+ "CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExDDAK"+
+ "BgNVBAoTA1N1bjEQMA4GA1UECxMHT3BlblNTTzENMAsGA1UEAxMEdGVzdDCBnzANBgkqhkiG9w0B"+
+ "AQEFAAOBjQAwgYkCgYEArSQc/U75GB2AtKhbGS5piiLkmJzqEsp64rDxbMJ+xDrye0EN/q1U5Of+"+
+ "RkDsaN/igkAvV1cuXEgTL6RlafFPcUX7QxDhZBhsYF9pbwtMzi4A4su9hnxIhURebGEmxKW9qJNY"+
+ "Js0Vo5+IgjxuEWnjnnVgHTs1+mq5QYTA7E6ZyL8CAwEAATANBgkqhkiG9w0BAQQFAAOBgQB3Pw/U"+
+ "QzPKTPTYi9upbFXlrAKMwtFf2OW4yvGWWvlcwcNSZJmTJ8ARvVYOMEVNbsT4OFcfu2/PeYoAdiDA"+
+ "cGy/F2Zuj8XJJpuQRSE6PtQqBuDEHjjmOQJ0rV/r8mO1ZCtHRhpZ5zYRjhRC9eCbjx9VrFax0JDC"+
+ "/FfwWigmrW0Y0Q==";
+ accSettings.setCertificate(cert);
// Generate an AuthRequest and send it to the identity provider
AuthRequest authReq = new AuthRequest(appSettings, accSettings);
- String reqString = accSettings.getIdp_sso_target_url()+"?SAMLRequest=" + URLEncoder.encode(authReq.getRequest(AuthRequest.base64),"UTF-8");
+ String reqString
+ = accSettings.getIdp_sso_target_url()
+ +"?SAMLRequest="
+ + URLEncoder.encode(authReq.getRequest(AuthRequest.base64),"UTF-8");
response.sendRedirect(reqString);
%>
diff --git a/src/main/java/com/onelogin/saml/AuthRequest.java b/src/main/java/com/onelogin/saml/AuthRequest.java
index 14d900d3..382f4108 100644
--- a/src/main/java/com/onelogin/saml/AuthRequest.java
+++ b/src/main/java/com/onelogin/saml/AuthRequest.java
@@ -16,82 +16,85 @@
public class AuthRequest {
- private final String id;
- private final String issueInstant;
- private final AppSettings appSettings;
- public static final int base64 = 1;
- private Deflater deflater;
-
- public AuthRequest(AppSettings appSettings, AccountSettings accountSettings){
- this.appSettings = appSettings;
- id="_"+UUID.randomUUID().toString();
- SimpleDateFormat simpleDf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
- issueInstant = simpleDf.format(new Date());
- }
-
- public String getRequest(int format) throws XMLStreamException, IOException {
- String result = "";
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
- XMLOutputFactory factory = XMLOutputFactory.newInstance();
- XMLStreamWriter writer = factory.createXMLStreamWriter(baos);
-
- writer.writeStartElement("samlp", "AuthnRequest", "urn:oasis:names:tc:SAML:2.0:protocol");
- writer.writeNamespace("samlp","urn:oasis:names:tc:SAML:2.0:protocol");
-
- writer.writeAttribute("ID", id);
- writer.writeAttribute("Version", "2.0");
- writer.writeAttribute("IssueInstant", this.issueInstant);
- writer.writeAttribute("ProtocolBinding", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
- writer.writeAttribute("AssertionConsumerServiceURL", this.appSettings.getAssertionConsumerServiceUrl());
-
- writer.writeStartElement("saml","Issuer","urn:oasis:names:tc:SAML:2.0:assertion");
- writer.writeNamespace("saml","urn:oasis:names:tc:SAML:2.0:assertion");
- writer.writeCharacters(this.appSettings.getIssuer());
- writer.writeEndElement();
-
- writer.writeStartElement("samlp", "NameIDPolicy", "urn:oasis:names:tc:SAML:2.0:protocol");
-
- writer.writeAttribute("Format", "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified");
- writer.writeAttribute("AllowCreate", "true");
- writer.writeEndElement();
-
- writer.writeStartElement("samlp","RequestedAuthnContext","urn:oasis:names:tc:SAML:2.0:protocol");
-
- writer.writeAttribute("Comparison", "exact");
-
- writer.writeStartElement("saml","AuthnContextClassRef","urn:oasis:names:tc:SAML:2.0:assertion");
- writer.writeNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion");
- writer.writeCharacters("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport");
- writer.writeEndElement();
-
- writer.writeEndElement();
- writer.writeEndElement();
- writer.flush();
+ private final String id;
+ private final String issueInstant;
+ private final AppSettings appSettings;
+ public static final int base64 = 1;
+ private Deflater deflater;
+
+ public AuthRequest(AppSettings appSettings, AccountSettings accountSettings){
+ this.appSettings = appSettings;
+ id="_"+UUID.randomUUID().toString();
+ SimpleDateFormat simpleDf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ issueInstant = simpleDf.format(new Date());
+ }
+
+ public String getRequest(int format) throws XMLStreamException, IOException {
+ String result = "";
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ XMLOutputFactory factory = XMLOutputFactory.newInstance();
+ XMLStreamWriter writer = factory.createXMLStreamWriter(baos);
+
+ writer.writeStartElement("samlp", "AuthnRequest", "urn:oasis:names:tc:SAML:2.0:protocol");
+ writer.writeNamespace("samlp","urn:oasis:names:tc:SAML:2.0:protocol");
+
+ writer.writeAttribute("ID", id);
+ writer.writeAttribute("Version", "2.0");
+ writer.writeAttribute("IssueInstant", this.issueInstant);
+ writer.writeAttribute("ProtocolBinding", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
+ writer.writeAttribute("ProviderName", "https://www.mywebapp.com");
+ writer.writeAttribute("Destination", "https://qa-sungard.sso.sungardas.io/service/saml2/idp");
+ writer.writeAttribute("AssertionConsumerServiceURL", this.appSettings.getAssertionConsumerServiceUrl());
+
+ writer.writeStartElement("saml","Issuer","urn:oasis:names:tc:SAML:2.0:assertion");
+ writer.writeNamespace("saml","urn:oasis:names:tc:SAML:2.0:assertion");
+ writer.writeCharacters(this.appSettings.getIssuer());
+ writer.writeEndElement();
+
+ writer.writeStartElement("samlp", "NameIDPolicy", "urn:oasis:names:tc:SAML:2.0:protocol");
+
+ //writer.writeAttribute("Format", "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified");
+ writer.writeAttribute("Format", "urn:oasis:names:tc:SAML:2.0:nameid-format:transient");
+ writer.writeAttribute("AllowCreate", "true");
+ writer.writeEndElement();
+
+ writer.writeStartElement("samlp","RequestedAuthnContext","urn:oasis:names:tc:SAML:2.0:protocol");
+
+ writer.writeAttribute("Comparison", "exact");
+
+ writer.writeStartElement("saml","AuthnContextClassRef","urn:oasis:names:tc:SAML:2.0:assertion");
+ writer.writeNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion");
+ writer.writeCharacters("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport");
+ writer.writeEndElement();
+
+ writer.writeEndElement();
+ writer.writeEndElement();
+ writer.flush();
- result = encodeSAMLRequest(baos.toByteArray());
- return result;
- }
+ result = encodeSAMLRequest(baos.toByteArray());
+ return result;
+ }
- private String encodeSAMLRequest(byte[] pSAMLRequest) throws RuntimeException {
+ private String encodeSAMLRequest(byte[] pSAMLRequest) throws RuntimeException {
- Base64 base64Encoder = new Base64();
+ Base64 base64Encoder = new Base64();
- try {
- ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
- Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
+ try {
+ ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
+ Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
- DeflaterOutputStream def = new DeflaterOutputStream(byteArray, deflater);
- def.write(pSAMLRequest);
- def.close();
- byteArray.close();
+ DeflaterOutputStream def = new DeflaterOutputStream(byteArray, deflater);
+ def.write(pSAMLRequest);
+ def.close();
+ byteArray.close();
- String stream = new String(base64Encoder.encode(byteArray.toByteArray()));
+ String stream = new String(base64Encoder.encode(byteArray.toByteArray()));
- return stream.trim();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
+ return stream.trim();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/src/main/java/com/onelogin/saml/Response.java b/src/main/java/com/onelogin/saml/Response.java
index bde1b600..40ee6b71 100644
--- a/src/main/java/com/onelogin/saml/Response.java
+++ b/src/main/java/com/onelogin/saml/Response.java
@@ -37,197 +37,203 @@
public class Response {
- private Document xmlDoc;
- private NodeList assertions;
- private Element rootElement;
- private final AccountSettings accountSettings;
- private final Certificate certificate;
- private String currentUrl;
-
- public Response(AccountSettings accountSettings) throws CertificateException {
- this.accountSettings = accountSettings;
- certificate = new Certificate();
- certificate.loadCertificate(this.accountSettings.getCertificate());
- }
-
- public void loadXml(String xml) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
- DocumentBuilderFactory fty = DocumentBuilderFactory.newInstance();
- fty.setNamespaceAware(true);
- // XMLConstants with FEATURE_SECURE_PROCESSING prevents external document access. (XXE/XEE Possible Attacks).
- fty.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
- DocumentBuilder builder = fty.newDocumentBuilder();
- ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes());
- xmlDoc = builder.parse(bais);
- // Loop through the doc and tag every element with an ID attribute as an XML ID node.
- XPath xpath = XPathFactory.newInstance().newXPath();
- XPathExpression expr = xpath.compile("//*[@ID]");
- NodeList nodeList = (NodeList) expr.evaluate(xmlDoc, XPathConstants.NODESET);
- for (int i=0; i attributes = new HashMap();
- NodeList nodes = xmlDoc.getElementsByTagNameNS("urn:oasis:names:tc:SAML:2.0:assertion", "Attribute");
-
- if (nodes.getLength() != 0) {
- for (int i = 0; i < nodes.getLength(); i++) {
- NamedNodeMap attrName = nodes.item(i).getAttributes();
- String attName = attrName.getNamedItem("Name").getNodeValue();
- NodeList children = nodes.item(i).getChildNodes();
-
- ArrayList attrValues = new ArrayList();
- for (int j = 0; j < children.getLength(); j++) {
- attrValues.add(children.item(j).getTextContent());
- }
- attributes.put(attName, attrValues);
- }
- } else {
- return null;
- }
- return attributes;
- }
-
- private boolean setIdAttributeExists() {
- for (Method method : Element.class.getDeclaredMethods()) {
- if (method.getName().equals("setIdAttribute")) {
- return true;
- }
- }
- return false;
- }
-
- private void tagIdAttributes(Document xmlDoc) {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- public void setDestinationUrl(String urld){
- currentUrl = urld;
- }
+
+
+// if (setIdAttributeExists()) {
+// tagIdAttributes(xmlDoc);
+// }
+
+ X509Certificate cert = certificate.getX509Cert();
+ DOMValidateContext ctx = new DOMValidateContext(cert.getPublicKey(), nodes.item(0));
+ XMLSignatureFactory sigF = XMLSignatureFactory.getInstance("DOM");
+ XMLSignature xmlSignature = sigF.unmarshalXMLSignature(ctx);
+
+ return xmlSignature.validate(ctx);
+ }
+
+ public String getNameId() throws Exception {
+ NodeList nodes = xmlDoc.getElementsByTagNameNS("urn:oasis:names:tc:SAML:2.0:assertion", "NameID");
+ if (nodes.getLength() == 0) {
+ throw new Exception("No name id found in Document.");
+ }
+ return nodes.item(0).getTextContent();
+ }
+
+ public String getAttribute(String name) {
+ HashMap attributes = getAttributes();
+ if (!attributes.isEmpty()) {
+ return attributes.get(name).toString();
+ }
+ return null;
+ }
+
+ public HashMap getAttributes() {
+ HashMap attributes = new HashMap();
+ NodeList nodes = xmlDoc.getElementsByTagNameNS("urn:oasis:names:tc:SAML:2.0:assertion", "Attribute");
+
+ if (nodes.getLength() != 0) {
+ for (int i = 0; i < nodes.getLength(); i++) {
+ NamedNodeMap attrName = nodes.item(i).getAttributes();
+ String attName = attrName.getNamedItem("Name").getNodeValue();
+ NodeList children = nodes.item(i).getChildNodes();
+
+ ArrayList attrValues = new ArrayList();
+ for (int j = 0; j < children.getLength(); j++) {
+ attrValues.add(children.item(j).getTextContent());
+ }
+ attributes.put(attName, attrValues);
+ }
+ } else {
+ return null;
+ }
+ return attributes;
+ }
+
+ private boolean setIdAttributeExists() {
+ for (Method method : Element.class.getDeclaredMethods()) {
+ if (method.getName().equals("setIdAttribute")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void tagIdAttributes(Document xmlDoc) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void setDestinationUrl(String urld){
+ currentUrl = urld;
+ }
}