You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2017/06/16 09:22:21 UTC

[2/2] cxf git commit: Fixing merge

Fixing merge


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/72c4194a
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/72c4194a
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/72c4194a

Branch: refs/heads/2.6.x-fixes
Commit: 72c4194a634a40265171b4288927f35dc329c552
Parents: f318ee6
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Fri Jun 16 10:21:38 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Fri Jun 16 10:21:38 2017 +0100

----------------------------------------------------------------------
 .../saml/sso/SAMLSSOResponseValidator.java      |  95 +++---
 .../saml/sso/CombinedValidatorTest.java         | 329 ++++++++++++++-----
 2 files changed, 305 insertions(+), 119 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/72c4194a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
index 096468c..0bb1c79 100644
--- a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
+++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
@@ -36,32 +36,33 @@ import org.opensaml.saml2.core.AuthnStatement;
  * should be validated by the SAMLProtocolResponseValidator first.
  */
 public class SAMLSSOResponseValidator {
-    
+
     private static final Logger LOG = LogUtils.getL7dLogger(SAMLSSOResponseValidator.class);
-    
+
     private String issuerIDP;
     private String assertionConsumerURL;
     private String clientAddress;
     private String requestId;
     private String spIdentifier;
+    private boolean enforceResponseSigned;
     private boolean enforceAssertionsSigned = true;
     private boolean enforceKnownIssuer = true;
     private TokenReplayCache<String> replayCache;
-    
+
     /**
      * Enforce that Assertions must be signed if the POST binding was used. The default is true.
      */
     public void setEnforceAssertionsSigned(boolean enforceAssertionsSigned) {
         this.enforceAssertionsSigned = enforceAssertionsSigned;
     }
-    
+
     /**
      * Enforce that the Issuer of the received Response/Assertion is known. The default is true.
      */
     public void setEnforceKnownIssuer(boolean enforceKnownIssuer) {
         this.enforceKnownIssuer = enforceKnownIssuer;
     }
-    
+
     /**
      * Validate a SAML 2 Protocol Response
      * @param samlResponse
@@ -81,7 +82,7 @@ public class SAMLSSOResponseValidator {
             LOG.fine("The Response must contain at least one Assertion");
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
-        
+
         // The Response must contain a Destination that matches the assertionConsumerURL if it is
         // signed
         String destination = samlResponse.getDestination();
@@ -90,9 +91,14 @@ public class SAMLSSOResponseValidator {
             LOG.fine("The Response must contain a destination that matches the assertion consumer URL");
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
-        
+
+        if (enforceResponseSigned && !samlResponse.isSigned()) {
+            LOG.fine("The Response must be signed!");
+            throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+        }
+
         // Validate Assertions
-        org.opensaml.saml.saml2.core.Assertion validAssertion = null;
+        org.opensaml.saml2.core.Assertion validAssertion = null;
         Date sessionNotOnOrAfter = null;
         for (org.opensaml.saml2.core.Assertion assertion : samlResponse.getAssertions()) {
             // Check the Issuer
@@ -101,13 +107,13 @@ public class SAMLSSOResponseValidator {
                 throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
             }
             validateIssuer(assertion.getIssuer());
-            
+
             if (enforceAssertionsSigned && postBinding && assertion.getSignature() == null) {
                 LOG.fine("If the HTTP Post binding is used to deliver the Response, "
                          + "the enclosed assertions must be signed");
                 throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
             }
-            
+
             // Check for AuthnStatements and validate the Subject accordingly
             if (assertion.getAuthnStatements() != null
                 && !assertion.getAuthnStatements().isEmpty()) {
@@ -124,30 +130,25 @@ public class SAMLSSOResponseValidator {
                 }
             }
         }
-        
+
         if (validAssertion == null) {
             LOG.fine("The Response did not contain any Authentication Statement that matched "
                      + "the Subject Confirmation criteria");
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
-        
+
         SSOValidatorResponse validatorResponse = new SSOValidatorResponse();
         validatorResponse.setResponseId(samlResponse.getID());
         validatorResponse.setSessionNotOnOrAfter(sessionNotOnOrAfter);
-        // the assumption for now is that SAMLResponse will contain only a single assertion
-<<<<<<< HEAD
-        Element assertionElement = samlResponse.getAssertions().get(0).getDOM();
-        validatorResponse.setAssertion(DOM2Writer.nodeToString(assertionElement.cloneNode(true)));
-=======
+        
         Element assertionElement = validAssertion.getDOM();
         Element clonedAssertionElement = (Element)assertionElement.cloneNode(true);
-        validatorResponse.setAssertionElement(clonedAssertionElement);
+        // validatorResponse.setAssertionElement(clonedAssertionElement);
         validatorResponse.setAssertion(DOM2Writer.nodeToString(clonedAssertionElement));
-        
->>>>>>> 1c2a530... Adding SAML SSO tests.
+
         return validatorResponse;
     }
-    
+
     /**
      * Validate the Issuer (if it exists)
      */
@@ -155,23 +156,23 @@ public class SAMLSSOResponseValidator {
         if (issuer == null) {
             return;
         }
-        
+
         // Issuer value must match (be contained in) Issuer IDP
         if (enforceKnownIssuer && !issuerIDP.startsWith(issuer.getValue())) {
-            LOG.fine("Issuer value: " + issuer.getValue() + " does not match issuer IDP: " 
+            LOG.fine("Issuer value: " + issuer.getValue() + " does not match issuer IDP: "
                 + issuerIDP);
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
-        
+
         // Format must be nameid-format-entity
         if (issuer.getFormat() != null
             && !SAML2Constants.NAMEID_FORMAT_ENTITY.equals(issuer.getFormat())) {
-            LOG.fine("Issuer format is not null and does not equal: " 
+            LOG.fine("Issuer format is not null and does not equal: "
                 + SAML2Constants.NAMEID_FORMAT_ENTITY);
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
     }
-    
+
     /**
      * Validate the Subject (of an Authentication Statement).
      */
@@ -182,16 +183,16 @@ public class SAMLSSOResponseValidator {
             return false;
         }
         // We need to find a Bearer Subject Confirmation method
-        for (org.opensaml.saml2.core.SubjectConfirmation subjectConf 
+        for (org.opensaml.saml2.core.SubjectConfirmation subjectConf
             : subject.getSubjectConfirmations()) {
             if (SAML2Constants.CONF_BEARER.equals(subjectConf.getMethod())) {
                 validateSubjectConfirmation(subjectConf.getSubjectConfirmationData(), id, postBinding);
             }
         }
-        
+
         return true;
     }
-    
+
     /**
      * Validate a (Bearer) Subject Confirmation
      */
@@ -202,7 +203,7 @@ public class SAMLSSOResponseValidator {
             LOG.fine("Subject Confirmation Data of a Bearer Subject Confirmation is null");
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
-        
+
         // Recipient must match assertion consumer URL
         String recipient = subjectConfData.getRecipient();
         if (recipient == null || !recipient.equals(assertionConsumerURL)) {
@@ -210,14 +211,14 @@ public class SAMLSSOResponseValidator {
                 + assertionConsumerURL);
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
-        
+
         // We must have a NotOnOrAfter timestamp
         if (subjectConfData.getNotOnOrAfter() == null
             || subjectConfData.getNotOnOrAfter().isBeforeNow()) {
             LOG.fine("Subject Conf Data does not contain NotOnOrAfter or it has expired");
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
-        
+
         // Need to keep bearer assertion IDs based on NotOnOrAfter to detect replay attacks
         if (postBinding && replayCache != null) {
             if (replayCache.getId(id) == null) {
@@ -230,7 +231,7 @@ public class SAMLSSOResponseValidator {
                 throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
             }
         }
-        
+
         // Check address
         if (subjectConfData.getAddress() != null
             && !subjectConfData.getAddress().equals(clientAddress)) {
@@ -238,21 +239,21 @@ public class SAMLSSOResponseValidator {
                      + " client address " + clientAddress);
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
-        
+
         // It must not contain a NotBefore timestamp
         if (subjectConfData.getNotBefore() != null) {
             LOG.fine("The Subject Conf Data must not contain a NotBefore timestamp");
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
-        
+
         // InResponseTo must match the AuthnRequest request Id
         if (requestId != null && !requestId.equals(subjectConfData.getInResponseTo())) {
             LOG.fine("The InResponseTo String does match the original request id " + requestId);
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
-        
+
     }
-    
+
     private void validateAudienceRestrictionCondition(
         org.opensaml.saml2.core.Conditions conditions
     ) throws WSSecurityException {
@@ -262,13 +263,13 @@ public class SAMLSSOResponseValidator {
         }
         List<AudienceRestriction> audienceRestrs = conditions.getAudienceRestrictions();
         if (!matchSaml2AudienceRestriction(spIdentifier, audienceRestrs)) {
-            LOG.fine("Assertion does not contain unique subject provider identifier " 
+            LOG.fine("Assertion does not contain unique subject provider identifier "
                      + spIdentifier + " in the audience restriction conditions");
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
         }
     }
-    
-    
+
+
     private boolean matchSaml2AudienceRestriction(
         String appliesTo, List<AudienceRestriction> audienceRestrictions
     ) {
@@ -327,9 +328,19 @@ public class SAMLSSOResponseValidator {
     public void setSpIdentifier(String spIdentifier) {
         this.spIdentifier = spIdentifier;
     }
-    
+
     public void setReplayCache(TokenReplayCache<String> replayCache) {
         this.replayCache = replayCache;
     }
-    
+
+    public boolean isEnforceResponseSigned() {
+        return enforceResponseSigned;
+    }
+
+    /**
+     * Enforce whether a SAML Response must be signed.
+     */
+    public void setEnforceResponseSigned(boolean enforceResponseSigned) {
+        this.enforceResponseSigned = enforceResponseSigned;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/72c4194a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/CombinedValidatorTest.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/CombinedValidatorTest.java b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/CombinedValidatorTest.java
index 53aed3e..63b6db9 100644
--- a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/CombinedValidatorTest.java
+++ b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/CombinedValidatorTest.java
@@ -20,7 +20,10 @@
 package org.apache.cxf.rs.security.saml.sso;
 
 import java.io.InputStream;
+import java.io.StringReader;
 import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
 import java.util.Collections;
 
 import javax.xml.parsers.DocumentBuilder;
@@ -28,54 +31,72 @@ import javax.xml.parsers.DocumentBuilderFactory;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
-
-import org.apache.wss4j.common.crypto.Crypto;
-import org.apache.wss4j.common.crypto.Merlin;
-import org.apache.wss4j.common.saml.OpenSAMLUtil;
-import org.apache.wss4j.common.saml.SAMLCallback;
-import org.apache.wss4j.common.saml.SAMLUtil;
-import org.apache.wss4j.common.saml.SamlAssertionWrapper;
-import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
-import org.apache.wss4j.common.saml.bean.ConditionsBean;
-import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
-import org.apache.wss4j.common.saml.builder.SAML2Constants;
-import org.apache.wss4j.common.util.Loader;
-import org.apache.wss4j.dom.WSConstants;
-import org.apache.wss4j.dom.WSSConfig;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.ws.security.WSConstants;
+import org.apache.ws.security.WSSConfig;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoType;
+import org.apache.ws.security.components.crypto.Merlin;
+import org.apache.ws.security.saml.ext.AssertionWrapper;
+import org.apache.ws.security.saml.ext.OpenSAMLUtil;
+import org.apache.ws.security.saml.ext.SAMLParms;
+import org.apache.ws.security.saml.ext.bean.AudienceRestrictionBean;
+import org.apache.ws.security.saml.ext.bean.ConditionsBean;
+import org.apache.ws.security.saml.ext.bean.SubjectConfirmationDataBean;
+import org.apache.ws.security.saml.ext.builder.SAML2Constants;
+import org.apache.ws.security.util.Loader;
 import org.joda.time.DateTime;
-import org.opensaml.saml.common.xml.SAMLConstants;
-import org.opensaml.saml.saml2.core.Response;
-import org.opensaml.saml.saml2.core.Status;
+import org.opensaml.common.SignableSAMLObject;
+import org.opensaml.common.xml.SAMLConstants;
+import org.opensaml.saml2.core.Response;
+import org.opensaml.saml2.core.Status;
+import org.opensaml.xml.security.x509.BasicX509Credential;
+import org.opensaml.xml.security.x509.X509KeyInfoGeneratorFactory;
+import org.opensaml.xml.signature.KeyInfo;
+import org.opensaml.xml.signature.Signature;
+import org.opensaml.xml.signature.SignatureConstants;
 
 /**
  * Some unit tests for the SAMLProtocolResponseValidator and the SAMLSSOResponseValidator
  */
 public class CombinedValidatorTest extends org.junit.Assert {
-    
+
+    private static final DocumentBuilderFactory DOC_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
+
     static {
         WSSConfig.init();
         OpenSAMLUtil.initSamlEngine();
+        DOC_BUILDER_FACTORY.setNamespaceAware(true);
     }
 
     @org.junit.Test
     public void testSuccessfulValidation() throws Exception {
-        
-        Element responseElement = createResponse();
+
+        DocumentBuilder docBuilder = DOC_BUILDER_FACTORY.newDocumentBuilder();
+        Document doc = docBuilder.newDocument();
+
+        Response response = createResponse(doc);
+
+        Element responseElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(responseElement);
+        assertNotNull(responseElement);
+
         Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
-        
+
         Crypto issuerCrypto = new Merlin();
         KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
         ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
         InputStream input = Merlin.loadInputStream(loader, "alice.jks");
         keyStore.load(input, "password".toCharArray());
         ((Merlin)issuerCrypto).setKeyStore(keyStore);
-        
+
         // Validate the Response
         SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
         validator.validateSamlResponse(
             marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
         );
-        
+
         // Test SSO validation
         SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
         ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
@@ -83,62 +104,70 @@ public class CombinedValidatorTest extends org.junit.Assert {
         ssoValidator.setClientAddress("http://apache.org");
         ssoValidator.setRequestId("12345");
         ssoValidator.setSpIdentifier("http://service.apache.org");
-        
+
         // Parse the response
-        SSOValidatorResponse ssoResponse = 
+        SSOValidatorResponse ssoResponse =
             ssoValidator.validateSamlResponse(marshalledResponse, false);
-        SamlAssertionWrapper parsedAssertion = 
-            new SamlAssertionWrapper(ssoResponse.getAssertionElement());
-        
-        assertEquals("alice", parsedAssertion.getSubjectName());
+        Document assertionDoc = StaxUtils.read(new StringReader(ssoResponse.getAssertion()));
+        AssertionWrapper parsedAssertion =
+            new AssertionWrapper(assertionDoc.getDocumentElement());
+
+        assertEquals("alice", parsedAssertion.getSaml2().getSubject().getNameID().getValue());
     }
-    
+
     @org.junit.Test
     public void testWrappingAttack3() throws Exception {
-        Element responseElement = createResponse();
-        
+        DocumentBuilder docBuilder = DOC_BUILDER_FACTORY.newDocumentBuilder();
+        Document doc = docBuilder.newDocument();
+
+        Response response = createResponse(doc);
+
+        Element responseElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(responseElement);
+        assertNotNull(responseElement);
+
         // Get Assertion Element
-        Element assertionElement = 
+        Element assertionElement =
             (Element)responseElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Assertion").item(0);
         assertNotNull(assertionElement);
-        
+
         // Clone it, strip the Signature, modify the Subject, change Subj Conf
         Element clonedAssertion = (Element)assertionElement.cloneNode(true);
         clonedAssertion.setAttributeNS(null, "ID", "_12345623562");
-        Element sigElement = 
+        Element sigElement =
             (Element)clonedAssertion.getElementsByTagNameNS(WSConstants.SIG_NS, "Signature").item(0);
         clonedAssertion.removeChild(sigElement);
-        
-        Element subjElement = 
+
+        Element subjElement =
             (Element)clonedAssertion.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Subject").item(0);
-        Element subjNameIdElement = 
+        Element subjNameIdElement =
             (Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "NameID").item(0);
         subjNameIdElement.setTextContent("bob");
-        
-        Element subjConfElement = 
+
+        Element subjConfElement =
             (Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "SubjectConfirmation").item(0);
         subjConfElement.setAttributeNS(null, "Method", SAML2Constants.CONF_SENDER_VOUCHES);
-        
+
         // Now insert the modified cloned Assertion into the Response before actual assertion
         responseElement.insertBefore(clonedAssertion, assertionElement);
-        
+
         // System.out.println(DOM2Writer.nodeToString(responseElement));
-        
+
         Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
-        
+
         Crypto issuerCrypto = new Merlin();
         KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
         ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
         InputStream input = Merlin.loadInputStream(loader, "alice.jks");
         keyStore.load(input, "password".toCharArray());
         ((Merlin)issuerCrypto).setKeyStore(keyStore);
-        
+
         // Validate the Response
         SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
         validator.validateSamlResponse(
             marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
         );
-        
+
         // Test SSO validation
         SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
         ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
@@ -146,73 +175,219 @@ public class CombinedValidatorTest extends org.junit.Assert {
         ssoValidator.setClientAddress("http://apache.org");
         ssoValidator.setRequestId("12345");
         ssoValidator.setSpIdentifier("http://service.apache.org");
-        
+
         // Parse the response
-        SSOValidatorResponse ssoResponse = 
+        SSOValidatorResponse ssoResponse =
             ssoValidator.validateSamlResponse(marshalledResponse, false);
-        SamlAssertionWrapper parsedAssertion = 
-            new SamlAssertionWrapper(ssoResponse.getAssertionElement());
-        
-        assertEquals("alice", parsedAssertion.getSubjectName());
+        Document assertionDoc = StaxUtils.read(new StringReader(ssoResponse.getAssertion()));
+        AssertionWrapper parsedAssertion =
+            new AssertionWrapper(assertionDoc.getDocumentElement());
+
+        assertEquals("alice", parsedAssertion.getSaml2().getSubject().getNameID().getValue());
     }
-    
-    private Element createResponse() throws Exception {
-        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
-        docBuilderFactory.setNamespaceAware(true);
-        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+
+    @org.junit.Test
+    public void testSuccessfulSignedValidation() throws Exception {
+
+        DocumentBuilder docBuilder = DOC_BUILDER_FACTORY.newDocumentBuilder();
         Document doc = docBuilder.newDocument();
-        
-        Status status = 
+
+        Response response = createResponse(doc);
+
+        Crypto issuerCrypto = new Merlin();
+        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
+        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
+        keyStore.load(input, "password".toCharArray());
+        ((Merlin)issuerCrypto).setKeyStore(keyStore);
+
+        signResponse(response, "alice", "password", issuerCrypto, true);
+
+        Element responseElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(responseElement);
+        assertNotNull(responseElement);
+
+        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
+
+        // Validate the Response
+        SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
+        validator.validateSamlResponse(
+            marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
+        );
+
+        // Test SSO validation
+        SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
+        ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
+        ssoValidator.setAssertionConsumerURL("http://recipient.apache.org");
+        ssoValidator.setClientAddress("http://apache.org");
+        ssoValidator.setRequestId("12345");
+        ssoValidator.setSpIdentifier("http://service.apache.org");
+
+        // Parse the response
+        SSOValidatorResponse ssoResponse =
+            ssoValidator.validateSamlResponse(marshalledResponse, false);
+        Document assertionDoc = StaxUtils.read(new StringReader(ssoResponse.getAssertion()));
+        AssertionWrapper parsedAssertion =
+            new AssertionWrapper(assertionDoc.getDocumentElement());
+
+        assertEquals("alice", parsedAssertion.getSaml2().getSubject().getNameID().getValue());
+    }
+
+    @org.junit.Test
+    public void testEnforceResponseSigned() throws Exception {
+
+        DocumentBuilder docBuilder = DOC_BUILDER_FACTORY.newDocumentBuilder();
+        Document doc = docBuilder.newDocument();
+
+        Response response = createResponse(doc);
+
+        Element responseElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(responseElement);
+        assertNotNull(responseElement);
+
+        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
+
+        Crypto issuerCrypto = new Merlin();
+        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
+        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
+        keyStore.load(input, "password".toCharArray());
+        ((Merlin)issuerCrypto).setKeyStore(keyStore);
+
+        // Validate the Response
+        SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
+        validator.validateSamlResponse(
+            marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
+        );
+
+        // Test SSO validation
+        SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
+        ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
+        ssoValidator.setAssertionConsumerURL("http://recipient.apache.org");
+        ssoValidator.setClientAddress("http://apache.org");
+        ssoValidator.setRequestId("12345");
+        ssoValidator.setSpIdentifier("http://service.apache.org");
+        ssoValidator.setEnforceResponseSigned(true);
+
+        // Parse the response
+        try {
+            ssoValidator.validateSamlResponse(marshalledResponse, false);
+            fail("Failure expected on an unsigned Response");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+
+    private Response createResponse(Document doc) throws Exception {
+        Status status =
             SAML2PResponseComponentBuilder.createStatus(
                 SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
             );
-        Response response = 
+        Response response =
             SAML2PResponseComponentBuilder.createSAMLResponse(
                 "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
             );
-        
+        response.setDestination("http://recipient.apache.org");
+
         // Create an AuthenticationAssertion
         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
         callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
         callbackHandler.setIssuer("http://cxf.apache.org/issuer");
         callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
         callbackHandler.setSubjectName("alice");
-        
+
         SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
         subjectConfirmationData.setAddress("http://apache.org");
         subjectConfirmationData.setInResponseTo("12345");
         subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
         subjectConfirmationData.setRecipient("http://recipient.apache.org");
         callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
-        
+
         ConditionsBean conditions = new ConditionsBean();
         conditions.setNotBefore(new DateTime());
         conditions.setNotAfter(new DateTime().plusMinutes(5));
-        
+
         AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
         audienceRestriction.setAudienceURIs(Collections.singletonList("http://service.apache.org"));
         conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
         callbackHandler.setConditions(conditions);
-        
-        SAMLCallback samlCallback = new SAMLCallback();
-        SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
-        SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
-        
+
+        SAMLParms samlParms = new SAMLParms();
+        samlParms.setCallbackHandler(callbackHandler);
+        AssertionWrapper assertion = new AssertionWrapper(samlParms);
+
         Crypto issuerCrypto = new Merlin();
         KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
         ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
         InputStream input = Merlin.loadInputStream(loader, "alice.jks");
         keyStore.load(input, "password".toCharArray());
         ((Merlin)issuerCrypto).setKeyStore(keyStore);
-        
+
         assertion.signAssertion("alice", "password", issuerCrypto, false);
-        
+
         response.getAssertions().add(assertion.getSaml2());
-        
-        Element policyElement = OpenSAMLUtil.toDom(response, doc);
-        doc.appendChild(policyElement);
-        assertNotNull(policyElement);
-        
-        return policyElement;
+
+        return response;
+    }
+
+    private void signResponse(
+        Response response,
+        String issuerKeyName,
+        String issuerKeyPassword,
+        Crypto issuerCrypto,
+        boolean useKeyInfo
+    ) throws Exception {
+        //
+        // Create the signature
+        //
+        Signature signature = OpenSAMLUtil.buildSignature();
+        signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
+
+        // prepare to sign the SAML token
+        CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
+        cryptoType.setAlias(issuerKeyName);
+        X509Certificate[] issuerCerts = issuerCrypto.getX509Certificates(cryptoType);
+        if (issuerCerts == null) {
+            throw new Exception(
+                    "No issuer certs were found to sign the SAML Assertion using issuer name: "
+                            + issuerKeyName);
+        }
+
+        String sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
+        String pubKeyAlgo = issuerCerts[0].getPublicKey().getAlgorithm();
+
+        if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
+            sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_DSA;
+        }
+
+        PrivateKey privateKey = issuerCrypto.getPrivateKey(issuerKeyName, issuerKeyPassword);
+
+        signature.setSignatureAlgorithm(sigAlgo);
+
+        BasicX509Credential signingCredential = new BasicX509Credential();
+        signingCredential.setEntityCertificate(issuerCerts[0]);
+        signingCredential.setPrivateKey(privateKey);
+
+        signature.setSigningCredential(signingCredential);
+
+        if (useKeyInfo) {
+            X509KeyInfoGeneratorFactory kiFactory = new X509KeyInfoGeneratorFactory();
+            kiFactory.setEmitEntityCertificate(true);
+
+            try {
+                KeyInfo keyInfo = kiFactory.newInstance().generate(signingCredential);
+                signature.setKeyInfo(keyInfo);
+            } catch (org.opensaml.xml.security.SecurityException ex) {
+                throw new Exception(
+                        "Error generating KeyInfo from signing credential", ex);
+            }
+        }
+
+        // add the signature to the assertion
+        SignableSAMLObject signableObject = (SignableSAMLObject) response;
+        signableObject.setSignature(signature);
+        signableObject.releaseDOM();
+        signableObject.releaseChildrenDOM(true);
     }
+
 }