diff --git a/core/src/main/java/com/onelogin/saml2/exception/SettingsException.java b/core/src/main/java/com/onelogin/saml2/exception/SettingsException.java index 0fa11018..47a87952 100644 --- a/core/src/main/java/com/onelogin/saml2/exception/SettingsException.java +++ b/core/src/main/java/com/onelogin/saml2/exception/SettingsException.java @@ -10,7 +10,8 @@ public class SettingsException extends Exception { public static final int PRIVATE_KEY_NOT_FOUND = 4; public static final int PUBLIC_CERT_FILE_NOT_FOUND = 5; public static final int PRIVATE_KEY_FILE_NOT_FOUND = 6; - + public static final int UNSUPPORTED_BINDING = 7; + private int errorCode; public SettingsException(String message, int errorCode) { diff --git a/core/src/main/java/com/onelogin/saml2/util/Util.java b/core/src/main/java/com/onelogin/saml2/util/Util.java index 2467c953..060fe609 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -1081,6 +1081,29 @@ public static Document copyDocument(Document source) throws ParserConfigurationE * @throws XPathExpressionException */ public static String addSign(Document document, PrivateKey key, X509Certificate certificate, String signAlgorithm) throws XMLSecurityException, XPathExpressionException { + return addSign(document, key, certificate, signAlgorithm, Constants.C14N_WC); + } + + /** + * Signs the Document using the specified signature algorithm with the private key and the public certificate. + * + * @param document + * The document to be signed + * @param key + * The private key + * @param certificate + * The public certificate + * @param signAlgorithm + * Signature Algorithm + * @param c14nMethod + * Canonicalization method + * + * @return the signed document in string format + * + * @throws XMLSecurityException + * @throws XPathExpressionException + */ + public static String addSign(Document document, PrivateKey key, X509Certificate certificate, String signAlgorithm, String c14nMethod) throws XMLSecurityException, XPathExpressionException { org.apache.xml.security.Init.init(); // Check arguments. @@ -1095,7 +1118,7 @@ public static String addSign(Document document, PrivateKey key, X509Certificate if (key == null) { throw new IllegalArgumentException("Provided key was null"); } - + if (certificate == null) { throw new IllegalArgumentException("Provided certificate was null"); } @@ -1104,17 +1127,13 @@ public static String addSign(Document document, PrivateKey key, X509Certificate signAlgorithm = Constants.RSA_SHA1; } - // document.normalizeDocument(); - - String c14nMethod = Constants.C14N_WC; - // Signature object XMLSignature sig = new XMLSignature(document, null, signAlgorithm, c14nMethod); // Including the signature into the document before sign, because // this is an envelop signature Element root = document.getDocumentElement(); - document.setXmlStandalone(false); + document.setXmlStandalone(false); // If Issuer, locate Signature after Issuer, Otherwise as first child. NodeList issuerNodes = Util.query(document, "//saml:Issuer", null); @@ -1141,7 +1160,7 @@ public static String addSign(Document document, PrivateKey key, X509Certificate sig.addDocument(reference, transforms, Constants.SHA1); // Add the certification info - sig.addKeyInfo(certificate); + sig.addKeyInfo(certificate); // Sign the document sig.sign(key); @@ -1553,5 +1572,5 @@ private static byte[] toBytesUtf8(String str) { } } - + } diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index d89613d4..69481b69 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -11,11 +11,16 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Scanner; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPathExpressionException; import org.apache.commons.lang3.StringUtils; +import org.apache.xml.security.exceptions.XMLSecurityException; import org.joda.time.DateTime; import org.joda.time.Instant; import org.slf4j.Logger; @@ -34,6 +39,7 @@ import com.onelogin.saml2.settings.SettingsBuilder; import com.onelogin.saml2.util.Constants; import com.onelogin.saml2.util.Util; +import org.xml.sax.SAXException; /** * Main class of OneLogin's Java Toolkit. @@ -139,6 +145,8 @@ public class Auth { */ private String lastResponse; + private String requestPostBindingHTML; + /** * Initializes the SP SAML instance. * @@ -224,6 +232,12 @@ public Auth(Saml2Settings settings, HttpServletRequest request, HttpServletRespo throw new SettingsException(errorMsg, SettingsException.SETTINGS_INVALID); } LOGGER.debug("Settings validated"); + + + if (Objects.equals(Constants.BINDING_HTTP_POST, settings.getIdpSingleSignOnServiceBinding())) { + Scanner s = new Scanner(getClass().getClassLoader().getResourceAsStream("request-post-binding.html")).useDelimiter("\\A"); + this.requestPostBindingHTML = s.hasNext() ? s.next() : ""; + } } /** @@ -262,10 +276,6 @@ public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Bool AuthnRequest authnRequest = new AuthnRequest(settings, forceAuthn, isPassive, setNameIdPolicy); - String samlRequest = authnRequest.getEncodedAuthnRequest(); - - parameters.put("SAMLRequest", samlRequest); - String relayState; if (returnTo == null) { relayState = ServletUtils.getSelfRoutedURLNoQuery(request); @@ -273,26 +283,70 @@ public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Bool relayState = returnTo; } - if (!relayState.isEmpty()) { - parameters.put("RelayState", relayState); - } + if (Objects.equals(Constants.BINDING_HTTP_REDIRECT, settings.getIdpSingleSignOnServiceBinding())) { + String samlRequest = authnRequest.getEncodedAuthnRequest(); - if (settings.getAuthnRequestsSigned()) { - String sigAlg = settings.getSignatureAlgorithm(); - String signature = this.buildRequestSignature(samlRequest, relayState, sigAlg); + parameters.put("SAMLRequest", samlRequest); - parameters.put("SigAlg", sigAlg); - parameters.put("Signature", signature); - } + if (!relayState.isEmpty()) { + parameters.put("RelayState", relayState); + } - String ssoUrl = getSSOurl(); - lastRequestId = authnRequest.getId(); - lastRequest = authnRequest.getAuthnRequestXml(); + if (settings.getAuthnRequestsSigned()) { + String sigAlg = settings.getSignatureAlgorithm(); + String signature = this.buildRequestSignature(samlRequest, relayState, sigAlg); - if (!stay) { - LOGGER.debug("AuthNRequest sent to " + ssoUrl + " --> " + samlRequest); + parameters.put("SigAlg", sigAlg); + parameters.put("Signature", signature); + } + + String ssoUrl = getSSOurl(); + lastRequestId = authnRequest.getId(); + lastRequest = authnRequest.getAuthnRequestXml(); + + if (!stay) { + LOGGER.debug("AuthNRequest sent to " + ssoUrl + " --> " + samlRequest); + } + return ServletUtils.sendRedirect(response, ssoUrl, parameters, stay); + } else if (Objects.equals(Constants.BINDING_HTTP_POST, settings.getIdpSingleSignOnServiceBinding())) { + String requestPostBinding = null; + + try { + String authnRequestXML; + + if (settings.getAuthnRequestsSigned()) { + authnRequestXML = Util.addSign( + Util.convertStringToDocument(authnRequest.getAuthnRequestXml()), + settings.getSPkey(), + settings.getSPcert(), + settings.getSignatureAlgorithm(), + Constants.C14NEXC); + + LOGGER.debug("Signed XML: {}", authnRequestXML); + } else { + authnRequestXML = authnRequest.getEncodedAuthnRequest(false); + } + + requestPostBinding = String.format( + this.requestPostBindingHTML, + settings.getOrganization().getOrgDisplayName(), + settings.getIdpSingleSignOnServiceUrl(), + Util.base64encoder(authnRequestXML), + relayState + ); + + if (!stay) { + ServletUtils.respondWithContentString(response, requestPostBinding, "text/html; charset=utf-8"); + } + + } catch (Exception e) { + LOGGER.error("Error {}", e.getMessage(), e); + } + + return requestPostBinding; + } else { + throw new SettingsException("Unsupported SSO binding: " + settings.getIdpSingleSignOnServiceBinding(), SettingsException.UNSUPPORTED_BINDING); } - return ServletUtils.sendRedirect(response, ssoUrl, parameters, stay); } /** diff --git a/toolkit/src/main/java/com/onelogin/saml2/servlet/ServletUtils.java b/toolkit/src/main/java/com/onelogin/saml2/servlet/ServletUtils.java index c62bdd71..48f3b09d 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/servlet/ServletUtils.java +++ b/toolkit/src/main/java/com/onelogin/saml2/servlet/ServletUtils.java @@ -214,4 +214,20 @@ public static void sendRedirect(HttpServletResponse response, String location) t Map parameters =new HashMap(); sendRedirect(response, location, parameters); } + + /** + * Respond to a request with a given {@link String}. + * + * @param response The response. + * @param contentToWrite The content to write in the {@code response}. + * @param contentType The content type of the content to be written. + * @throws IOException If it fails to write in the {@code response}. + */ + public static void respondWithContentString(HttpServletResponse response, String contentToWrite, String contentType) throws IOException { + response.setContentType(contentType); + response.setContentLength(contentToWrite.getBytes("UTF-8").length); + response.getWriter().write(contentToWrite); + response.getWriter().flush(); + response.getWriter().close(); + } } diff --git a/toolkit/src/main/resources/request-post-binding.html b/toolkit/src/main/resources/request-post-binding.html new file mode 100644 index 00000000..eea412dd --- /dev/null +++ b/toolkit/src/main/resources/request-post-binding.html @@ -0,0 +1,17 @@ + + + + %s + + +
+ + + +
+ + + \ No newline at end of file