protected LogoutRequest buildLogoutRequest(String user, String sessionIdx) throws SSOAgentException { LogoutRequest logoutReq = new LogoutRequestBuilder().buildObject(); logoutReq.setID(SSOAgentUtils.createID()); logoutReq.setDestination(ssoAgentConfig.getSAML2().getIdPURL()); DateTime issueInstant = new DateTime(); logoutReq.setIssueInstant(issueInstant); logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + 5 * 60 * 1000)); IssuerBuilder issuerBuilder = new IssuerBuilder(); Issuer issuer = issuerBuilder.buildObject(); issuer.setValue(ssoAgentConfig.getSAML2().getSPEntityId()); logoutReq.setIssuer(issuer); NameID nameId = new NameIDBuilder().buildObject(); nameId.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:entity"); nameId.setValue(user); logoutReq.setNameID(nameId); SessionIndex sessionIndex = new SessionIndexBuilder().buildObject(); sessionIndex.setSessionIndex(sessionIdx); logoutReq.getSessionIndexes().add(sessionIndex); logoutReq.setReason("Single Logout"); return logoutReq; }
protected AuthnRequest buildAuthnRequest(HttpServletRequest request) throws SSOAgentException { IssuerBuilder issuerBuilder = new IssuerBuilder(); Issuer issuer = issuerBuilder.buildObject("urn:oasis:names:tc:SAML:2.0:assertion", "Issuer", "samlp"); issuer.setValue(ssoAgentConfig.getSAML2().getSPEntityId()); /* NameIDPolicy */ NameIDPolicyBuilder nameIdPolicyBuilder = new NameIDPolicyBuilder(); NameIDPolicy nameIdPolicy = nameIdPolicyBuilder.buildObject(); nameIdPolicy.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"); nameIdPolicy.setSPNameQualifier("Issuer"); nameIdPolicy.setAllowCreate(true); /* AuthnContextClass */ AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder(); AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject( "urn:oasis:names:tc:SAML:2.0:assertion", "AuthnContextClassRef", "saml"); authnContextClassRef.setAuthnContextClassRef( "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"); /* AuthnContex */ RequestedAuthnContextBuilder requestedAuthnContextBuilder = new RequestedAuthnContextBuilder(); RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject(); requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT); requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef); DateTime issueInstant = new DateTime(); /* Creation of AuthRequestObject */ AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder(); AuthnRequest authRequest = authRequestBuilder.buildObject( "urn:oasis:names:tc:SAML:2.0:protocol", "AuthnRequest", "samlp"); authRequest.setForceAuthn(ssoAgentConfig.getSAML2().isForceAuthn()); authRequest.setIsPassive(ssoAgentConfig.getSAML2().isPassiveAuthn()); authRequest.setIssueInstant(issueInstant); authRequest.setProtocolBinding(ssoAgentConfig.getSAML2().getHttpBinding()); authRequest.setAssertionConsumerServiceURL(ssoAgentConfig.getSAML2().getACSURL()); authRequest.setIssuer(issuer); authRequest.setNameIDPolicy(nameIdPolicy); authRequest.setRequestedAuthnContext(requestedAuthnContext); authRequest.setID(SSOAgentUtils.createID()); authRequest.setVersion(SAMLVersion.VERSION_20); authRequest.setDestination(ssoAgentConfig.getSAML2().getIdPURL()); if (request.getAttribute(Extensions.LOCAL_NAME) != null) { authRequest.setExtensions((Extensions) request.getAttribute(Extensions.LOCAL_NAME)); } /* Requesting Attributes. This Index value is registered in the IDP */ if (ssoAgentConfig.getSAML2().getAttributeConsumingServiceIndex() != null && ssoAgentConfig.getSAML2().getAttributeConsumingServiceIndex().trim().length() > 0) { authRequest.setAttributeConsumingServiceIndex( Integer.parseInt(ssoAgentConfig.getSAML2().getAttributeConsumingServiceIndex())); } return authRequest; }
/** * Validate the signature of a SAML2 Response and Assertion * * @param response SAML2 Response * @return true, if signature is valid. */ protected void validateSignature(Response response, Assertion assertion) throws SSOAgentException { if (SSOAgentDataHolder.getInstance().getSignatureValidator() != null) { // Custom implemetation of signature validation SAMLSignatureValidator signatureValidatorUtility = (SAMLSignatureValidator) SSOAgentDataHolder.getInstance().getSignatureValidator(); signatureValidatorUtility.validateSignature(response, assertion, ssoAgentConfig); } else { // If custom implementation not found, Execute the default implementation if (ssoAgentConfig.getSAML2().isResponseSigned()) { if (response.getSignature() == null) { throw new SSOAgentException( "SAML2 Response signing is enabled, but signature element not found in SAML2 Response element"); } else { try { SignatureValidator validator = new SignatureValidator( new X509CredentialImpl(ssoAgentConfig.getSAML2().getSSOAgentX509Credential())); validator.validate(response.getSignature()); } catch (ValidationException e) { if (log.isDebugEnabled()) { log.debug("Validation exception : ", e); } throw new SSOAgentException("Signature validation failed for SAML2 Response"); } } } if (ssoAgentConfig.getSAML2().isAssertionSigned()) { if (assertion.getSignature() == null) { throw new SSOAgentException( "SAML2 Assertion signing is enabled, but signature element not found in SAML2 Assertion element"); } else { try { SignatureValidator validator = new SignatureValidator( new X509CredentialImpl(ssoAgentConfig.getSAML2().getSSOAgentX509Credential())); validator.validate(assertion.getSignature()); } catch (ValidationException e) { if (log.isDebugEnabled()) { log.debug("Validation exception : ", e); } throw new SSOAgentException("Signature validation failed for SAML2 Assertion"); } } } } }
/** * Validate the AudienceRestriction of SAML2 Response * * @param assertion SAML2 Assertion * @return validity */ protected void validateAudienceRestriction(Assertion assertion) throws SSOAgentException { if (assertion != null) { Conditions conditions = assertion.getConditions(); if (conditions != null) { List<AudienceRestriction> audienceRestrictions = conditions.getAudienceRestrictions(); if (audienceRestrictions != null && !audienceRestrictions.isEmpty()) { boolean audienceFound = false; for (AudienceRestriction audienceRestriction : audienceRestrictions) { if (audienceRestriction.getAudiences() != null && !audienceRestriction.getAudiences().isEmpty()) { for (Audience audience : audienceRestriction.getAudiences()) { if (ssoAgentConfig.getSAML2().getSPEntityId().equals(audience.getAudienceURI())) { audienceFound = true; break; } } } if (audienceFound) { break; } } if (!audienceFound) { throw new SSOAgentException("SAML2 Assertion Audience Restriction validation failed"); } } else { throw new SSOAgentException("SAML2 Response doesn't contain AudienceRestrictions"); } } else { throw new SSOAgentException("SAML2 Response doesn't contain Conditions"); } } }
public void processResponse(HttpServletRequest request, HttpServletResponse response) throws SSOAgentException { String saml2SSOResponse = request.getParameter(SSOAgentConstants.SAML2SSO.HTTP_POST_PARAM_SAML2_RESP); if (saml2SSOResponse != null) { String decodedResponse = new String(Base64.decode(saml2SSOResponse), Charset.forName("UTF-8")); XMLObject samlObject = SSOAgentUtils.unmarshall(decodedResponse); if (samlObject instanceof LogoutResponse) { // This is a SAML response for a single logout request from the SP doSLO(request); } else { processSSOResponse(request); } String relayState = request.getParameter(RelayState.DEFAULT_ELEMENT_LOCAL_NAME); if (relayState != null && !relayState.isEmpty() && !"null".equalsIgnoreCase(relayState)) { // additional // checks for incompetent IdPs ssoAgentConfig.getSAML2().setRelayState(relayState); } } else { throw new SSOAgentException("Invalid SAML2 Response. SAML2 Response can not be null."); } }
public SAML2SSOManager(SSOAgentConfig ssoAgentConfig) throws SSOAgentException { /* Initializing the OpenSAML library, loading default configurations */ this.ssoAgentConfig = ssoAgentConfig; // load custom Signature Validator Class String signerClassName = ssoAgentConfig.getSAML2().getSignatureValidatorImplClass(); try { if (signerClassName != null) { SSOAgentDataHolder.getInstance() .setSignatureValidator(Class.forName(signerClassName).newInstance()); } } catch (ClassNotFoundException e) { throw new SSOAgentException("Error loading custom signature validator class", e); } catch (IllegalAccessException e) { throw new SSOAgentException("Error loading custom signature validator class", e); } catch (InstantiationException e) { throw new SSOAgentException("Error loading custom signature validator class", e); } SSOAgentUtils.doBootstrap(); }
/** * Get Decrypted Assertion * * @param encryptedAssertion * @return * @throws Exception */ protected Assertion getDecryptedAssertion(EncryptedAssertion encryptedAssertion) throws SSOAgentException { try { KeyInfoCredentialResolver keyResolver = new StaticKeyInfoCredentialResolver( new X509CredentialImpl(ssoAgentConfig.getSAML2().getSSOAgentX509Credential())); EncryptedKey key = encryptedAssertion.getEncryptedData().getKeyInfo().getEncryptedKeys().get(0); Decrypter decrypter = new Decrypter(null, keyResolver, null); SecretKey dkey = (SecretKey) decrypter.decryptKey( key, encryptedAssertion.getEncryptedData().getEncryptionMethod().getAlgorithm()); Credential shared = SecurityHelper.getSimpleCredential(dkey); decrypter = new Decrypter(new StaticKeyInfoCredentialResolver(shared), null, null); decrypter.setRootInNewDocument(true); return decrypter.decrypt(encryptedAssertion); } catch (Exception e) { throw new SSOAgentException("Decrypted assertion error", e); } }
protected void processSSOResponse(HttpServletRequest request) throws SSOAgentException { LoggedInSessionBean sessionBean = new LoggedInSessionBean(); sessionBean.setSAML2SSO(sessionBean.new SAML2SSO()); String saml2ResponseString = new String( Base64.decode( request.getParameter(SSOAgentConstants.SAML2SSO.HTTP_POST_PARAM_SAML2_RESP)), Charset.forName("UTF-8")); Response saml2Response = (Response) SSOAgentUtils.unmarshall(saml2ResponseString); sessionBean.getSAML2SSO().setResponseString(saml2ResponseString); sessionBean.getSAML2SSO().setSAMLResponse(saml2Response); Assertion assertion = null; if (ssoAgentConfig.getSAML2().isAssertionEncrypted()) { List<EncryptedAssertion> encryptedAssertions = saml2Response.getEncryptedAssertions(); EncryptedAssertion encryptedAssertion = null; if (!org.apache.commons.collections.CollectionUtils.isEmpty(encryptedAssertions)) { encryptedAssertion = encryptedAssertions.get(0); try { assertion = getDecryptedAssertion(encryptedAssertion); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug("Assertion decryption failure : ", e); } throw new SSOAgentException("Unable to decrypt the SAML2 Assertion"); } } } else { List<Assertion> assertions = saml2Response.getAssertions(); if (assertions != null && !assertions.isEmpty()) { assertion = assertions.get(0); } } if (assertion == null) { if (isNoPassive(saml2Response)) { LOGGER.log(Level.FINE, "Cannot authenticate in passive mode"); return; } throw new SSOAgentException("SAML2 Assertion not found in the Response"); } String idPEntityIdValue = assertion.getIssuer().getValue(); if (idPEntityIdValue == null || idPEntityIdValue.isEmpty()) { throw new SSOAgentException("SAML2 Response does not contain an Issuer value"); } else if (!idPEntityIdValue.equals(ssoAgentConfig.getSAML2().getIdPEntityId())) { throw new SSOAgentException("SAML2 Response Issuer verification failed"); } sessionBean.getSAML2SSO().setAssertion(assertion); // Cannot marshall SAML assertion here, before signature validation due to a weird issue in // OpenSAML // Get the subject name from the Response Object and forward it to login_action.jsp String subject = null; if (assertion.getSubject() != null && assertion.getSubject().getNameID() != null) { subject = assertion.getSubject().getNameID().getValue(); } if (subject == null) { throw new SSOAgentException("SAML2 Response does not contain the name of the subject"); } sessionBean.getSAML2SSO().setSubjectId(subject); // set the subject request.getSession().setAttribute(SSOAgentConstants.SESSION_BEAN_NAME, sessionBean); // validate audience restriction validateAudienceRestriction(assertion); // validate signature validateSignature(saml2Response, assertion); // Marshalling SAML2 assertion after signature validation due to a weird issue in OpenSAML sessionBean.getSAML2SSO().setAssertionString(marshall(assertion)); ((LoggedInSessionBean) request.getSession().getAttribute(SSOAgentConstants.SESSION_BEAN_NAME)) .getSAML2SSO() .setSubjectAttributes(getAssertionStatements(assertion)); // For removing the session when the single sign out request made by the SP itself if (ssoAgentConfig.getSAML2().isSLOEnabled()) { String sessionId = assertion.getAuthnStatements().get(0).getSessionIndex(); if (sessionId == null) { throw new SSOAgentException( "Single Logout is enabled but IdP Session ID not found in SAML2 Assertion"); } ((LoggedInSessionBean) request.getSession().getAttribute(SSOAgentConstants.SESSION_BEAN_NAME)) .getSAML2SSO() .setSessionIndex(sessionId); SSOAgentSessionManager.addAuthenticatedSession(request.getSession(false)); } request.getSession().setAttribute(SSOAgentConstants.SESSION_BEAN_NAME, sessionBean); }
/** * Handles the request for http post binding * * @param request The HTTP request with SAML2 message * @param response The HTTP response * @param isLogout Whether the request is a logout request * @throws SSOAgentException */ public String buildPostRequest( HttpServletRequest request, HttpServletResponse response, boolean isLogout) throws SSOAgentException { RequestAbstractType requestMessage = null; if (!isLogout) { requestMessage = buildAuthnRequest(request); if (ssoAgentConfig.getSAML2().isRequestSigned()) { requestMessage = SSOAgentUtils.setSignature( (AuthnRequest) requestMessage, XMLSignature.ALGO_ID_SIGNATURE_RSA, new X509CredentialImpl(ssoAgentConfig.getSAML2().getSSOAgentX509Credential())); } } else { LoggedInSessionBean sessionBean = (LoggedInSessionBean) request.getSession(false).getAttribute(SSOAgentConstants.SESSION_BEAN_NAME); if (sessionBean != null) { requestMessage = buildLogoutRequest( sessionBean.getSAML2SSO().getSubjectId(), sessionBean.getSAML2SSO().getSessionIndex()); if (ssoAgentConfig.getSAML2().isRequestSigned()) { requestMessage = SSOAgentUtils.setSignature( (LogoutRequest) requestMessage, XMLSignature.ALGO_ID_SIGNATURE_RSA, new X509CredentialImpl(ssoAgentConfig.getSAML2().getSSOAgentX509Credential())); } } else { throw new SSOAgentException("SLO Request can not be built. SSO Session is null"); } } String encodedRequestMessage = encodeRequestMessage(requestMessage, SAMLConstants.SAML2_POST_BINDING_URI); Map<String, String[]> paramsMap = new HashMap<String, String[]>(); paramsMap.put( SSOAgentConstants.SAML2SSO.HTTP_POST_PARAM_SAML2_AUTH_REQ, new String[] {encodedRequestMessage}); if (ssoAgentConfig.getSAML2().getRelayState() != null) { paramsMap.put( RelayState.DEFAULT_ELEMENT_LOCAL_NAME, new String[] {ssoAgentConfig.getSAML2().getRelayState()}); } // Add any additional parameters defined if (ssoAgentConfig.getQueryParams() != null && !ssoAgentConfig.getQueryParams().isEmpty()) { paramsMap.putAll(ssoAgentConfig.getQueryParams()); } StringBuilder htmlParams = new StringBuilder(); for (Map.Entry<String, String[]> entry : paramsMap.entrySet()) { if (entry.getKey() != null && entry.getValue() != null && entry.getValue().length > 0) { for (String param : entry.getValue()) { htmlParams .append("<input type='hidden' name='") .append(entry.getKey()) .append("' value='") .append(param) .append("'>\n"); } } } String htmlPayload = ssoAgentConfig.getSAML2().getPostBindingRequestHTMLPayload(); if (htmlPayload == null || !htmlPayload.contains("<!--$saml_params-->")) { htmlPayload = "<html>\n" + "<body>\n" + "<p>You are now redirected back to " + ssoAgentConfig.getSAML2().getIdPURL() + " \n" + "If the redirection fails, please click the post button.</p>\n" + "<form method='post' action='" + ssoAgentConfig.getSAML2().getIdPURL() + "'>\n" + "<p>\n" + htmlParams.toString() + "<button type='submit'>POST</button>\n" + "</p>\n" + "</form>\n" + "<script type='text/javascript'>\n" + "document.forms[0].submit();\n" + "</script>\n" + "</body>\n" + "</html>"; } else { htmlPayload = htmlPayload.replace("<!--$saml_params-->", htmlParams.toString()); } return htmlPayload; }
/** * Returns the redirection URL with the appended SAML2 Request message * * @param request SAML 2 request * @return redirectionUrl */ public String buildRedirectRequest(HttpServletRequest request, boolean isLogout) throws SSOAgentException { RequestAbstractType requestMessage = null; if (!isLogout) { requestMessage = buildAuthnRequest(request); } else { LoggedInSessionBean sessionBean = (LoggedInSessionBean) request.getSession(false).getAttribute(SSOAgentConstants.SESSION_BEAN_NAME); if (sessionBean != null) { requestMessage = buildLogoutRequest( sessionBean.getSAML2SSO().getSubjectId(), sessionBean.getSAML2SSO().getSessionIndex()); } else { throw new SSOAgentException("SLO Request can not be built. SSO Session is NULL"); } } String idpUrl = null; String encodedRequestMessage = encodeRequestMessage(requestMessage, SAMLConstants.SAML2_REDIRECT_BINDING_URI); StringBuilder httpQueryString = new StringBuilder( SSOAgentConstants.SAML2SSO.HTTP_POST_PARAM_SAML2_AUTH_REQ + "=" + encodedRequestMessage); String relayState = ssoAgentConfig.getSAML2().getRelayState(); if (relayState != null) { try { httpQueryString.append( "&" + RelayState.DEFAULT_ELEMENT_LOCAL_NAME + "=" + URLEncoder.encode(relayState, "UTF-8").trim()); } catch (UnsupportedEncodingException e) { throw new SSOAgentException( "Error occurred while URLEncoding " + RelayState.DEFAULT_ELEMENT_LOCAL_NAME, e); } } if (ssoAgentConfig.getQueryParams() != null && !ssoAgentConfig.getQueryParams().isEmpty()) { StringBuilder builder = new StringBuilder(); for (Map.Entry<String, String[]> entry : ssoAgentConfig.getQueryParams().entrySet()) { if (entry.getKey() != null && entry.getValue() != null && entry.getValue().length > 0) { for (String param : entry.getValue()) { builder.append("&").append(entry.getKey()).append("=").append(param); } } } httpQueryString.append(builder); } if (ssoAgentConfig.getSAML2().isRequestSigned()) { SSOAgentUtils.addDeflateSignatureToHTTPQueryString( httpQueryString, new X509CredentialImpl(ssoAgentConfig.getSAML2().getSSOAgentX509Credential())); } if (ssoAgentConfig.getSAML2().getIdPURL().indexOf("?") > -1) { idpUrl = ssoAgentConfig.getSAML2().getIdPURL().concat("&").concat(httpQueryString.toString()); } else { idpUrl = ssoAgentConfig.getSAML2().getIdPURL().concat("?").concat(httpQueryString.toString()); } return idpUrl; }