Author: coheigea
Date: Mon Jan 31 15:20:22 2011
New Revision: 1065649

URL: http://svn.apache.org/viewvc?rev=1065649&view=rev
Log:
[WSS-146] - Added a Validator for validating SAML Assertions. Added a negative 
test for a HOK assertion that isn't signed, and a signed HOK that fails trust 
validation.

Added:
    
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/SamlAssertionValidator.java
Modified:
    
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/processor/SAMLTokenProcessor.java
    
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/saml/ext/AssertionWrapper.java
    
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/Credential.java
    
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/SignatureTrustValidator.java
    
webservices/wss4j/trunk/src/test/java/org/apache/ws/security/saml/SamlNegativeTest.java

Modified: 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/processor/SAMLTokenProcessor.java
URL: 
http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/main/java/org/apache/ws/security/processor/SAMLTokenProcessor.java?rev=1065649&r1=1065648&r2=1065649&view=diff
==============================================================================
--- 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/processor/SAMLTokenProcessor.java
 (original)
+++ 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/processor/SAMLTokenProcessor.java
 Mon Jan 31 15:20:22 2011
@@ -27,11 +27,10 @@ import org.apache.ws.security.WSSConfig;
 import org.apache.ws.security.WSSecurityEngineResult;
 import org.apache.ws.security.WSSecurityException;
 import org.apache.ws.security.components.crypto.Crypto;
-import org.apache.ws.security.saml.SAMLKeyInfo;
 import org.apache.ws.security.saml.ext.AssertionWrapper;
 import org.apache.ws.security.util.DOM2Writer;
 import org.apache.ws.security.validate.Credential;
-import org.apache.ws.security.validate.SignatureTrustValidator;
+import org.apache.ws.security.validate.SamlAssertionValidator;
 import org.apache.ws.security.validate.Validator;
 
 import org.w3c.dom.Element;
