public void processLogoutResponse(SAMLMessageContext context) throws SAMLException, org.opensaml.xml.security.SecurityException, ValidationException { SAMLObject message = context.getInboundSAMLMessage(); // Verify type if (!(message instanceof LogoutResponse)) { log.debug("Received response is not of a Response object type"); throw new SAMLException("Error validating SAML response"); } LogoutResponse response = (LogoutResponse) message; // Make sure request was authenticated if required, authentication is done as part of the // binding processing if (!context.isInboundSAMLMessageAuthenticated() && context.getLocalExtendedMetadata().isRequireLogoutResponseSigned()) { log.debug( "Logout Response object is required to be signed by the entity policy: " + context.getInboundSAMLMessageId()); throw new SAMLException("Logout Response object is required to be signed"); } // Verify issue time DateTime time = response.getIssueInstant(); if (!isDateTimeSkewValid(getResponseSkew(), time)) { log.debug("Response issue time is either too old or with date in the future"); throw new SAMLException("Error validating SAML response"); } // Verify response to field if present, set request if correct // The inResponseTo field is optional, SAML 2.0 Core, 1542 SAMLMessageStorage messageStorage = context.getMessageStorage(); if (messageStorage != null && response.getInResponseTo() != null) { XMLObject xmlObject = messageStorage.retrieveMessage(response.getInResponseTo()); if (xmlObject == null) { log.debug( "InResponseToField doesn't correspond to sent message", response.getInResponseTo()); throw new SAMLException("Error validating SAML response"); } else if (xmlObject instanceof LogoutRequest) { // Expected } else { log.debug( "Sent request was of different type then received response", response.getInResponseTo()); throw new SAMLException("Error validating SAML response"); } } // Verify destination if (response.getDestination() != null) { SPSSODescriptor localDescriptor = (SPSSODescriptor) context.getLocalEntityRoleMetadata(); // Check if destination is correct on this SP List<SingleLogoutService> services = localDescriptor.getSingleLogoutServices(); boolean found = false; for (SingleLogoutService service : services) { if (response.getDestination().equals(service.getLocation()) && context.getInboundSAMLBinding().equals(service.getBinding())) { found = true; break; } } if (!found) { log.debug( "Destination of the response was not the expected value", response.getDestination()); throw new SAMLException("Error validating SAML response"); } } // Verify issuer if (response.getIssuer() != null) { Issuer issuer = response.getIssuer(); verifyIssuer(issuer, context); } // Verify status String statusCode = response.getStatus().getStatusCode().getValue(); if (StatusCode.SUCCESS_URI.equals(statusCode)) { log.trace("Single Logout was successful"); } else if (StatusCode.PARTIAL_LOGOUT_URI.equals(statusCode)) { log.trace("Single Logout was partially successful"); } else { String[] logMessage = new String[2]; logMessage[0] = response.getStatus().getStatusCode().getValue(); StatusMessage message1 = response.getStatus().getStatusMessage(); if (message1 != null) { logMessage[1] = message1.getMessage(); } log.warn("Received LogoutResponse has invalid status code", logMessage); } }
/** * @param SAMLToken - SAML Assertion XML * @param contextAttributeName - SAML Attribute name containing Context data * @param verfiySignature * @return a base64 encoded string containing the context * @throws SAMLValidationException - if validation fails */ public static List<AttributeStatement> validate(byte[] SAMLToken, SAMLConfiguration samlConfig) throws SAMLValidationException { if (logger.isDebugEnabled()) { logger.debug("validate(byte[], SAMLConfiguration) - start"); // $NON-NLS-1$ } Decrypter samlDecrypter = samlConfig.getSamlDecrypter(); SignatureTrustEngine sigTrustEngine = samlConfig.getTrustEngine(); String myURI = samlConfig.getMyURI(); MessageReplayRule replayRule = samlConfig.getReplayRule(); BasicParserPool ppMgr = new BasicParserPool(); ppMgr.setNamespaceAware(true); VerifySignatureType verfiySignature = samlConfig.getVerifySignature(); List<AttributeStatement> attrStatements = null; if (SAMLToken != null) { try { InputStream in = new ByteArrayInputStream(SAMLToken); Document SAMLDoc; try { SAMLDoc = ppMgr.parse(in); } catch (XMLParserException e) { logger.error( "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)", e); //$NON-NLS-1$ throw createSAMLValidationException("Error parsing SAML XML", true, e); } Element responseElement = SAMLDoc.getDocumentElement(); SAMLType samlType = SAMLType.Response; if (responseElement == null) { throw createSAMLValidationException( "Missing SAML Encrypted Assertion or Assertion or Assertion Response", true); } if (!"Response".equals(responseElement.getLocalName()) || !SAMLConstants.SAML20P_NS.equals(responseElement.getNamespaceURI())) { if (!"Assertion".equals(responseElement.getLocalName()) || !SAMLConstants.SAML20_NS.equals(responseElement.getNamespaceURI())) { if (!"EncryptedAssertion".equals(responseElement.getLocalName()) || !SAMLConstants.SAML20_NS.equals(responseElement.getNamespaceURI())) { throw createSAMLValidationException( "Missing or invalid SAML Encrypted Assertion or Assertion or Assertion Response", true); } else { samlType = SAMLType.EncryptedAssertion; } } else { samlType = SAMLType.Assertion; } } else { samlType = SAMLType.Response; } if (samlType == SAMLType.Response) { // Unmarshall SAML Assertion Response into an OpenSAML Java object. UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory(); Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(responseElement); Response samlResponse; try { samlResponse = (Response) unmarshaller.unmarshall(responseElement); } catch (UnmarshallingException e) { logger.error( "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)", e); //$NON-NLS-1$ throw createSAMLValidationException( "Error in unmarshalling SAML XML Document", true, e); } // Check the replay attack for Response if (replayRule != null) { BasicSAMLMessageContext messageContext = new BasicSAMLMessageContext(); // messageContext.setInboundMessage(samlResponse); if (samlResponse.getIssuer() != null) messageContext.setInboundMessageIssuer(samlResponse.getIssuer().getValue()); messageContext.setInboundSAMLMessageId(samlResponse.getID()); try { replayRule.evaluate(messageContext); } catch (SecurityPolicyException e) { logger.error( "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)", e); //$NON-NLS-1$ throw createSAMLValidationException("Possible Replay Attack for Response", false, e); } } // Validate the status code if (!StatusCode.SUCCESS_URI.equals(samlResponse.getStatus().getStatusCode().getValue())) { throw createSAMLValidationException("Invalid Status Code.", true); } boolean responseSignatureVerified = validateResponseSignature(samlResponse, sigTrustEngine, verfiySignature); // Get first encrypted Assertion, if not present get first unencrypted assertion int assertCount = samlResponse.getEncryptedAssertions().size(); if (assertCount == 0) { assertCount = samlResponse.getAssertions().size(); if (assertCount == 0) { throw createSAMLValidationException( "No Assertion or EncryptedAssertion found in received response", true); } else { for (Assertion samlAssertion : samlResponse.getAssertions()) { attrStatements = validateAssertion( samlAssertion, sigTrustEngine, myURI, replayRule, verfiySignature, responseSignatureVerified); break; // Use the first only } } } else { for (EncryptedAssertion samlEncryptedAssertion : samlResponse.getEncryptedAssertions()) { // Decryption Assertion samlAssertion = null; try { samlAssertion = samlDecrypter.decrypt(samlEncryptedAssertion); } catch (DecryptionException e) { logger.error( "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)", e); //$NON-NLS-1$ throw createSAMLValidationException( "Error Decrypting Received Encrypted Assertion", true, e); } attrStatements = validateAssertion( samlAssertion, sigTrustEngine, myURI, replayRule, verfiySignature, responseSignatureVerified); break; // Use the first only } } } else if (samlType == SAMLType.EncryptedAssertion) { // Unmarshall SAML Encrypted Assertion into an OpenSAML Java object. UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory(); Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(responseElement); EncryptedAssertion encryptedAssertion = null; try { encryptedAssertion = (EncryptedAssertion) unmarshaller.unmarshall(responseElement); } catch (UnmarshallingException e) { logger.error( "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)", e); //$NON-NLS-1$ throw createSAMLValidationException( "Error in unmarshalling SAML XML Document", true, e); } boolean responseSignatureVerified = false; // Decryption Assertion samlAssertion = null; try { samlAssertion = samlDecrypter.decrypt(encryptedAssertion); } catch (DecryptionException e) { logger.error( "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)", e); //$NON-NLS-1$ throw createSAMLValidationException( "Error Decrypting Received Encrypted Assertion", true, e); } attrStatements = validateAssertion( samlAssertion, sigTrustEngine, myURI, replayRule, verfiySignature, responseSignatureVerified); } else if (samlType == SAMLType.Assertion) { // Unmarshall SAML Assertion into an OpenSAML Java object. UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory(); Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(responseElement); Assertion samlAssertion = null; try { samlAssertion = (Assertion) unmarshaller.unmarshall(responseElement); } catch (UnmarshallingException e) { logger.error( "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)", e); //$NON-NLS-1$ throw createSAMLValidationException( "Error in unmarshalling SAML XML Document", true, e); } boolean responseSignatureVerified = false; attrStatements = validateAssertion( samlAssertion, sigTrustEngine, myURI, replayRule, verfiySignature, responseSignatureVerified); } } catch (SAMLValidationException e) { throw e; } } if (logger.isDebugEnabled()) { logger.debug( "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType) - end"); //$NON-NLS-1$ } return attrStatements; }