Repository: cxf
Updated Branches:
  refs/heads/2.7.x-fixes c716a824b -> 6c2ab7d08


Adding more SAML SSO tests

Conflicts:
        
rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/CombinedValidatorTest.java

Conflicts:
        
rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java
        
rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/CombinedValidatorTest.java


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

Branch: refs/heads/2.7.x-fixes
Commit: ef06a5634c203bd23aa9887d91db3003451c7c4a
Parents: c716a82
Author: Colm O hEigeartaigh <cohei...@apache.org>
Authored: Fri Jul 31 11:59:53 2015 +0100
Committer: Colm O hEigeartaigh <cohei...@apache.org>
Committed: Fri Jul 31 12:06:51 2015 +0100

----------------------------------------------------------------------
 ...AbstractRequestAssertionConsumerHandler.java |  23 ++
 .../saml/sso/SAMLSSOResponseValidator.java      |  17 ++
 .../saml/sso/CombinedValidatorTest.java         | 211 ++++++++++++++++++-
 3 files changed, 240 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/ef06a563/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java
----------------------------------------------------------------------
diff --git 
a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java
 
b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java
index f622ace..2c37543 100644
--- 
a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java
+++ 
b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java
@@ -65,6 +65,11 @@ public class AbstractRequestAssertionConsumerHandler extends 
AbstractSSOSpHandle
     private boolean supportBase64Encoding = true;
     private boolean enforceAssertionsSigned = true;
     private boolean enforceKnownIssuer = true;
+<<<<<<< HEAD
+=======
+    private boolean keyInfoMustBeAvailable = true;
+    private boolean enforceResponseSigned;
+>>>>>>> a61db28... Adding more SAML SSO tests
     private TokenReplayCache<String> replayCache;
 
     private MessageContext messageContext;
@@ -318,6 +323,7 @@ public class AbstractRequestAssertionConsumerHandler 
extends AbstractSSOSpHandle
             ssoResponseValidator.setRequestId(requestState.getSamlRequestId());
             ssoResponseValidator.setSpIdentifier(requestState.getIssuerId());
             
ssoResponseValidator.setEnforceAssertionsSigned(enforceAssertionsSigned);
+            
ssoResponseValidator.setEnforceResponseSigned(enforceResponseSigned);
             ssoResponseValidator.setEnforceKnownIssuer(enforceKnownIssuer);
             ssoResponseValidator.setReplayCache(getReplayCache());
 
@@ -334,4 +340,21 @@ public class AbstractRequestAssertionConsumerHandler 
extends AbstractSSOSpHandle
         LOG.warning(errorMsg.toString());
     }
     
+<<<<<<< HEAD
+=======
+    public void setKeyInfoMustBeAvailable(boolean keyInfoMustBeAvailable) {
+        this.keyInfoMustBeAvailable = keyInfoMustBeAvailable;
+    }
+
+    public boolean isEnforceResponseSigned() {
+        return enforceResponseSigned;
+    }
+
+    /**
+     * Enforce that a SAML Response must be signed.
+     */
+    public void setEnforceResponseSigned(boolean enforceResponseSigned) {
+        this.enforceResponseSigned = enforceResponseSigned;
+    }
+>>>>>>> a61db28... Adding more SAML SSO tests
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/ef06a563/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 2d864a5..30bdcd8 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
@@ -44,6 +44,7 @@ public class SAMLSSOResponseValidator {
     private String clientAddress;
     private String requestId;
     private String spIdentifier;
+    private boolean enforceResponseSigned;
     private boolean enforceAssertionsSigned = true;
     private boolean enforceKnownIssuer = true;
     private TokenReplayCache<String> replayCache;
@@ -91,6 +92,11 @@ public class SAMLSSOResponseValidator {
             throw new WSSecurityException(WSSecurityException.FAILURE, 
"invalidSAMLsecurity");
         }
         
+        if (enforceResponseSigned && !samlResponse.isSigned()) {
+            LOG.fine("The Response must be signed!");
+            throw new 
WSSecurityException(WSSecurityException.ErrorCode.FAILURE, 
"invalidSAMLsecurity");
+        }
+        
         // Validate Assertions
         org.opensaml.saml2.core.Assertion validAssertion = null;
         Date sessionNotOnOrAfter = null;
@@ -333,5 +339,16 @@ public class SAMLSSOResponseValidator {
     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/ef06a563/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 7b9a9c1..466f97f 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
@@ -22,6 +22,8 @@ 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;
@@ -29,6 +31,7 @@ import javax.xml.parsers.DocumentBuilderFactory;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+<<<<<<< HEAD
 import org.apache.cxf.staxutils.StaxUtils;
 import org.apache.ws.security.WSConstants;
 import org.apache.ws.security.WSSConfig;
@@ -42,25 +45,65 @@ 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.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.CryptoType;
+import org.apache.wss4j.common.crypto.Merlin;
+import org.apache.wss4j.common.ext.WSSecurityException;
+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;
+>>>>>>> a61db28... Adding more SAML SSO tests
 import org.joda.time.DateTime;
+<<<<<<< HEAD
 import org.opensaml.common.xml.SAMLConstants;
 import org.opensaml.saml2.core.Response;
 import org.opensaml.saml2.core.Status;
+=======
+import org.opensaml.saml.common.SignableSAMLObject;
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.saml2.core.Response;
+import org.opensaml.saml.saml2.core.Status;
+import org.opensaml.security.x509.BasicX509Credential;
+import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;
+import org.opensaml.xmlsec.signature.KeyInfo;
+import org.opensaml.xmlsec.signature.Signature;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
+>>>>>>> 3228637... Adding more SAML SSO tests
 
 /**
  * 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();
@@ -96,7 +139,14 @@ public class CombinedValidatorTest extends org.junit.Assert 
{
     
     @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 = 
@@ -158,12 +208,98 @@ public class CombinedValidatorTest extends 
org.junit.Assert {
         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();
         
+        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);
+        SamlAssertionWrapper parsedAssertion = 
+            new SamlAssertionWrapper(ssoResponse.getAssertionElement());
+        
+        assertEquals("alice", parsedAssertion.getSubjectName());
+    }
+    
+    @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
@@ -172,6 +308,7 @@ public class CombinedValidatorTest extends org.junit.Assert 
{
             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();
@@ -211,10 +348,62 @@ public class CombinedValidatorTest extends 
org.junit.Assert {
         
         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(issuerCerts[0], 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.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);
     }
 }

Reply via email to