@@ -42,7 +41,7 @@ import javax.security.auth.callback.Call
 public class SAMLTokenProcessor implements Processor {
     private static Log log = 
LogFactory.getLog(SAMLTokenProcessor.class.getName());
     
-    private Validator validator = new SignatureTrustValidator();
+    private Validator validator = new SamlAssertionValidator();
     
     public List<WSSecurityEngineResult> handleToken(
         Element elem, 
@@ -77,16 +76,15 @@ public class SAMLTokenProcessor implemen
         AssertionWrapper assertion = new AssertionWrapper(token);
         if (assertion.isSigned()) {
             assertion.verifySignature(crypto);
-            assertion.parseHOKSubject(crypto, cb);
-            
-            // Now verify trust on the signature credential
-            validator.setCrypto(crypto);
-            Credential credential = new Credential();
-            SAMLKeyInfo samlKeyInfo = assertion.getSignatureKeyInfo();
-            credential.setPublicKey(samlKeyInfo.getPublicKey());
-            credential.setCertificates(samlKeyInfo.getCerts());
-            validator.validate(credential);
         }
+            
+        // Now delegate the rest of the verification to the Validator
+        validator.setCrypto(crypto);
+        validator.setCallbackHandler(cb);
+        Credential credential = new Credential();
+        credential.setAssertion(assertion);
+        validator.validate(credential);
+        
         if (log.isDebugEnabled()) {
             log.debug("SAML Assertion issuer " + assertion.getIssuerString());
             log.debug(DOM2Writer.nodeToString(token));

Modified: 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/saml/ext/AssertionWrapper.java
URL: 
http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/main/java/org/apache/ws/security/saml/ext/AssertionWrapper.java?rev=1065649&r1=1065648&r2=1065649&view=diff
==============================================================================
--- 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/saml/ext/AssertionWrapper.java
 (original)
+++ 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/saml/ext/AssertionWrapper.java
 Mon Jan 31 15:20:22 2011
@@ -570,7 +570,7 @@ public class AssertionWrapper {
     }
     
     /**
-     * This method ensures that the Subject contains a KeyInfo for the 
holder-of-key confirmation
+     * This method parse the KeyInfo of the Subject for the holder-of-key 
confirmation
      * method, as required by the SAML Token spec. It then stores the 
SAMLKeyInfo object that
      * has been obtained for future processing by the SignatureProcessor.
      * @throws WSSecurityException
@@ -587,9 +587,6 @@ public class AssertionWrapper {
             } else if (saml2 != null) {
                 subjectKeyInfo = SAMLUtil.getCredentialFromSubject(saml2, 
crypto, cb);
             }
-            if (subjectKeyInfo == null) {
-                throw new WSSecurityException(WSSecurityException.FAILURE, 
"noKeyInSAMLToken");
-            }
         }
     }
     

Modified: 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/Credential.java
URL: 
http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/Credential.java?rev=1065649&r1=1065648&r2=1065649&view=diff
==============================================================================
--- 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/Credential.java
 (original)
+++ 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/Credential.java
 Mon Jan 31 15:20:22 2011
@@ -24,6 +24,7 @@ import java.security.cert.X509Certificat
 
 import org.apache.ws.security.message.token.Timestamp;
 import org.apache.ws.security.message.token.UsernameToken;
+import org.apache.ws.security.saml.ext.AssertionWrapper;
 
 /**
  * This class stores various Credential types that have to be validated by a 
Validator
@@ -35,6 +36,7 @@ public class Credential {
     private X509Certificate[] certs;
     private Timestamp timestamp;
     private UsernameToken usernametoken;
+    private AssertionWrapper assertion;
     
     /**
      * Set a PublicKey to be validated
@@ -100,4 +102,20 @@ public class Credential {
         return usernametoken;
     }
     
+    /**
+     * Set an AssertionWrapper to be validated
+     * @param timestamp an AssertionWrapper to be validated
+     */
+    public void setAssertion(AssertionWrapper assertion) {
+        this.assertion = assertion;
+    }
+    
+    /**
+     * Get an AssertionWrapper to be validated
+     * @return an AssertionWrapper to be validated
+     */
+    public AssertionWrapper getAssertion() {
+        return assertion;
+    }
+    
 }

Added: 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/SamlAssertionValidator.java
URL: 
http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/SamlAssertionValidator.java?rev=1065649&view=auto
==============================================================================
--- 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/SamlAssertionValidator.java
 (added)
+++ 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/SamlAssertionValidator.java
 Mon Jan 31 15:20:22 2011
@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ws.security.validate;
+
+import java.util.List;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.saml.SAMLKeyInfo;
+import org.apache.ws.security.saml.ext.AssertionWrapper;
+import org.apache.ws.security.saml.ext.OpenSAMLUtil;
+
+/**
+ * This class validates a SAML Assertion, which is wrapped in an 
"AssertionWrapper" instance.
+ * It assumes that the AssertionWrapper instance has already verified the 
signature on the
+ * assertion (done by the SAMLTokenProcessor). It verifies trust in the 
signature, and also
+ * checks that the Subject contains a KeyInfo (and processes it) for the 
holder-of-key case,
+ * and verifies that the Assertion is signed as well for holder-of-key. 
+ */
+public class SamlAssertionValidator extends SignatureTrustValidator {
+    
+    private CallbackHandler callbackHandler;
+    
+    /**
+     * Validate the credential argument. It must contain a non-null 
AssertionWrapper. 
+     * A Crypto and a CallbackHandler implementation is also required to be 
set.
+     * 
+     * @param credential the Credential to be validated
+     * @throws WSSecurityException on a failed validation
+     */
+    public void validate(Credential credential) throws WSSecurityException {
+        if (credential == null || credential.getAssertion() == null) {
+            throw new WSSecurityException(WSSecurityException.FAILURE, 
"noCredential");
+        }
+        AssertionWrapper assertion = credential.getAssertion();
+        
+        // Check HOK requirements
+        String confirmMethod = null;
+        List<String> methods = assertion.getConfirmationMethods();
+        if (methods != null && methods.size() > 0) {
+            confirmMethod = methods.get(0);
+        }
+        if (OpenSAMLUtil.isMethodHolderOfKey(confirmMethod)) {
+            // The Subject KeyInfo must not be null (and must be successfully 
parsed)
+            assertion.parseHOKSubject(crypto, callbackHandler);
+            if (assertion.getSubjectKeyInfo() == null) {
+                throw new WSSecurityException(WSSecurityException.FAILURE, 
"noKeyInSAMLToken");
+            }
+            // The assertion must have been signed for HOK
+            if (!assertion.isSigned()) {
+                throw new WSSecurityException(WSSecurityException.FAILURE, 
"invalidSAMLsecurity");
+            }
+        }
+
+        // Verify trust on the signature
+        if (assertion.isSigned()) {
+            Credential trustCredential = new Credential();
+            SAMLKeyInfo samlKeyInfo = assertion.getSignatureKeyInfo();
+            trustCredential.setPublicKey(samlKeyInfo.getPublicKey());
+            trustCredential.setCertificates(samlKeyInfo.getCerts());
+            super.validate(trustCredential);
+        }
+    }
+    
+    /**
+     * Set a CallbackHandler instance used to validate credentials.
+     * @param callbackHandler a CallbackHandler instance used to validate 
credentials
+     */
+    @Override
+    public void setCallbackHandler(CallbackHandler callbackHandler) {
+        this.callbackHandler = callbackHandler;
+    }
+    
+}

Modified: 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/SignatureTrustValidator.java
URL: 
http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/SignatureTrustValidator.java?rev=1065649&r1=1065648&r2=1065649&view=diff
==============================================================================
--- 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/SignatureTrustValidator.java
 (original)
+++ 
webservices/wss4j/trunk/src/main/java/org/apache/ws/security/validate/SignatureTrustValidator.java
 Mon Jan 31 15:20:22 2011
@@ -40,7 +40,7 @@ import org.apache.ws.security.components
 public class SignatureTrustValidator implements Validator {
     
     private static Log LOG = 
LogFactory.getLog(SignatureTrustValidator.class.getName());
-    private Crypto crypto;
+    protected Crypto crypto;
     
     /**
      * Validate the credential argument. It must contain a non-null 
X509Certificate chain

Modified: 
webservices/wss4j/trunk/src/test/java/org/apache/ws/security/saml/SamlNegativeTest.java
URL: 
http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/test/java/org/apache/ws/security/saml/SamlNegativeTest.java?rev=1065649&r1=1065648&r2=1065649&view=diff
==============================================================================
--- 
webservices/wss4j/trunk/src/test/java/org/apache/ws/security/saml/SamlNegativeTest.java
 (original)
+++ 
webservices/wss4j/trunk/src/test/java/org/apache/ws/security/saml/SamlNegativeTest.java
 Mon Jan 31 15:20:22 2011
@@ -96,7 +96,6 @@ public class SamlNegativeTest extends or
      * should fail.
      */
     @org.junit.Test
-    @org.junit.Ignore
     public void testSAML2AuthnAssertionModified() throws Exception {
         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
         callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
@@ -149,7 +148,6 @@ public class SamlNegativeTest extends or
      * a signed assertion.
      */
     @org.junit.Test
-    @org.junit.Ignore
     public void testSAML1SignedKeyHolderSigModified() throws Exception {
         SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
         callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
@@ -203,7 +201,6 @@ public class SamlNegativeTest extends or
      * The signature verification should then fail.
      */
     @org.junit.Test
-    @org.junit.Ignore
     public void testSAML2SignedKeyHolderKeyModified() throws Exception {
         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
         callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
@@ -289,6 +286,94 @@ public class SamlNegativeTest extends or
     }
     
     /**
+     * Test that creates a SAML 1.1 authentication assertion that uses 
holder-of-key, but is 
+     * not signed, and hence will fail processing.
+     */
+    @org.junit.Test
+    public void testHOKNotSigned() throws Exception {
+        SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
+        callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
+        callbackHandler.setConfirmationMethod(SAML1Constants.CONF_HOLDER_KEY);
+        SAMLIssuer saml = new SAMLIssuerImpl();
+        saml.setIssuerName("www.example.com");
+        saml.setIssuerCrypto(issuerCrypto);
+        saml.setIssuerKeyName("wss40_server");
+        saml.setIssuerKeyPassword("security");
+        // saml.setSignAssertion(true);
+        saml.setCallbackHandler(callbackHandler);
+        AssertionWrapper assertion = saml.newAssertion();
+
+        Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+
+        WSSecSAMLToken wsSign = new WSSecSAMLToken();
+        Document signedDoc = wsSign.build(doc, assertion, secHeader);
+
+        String outputString = 
+            
org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("SAML 1.1 Authn Assertion (unsigned key holder):");
+            LOG.debug(outputString);
+        }
+        
+        try {
+            verify(signedDoc, trustCrypto);
+            fail("Expected failure on an unsigned assertion with holder-of-key 
confirmation method");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    /**
+     * Test that creates, sends and processes a signed SAML 2 authentication 
assertion, but it
+     * is rejected in processing as the signature on the assertion is not 
trusted.
+     */
+    @org.junit.Test
+    @SuppressWarnings("unchecked")
+    public void testSAML2TrustFailure() throws Exception {
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_HOLDER_KEY);
+        SAMLIssuer saml = new SAMLIssuerImpl();
+        saml.setIssuerName("www.example.com");
+        saml.setIssuerCrypto(CryptoFactory.getInstance("crypto.properties"));
+        saml.setIssuerKeyName("16c73ab6-b892-458f-abf5-2f875f74882e");
+        saml.setIssuerKeyPassword("security");
+        saml.setSignAssertion(true);
+        saml.setSamlVersion("2.0");
+        saml.setCallbackHandler(callbackHandler);
+        AssertionWrapper assertion = saml.newAssertion();
+
+        WSSecSignatureSAML wsSign = new WSSecSignatureSAML();
+        wsSign.setUserInfo("wss40", "security");
+        wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256";);
+        
wsSign.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";);
+        wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
+
+        Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+
+        Document signedDoc = 
+            wsSign.build(doc, userCrypto, assertion, null, null, null, 
secHeader);
+
+        String outputString = 
+            
org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Untrusted signed SAML 2 Authn Assertion (key holder):");
+            LOG.debug(outputString);
+        }
+        
+        try {
+            verify(signedDoc, trustCrypto);
+            fail ("Failure expected on an untrusted signed assertion");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    /**
      * Verifies the soap envelope
      * 
      * @param doc


Reply via email to