You are viewing a plain text version of this content. The canonical link for it is here.
Posted to wss4j-dev@ws.apache.org by co...@apache.org on 2009/04/29 15:26:48 UTC

svn commit: r769781 - in /webservices/wss4j/trunk: src/org/apache/ws/security/message/token/ src/org/apache/ws/security/transform/ src/org/apache/ws/security/util/ test/wssec/

Author: coheigea
Date: Wed Apr 29 13:26:47 2009
New Revision: 769781

URL: http://svn.apache.org/viewvc?rev=769781&view=rev
Log:
[WSS-178] - A fix for "signature verification failure of signed saml token"
 - The problem was that the SAML Assertion was below the STR, which assumed the Assertion would always have been previously processed
 - Also fixed the "No message with ID..." exception that was being thrown in XML Security.

Added:
    webservices/wss4j/trunk/test/wssec/TestWSSecurityWSS178.java   (with props)
Modified:
    webservices/wss4j/trunk/src/org/apache/ws/security/message/token/SecurityTokenReference.java
    webservices/wss4j/trunk/src/org/apache/ws/security/transform/STRTransform.java
    webservices/wss4j/trunk/src/org/apache/ws/security/util/WSSecurityUtil.java

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/message/token/SecurityTokenReference.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/message/token/SecurityTokenReference.java?rev=769781&r1=769780&r2=769781&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/message/token/SecurityTokenReference.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/message/token/SecurityTokenReference.java Wed Apr 29 13:26:47 2009
@@ -166,69 +166,9 @@
                 WSSecurityException.INVALID_SECURITY, "badReferenceURI"
             );
         }
-        Element tokElement = null;
-        String tmpS = WSConstants.WSS_SAML_NS + WSConstants.WSS_SAML_ASSERTION;
-        String saml10 = WSConstants.WSS_SAML_NS + WSConstants.SAML_ASSERTION_ID;
-        if (tmpS.equals(ref.getValueType())
-            || saml10.equals(ref.getValueType())
-            || WSConstants.WSC_SCT.equals(ref.getValueType())) {
-            Element sa = docInfo.getAssertion();
-            String saID = null;
-            if (sa != null) {
-                saID = sa.getAttribute("AssertionID");
-            }
-            if (doDebug) {
-                log.debug("SAML token ID: " + saID);
-            }
-            String id = uri;
-            if (id.charAt(0) == '#') {
-                id = id.substring(1);
-            }
-            if (saID == null || !saID.equals(id)) {
-                if (cb != null) {
-                    //try to find a custom token
-                    WSPasswordCallback pwcb = 
-                        new WSPasswordCallback(id, WSPasswordCallback.CUSTOM_TOKEN);
-                    try {
-                        cb.handle(new Callback[]{pwcb});
-                    } catch (Exception e) {
-                        throw new WSSecurityException(
-                            WSSecurityException.FAILURE,
-                            "noPassword", 
-                            new Object[] {id}, 
-                            e
-                        );
-                    }
-                    
-                    Element assertionElem = pwcb.getCustomToken();
-                    if (assertionElem != null) {
-                        sa = (Element)doc.importNode(assertionElem, true);
-                    }
-                    else {
-                        throw new WSSecurityException(
-                            WSSecurityException.INVALID_SECURITY,
-                            "badReferenceURI",
-                            new Object[]{"uri:" + uri + ", saID: " + saID}
-                        );
-                    }
-                } else {
-                    throw new WSSecurityException(
-                        WSSecurityException.INVALID_SECURITY,
-                        "badReferenceURI",
-                        new Object[]{"uri:" + uri + ", saID: " + saID}
-                    );
-                }
-            }
-            tokElement = sa;
-        } else {
-            tokElement = WSSecurityUtil.getElementByWsuId(doc, uri);
-            
-            // In some scenarios id is used rather than wsu:Id
-            if (tokElement == null) {
-                tokElement = WSSecurityUtil.getElementByGenId(doc, uri);
-            }
-
-        }
+        
+        Element tokElement = findTokenElement(doc, docInfo, cb, uri, ref.getValueType());
+        
         if (tokElement == null) {
             throw new WSSecurityException(
                 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
@@ -267,73 +207,92 @@
                 WSSecurityException.INVALID_SECURITY, "badReferenceURI"
             );
         }
+        
+        Element tokElement = findTokenElement(doc, docInfo, cb, value, type);
+        
+        if (tokElement == null) {
+            throw new WSSecurityException(
+                WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
+                "noToken",
+                new Object[]{value}
+            );
+        }
+        return tokElement;
+    }
+    
+    
+    private Element findTokenElement(
+        Document doc,
+        WSDocInfo docInfo,
+        CallbackHandler cb,
+        String uri,
+        String type
+    ) {
         Element tokElement = null;
-        String saml10 = WSConstants.WSS_SAML_NS + WSConstants.SAML_ASSERTION_ID;
-        if (saml10.equals(type)
-            || WSConstants.WSC_SCT.equals(type)) {
+        String id = uri;
+        if (id.charAt(0) == '#') {
+            id = id.substring(1);
+        }
+        //
+        // If the type is a SAMLAssertionID then find the SAML assertion - first check
+        // if it has been previously processed, else search the header for it
+        //
+        String assertionStr = WSConstants.WSS_SAML_NS + WSConstants.ASSERTION_LN;
+        if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(type)
+            || assertionStr.equals(type)) {
             Element sa = docInfo.getAssertion();
-            String saID = null;
             if (sa != null) {
-                saID = sa.getAttribute("AssertionID");
-            }
-            if (doDebug) {
-                log.debug("SAML token ID: " + saID);
+                String saID = sa.getAttribute("AssertionID");
+                if (doDebug) {
+                    log.debug("SAML token ID: " + saID);
+                }
+                if (saID.equals(id)) {
+                    tokElement = sa;
+                }
             }
-            String id = value;
-            if (id.charAt(0) == '#') {
-                id = id.substring(1);
-            }
-            if (saID == null || !saID.equals(id)) {
-                if (cb != null) {
-                    //try to find a custom token
-                    WSPasswordCallback pwcb = 
-                        new WSPasswordCallback(id, WSPasswordCallback.CUSTOM_TOKEN);
-                    try {
-                        cb.handle(new Callback[]{pwcb});
-                    } catch (Exception e) {
-                        throw new WSSecurityException(
-                            WSSecurityException.FAILURE,
-                            "noPassword", 
-                            new Object[] {id}, 
-                            e
-                        );
-                    }
-                    
-                    Element assertionElem = pwcb.getCustomToken();
-                    if (assertionElem != null) {
-                        sa = (Element)doc.importNode(assertionElem, true);
-                    }
-                    else {
-                        throw new WSSecurityException(
-                            WSSecurityException.INVALID_SECURITY,
-                            "badReferenceURI",
-                            new Object[]{"uri:" + value + ", saID: " + saID}
-                        );
-                    }
-                } else {
-                    throw new WSSecurityException(
-                        WSSecurityException.INVALID_SECURITY,
-                        "badReferenceURI",
-                        new Object[]{"uri:" + value + ", saID: " + saID}
+            if (tokElement == null) {
+                Node assertion = 
+                    WSSecurityUtil.findSAMLAssertionElementById(
+                        doc.getDocumentElement(),
+                        id
                     );
+                if (assertion != null) {
+                    tokElement = (Element)assertion;
                 }
             }
-            tokElement = sa;
-        } else {
-            tokElement = WSSecurityUtil.getElementByWsuId(doc, value);
+        }
+        
+        // 
+        // Try to find a custom token
+        //
+        if (tokElement == null && WSConstants.WSC_SCT.equals(type) && cb != null) {
+            //try to find a custom token
+            WSPasswordCallback pwcb = 
+                new WSPasswordCallback(id, WSPasswordCallback.CUSTOM_TOKEN);
+            try {
+                cb.handle(new Callback[]{pwcb});
+                Element assertionElem = pwcb.getCustomToken();
+                if (assertionElem != null) {
+                    tokElement = (Element)doc.importNode(assertionElem, true);
+                }
+            } catch (Exception e) {
+                log.debug(e.getMessage(), e);
+                // Consume this failure
+            }
+        }
+        
+        //
+        // Finally try to find the element by its Id
+        //
+        if (tokElement == null) {
+            tokElement = WSSecurityUtil.getElementByWsuId(doc, uri);
             
             // In some scenarios id is used rather than wsu:Id
             if (tokElement == null) {
-                tokElement = WSSecurityUtil.getElementByGenId(doc, value);
+                tokElement = WSSecurityUtil.getElementByGenId(doc, uri);
             }
         }
-        if (tokElement == null) {
-            throw new WSSecurityException(
-                WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
-                "noToken",
-                new Object[]{value}
-            );
-        }
+        
         return tokElement;
     }
 

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/transform/STRTransform.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/transform/STRTransform.java?rev=769781&r1=769780&r2=769781&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/transform/STRTransform.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/transform/STRTransform.java Wed Apr 29 13:26:47 2009
@@ -240,7 +240,8 @@
         }
         // End of HACK
         catch (WSSecurityException ex) {
-            throw (new CanonicalizationException("WS Security Exception", ex));
+            log.debug(ex.getMessage(), ex);
+            throw (new CanonicalizationException("c14n.Canonicalizer.Exception", ex));
         }
     }
 

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/util/WSSecurityUtil.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/util/WSSecurityUtil.java?rev=769781&r1=769780&r2=769781&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/util/WSSecurityUtil.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/util/WSSecurityUtil.java Wed Apr 29 13:26:47 2009
@@ -257,6 +257,68 @@
         }
         return null;
     }
+    
+    
+    /**
+     * Returns the single SAMLAssertion element that contains an AssertionID/ID that
+     * matches the supplied parameter.
+     * 
+     * @param startNode Where to start the search
+     * @param value Value of the AssertionID/ID attribute
+     * @return The found element if there was exactly one match, or
+     *         <code>null</code> otherwise
+     */
+    public static Element findSAMLAssertionElementById(Node startNode, String value) {
+        Element foundElement = null;
+
+        //
+        // Replace the formerly recursive implementation with a depth-first-loop
+        // lookup
+        //
+        if (startNode == null) {
+            return null;
+        }
+        Node startParent = startNode.getParentNode();
+        Node processedNode = null;
+
+        while (startNode != null) {
+            // start node processing at this point
+            if (startNode.getNodeType() == Node.ELEMENT_NODE) {
+                Element se = (Element) startNode;
+                if ((se.hasAttribute("ID") && value.equals(se.getAttribute("ID")))
+                    || (se.hasAttribute("AssertionID") 
+                        && value.equals(se.getAttribute("AssertionID")))) {
+                    if (foundElement == null) {
+                        foundElement = se; // Continue searching to find duplicates
+                    } else {
+                        log.warn("Multiple elements with the same 'ID' attribute value!");
+                        return null;
+                    }
+                }
+            }
+
+            processedNode = startNode;
+            startNode = startNode.getFirstChild();
+
+            // no child, this node is done.
+            if (startNode == null) {
+                // close node processing, get sibling
+                startNode = processedNode.getNextSibling();
+            }
+            // no more siblings, get parent, all children
+            // of parent are processed.
+            while (startNode == null) {
+                processedNode = processedNode.getParentNode();
+                if (processedNode == startParent) {
+                    return foundElement;
+                }
+                // close parent node processing (processed node now)
+                startNode = processedNode.getNextSibling();
+            }
+        }
+        return foundElement;
+    }
+    
 
     /**
      * Returns the single element that contains an Id with value

Added: webservices/wss4j/trunk/test/wssec/TestWSSecurityWSS178.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/test/wssec/TestWSSecurityWSS178.java?rev=769781&view=auto
==============================================================================
--- webservices/wss4j/trunk/test/wssec/TestWSSecurityWSS178.java (added)
+++ webservices/wss4j/trunk/test/wssec/TestWSSecurityWSS178.java Wed Apr 29 13:26:47 2009
@@ -0,0 +1,244 @@
+/**
+ * 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 wssec;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.axis.Message;
+import org.apache.axis.MessageContext;
+import org.apache.axis.client.AxisClient;
+import org.apache.axis.configuration.NullProvider;
+import org.apache.axis.message.SOAPEnvelope;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ws.security.WSConstants;
+import org.apache.ws.security.WSPasswordCallback;
+import org.apache.ws.security.WSSecurityEngine;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoFactory;
+import org.apache.ws.security.message.WSSecEncrypt;
+import org.apache.ws.security.message.WSSecHeader;
+import org.apache.ws.security.saml.SAMLIssuer;
+import org.apache.ws.security.saml.SAMLIssuerFactory;
+import org.apache.ws.security.saml.WSSecSignatureSAML;
+import org.opensaml.SAMLAssertion;
+import org.w3c.dom.Document;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * WS-Security Test Case for WSS-178 - "signature verification failure of signed saml token due to "
+ * "The Reference for URI (bst-saml-uri) has no XMLSignatureInput".
+ * 
+ * The problem is that the signature is referring to a SecurityTokenReference via the STRTransform,
+ * which in turn is referring to the SAML Assertion. The request is putting the SAML Assertion
+ * below the SecurityTokenReference, and this is causing SecurityTokenReference.get*TokenElement
+ * to fail.
+ */
+public class TestWSSecurityWSS178 extends TestCase implements CallbackHandler {
+    private static final Log LOG = LogFactory.getLog(TestWSSecurityWSS178.class);
+    private static final String SOAPMSG = 
+        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" 
+        + "<SOAP-ENV:Envelope "
+        +   "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+        +   "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
+        +   "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" 
+        +   "<SOAP-ENV:Body>" 
+        +      "<ns1:testMethod xmlns:ns1=\"uri:LogTestService2\"></ns1:testMethod>" 
+        +   "</SOAP-ENV:Body>" 
+        + "</SOAP-ENV:Envelope>";
+
+    private WSSecurityEngine secEngine = new WSSecurityEngine();
+    private Crypto crypto = CryptoFactory.getInstance("crypto.properties");
+    private MessageContext msgContext;
+    private Message message;
+
+    /**
+     * TestWSSecurity constructor
+     * 
+     * @param name name of the test
+     */
+    public TestWSSecurityWSS178(String name) {
+        super(name);
+    }
+
+    /**
+     * JUnit suite
+     * 
+     * @return a junit test suite
+     */
+    public static Test suite() {
+        return new TestSuite(TestWSSecurityWSS178.class);
+    }
+
+    /**
+     * Setup method
+     * 
+     * @throws Exception Thrown when there is a problem in setup
+     */
+    protected void setUp() throws Exception {
+        AxisClient tmpEngine = new AxisClient(new NullProvider());
+        msgContext = new MessageContext(tmpEngine);
+        message = getSOAPMessage();
+    }
+
+    /**
+     * Constructs a soap envelope
+     * 
+     * @return soap envelope
+     * @throws Exception if there is any problem constructing the soap envelope
+     */
+    protected Message getSOAPMessage() throws Exception {
+        InputStream in = new ByteArrayInputStream(SOAPMSG.getBytes());
+        Message msg = new Message(in);
+        msg.setMessageContext(msgContext);
+        return msg;
+    }
+
+    /**
+     * Test where the Assertion is referenced using a Key Identifier 
+     * (from the SecurityTokenReference).
+     */
+    public void testKeyIdentifier() throws Exception {
+        SOAPEnvelope unsignedEnvelope = message.getSOAPEnvelope();
+        Document doc = unsignedEnvelope.getAsDocument();
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        
+        SAMLIssuer saml = SAMLIssuerFactory.getInstance("saml.properties");
+        SAMLAssertion assertion = saml.newAssertion();
+        String issuerKeyName = saml.getIssuerKeyName();
+        String issuerKeyPW = saml.getIssuerKeyPassword();
+        Crypto issuerCrypto = saml.getIssuerCrypto();
+        WSSecSignatureSAML wsSign = new WSSecSignatureSAML();
+        wsSign.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER);
+        Document samlDoc = 
+            wsSign.build(doc, null, assertion, issuerCrypto, 
+                issuerKeyName, issuerKeyPW, secHeader
+            );
+        
+        WSSecEncrypt builder = new WSSecEncrypt();
+        builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e");
+        builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
+        Document encryptedDoc = builder.build(samlDoc, crypto, secHeader);
+        
+        //
+        // Remove the assertion its place in the security header and then append it
+        //
+        org.w3c.dom.Element secHeaderElement = secHeader.getSecurityHeader();
+        org.w3c.dom.Node assertionNode = 
+            secHeaderElement.getElementsByTagName("Assertion").item(0);
+        secHeaderElement.removeChild(assertionNode);
+        secHeaderElement.appendChild(assertionNode);
+
+        String outputString = 
+            org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(encryptedDoc);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Encrypted message:");
+            LOG.debug(outputString);
+        }
+        
+        verify(encryptedDoc);
+    }
+    
+    
+    /**
+     * Test where the Assertion is referenced using direct reference
+     * (from the SecurityTokenReference).
+     */
+    public void testDirectReference() throws Exception {
+        SOAPEnvelope unsignedEnvelope = message.getSOAPEnvelope();
+        Document doc = unsignedEnvelope.getAsDocument();
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        
+        SAMLIssuer saml = SAMLIssuerFactory.getInstance("saml.properties");
+        SAMLAssertion assertion = saml.newAssertion();
+        String issuerKeyName = saml.getIssuerKeyName();
+        String issuerKeyPW = saml.getIssuerKeyPassword();
+        Crypto issuerCrypto = saml.getIssuerCrypto();
+        WSSecSignatureSAML wsSign = new WSSecSignatureSAML();
+        wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
+        Document samlDoc = 
+            wsSign.build(doc, null, assertion, issuerCrypto, 
+                issuerKeyName, issuerKeyPW, secHeader
+            );
+        
+        WSSecEncrypt builder = new WSSecEncrypt();
+        builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e");
+        builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
+        Document encryptedDoc = builder.build(samlDoc, crypto, secHeader);
+        
+        //
+        // Remove the assertion its place in the security header and then append it
+        //
+        org.w3c.dom.Element secHeaderElement = secHeader.getSecurityHeader();
+        org.w3c.dom.Node assertionNode = 
+            secHeaderElement.getElementsByTagName("Assertion").item(0);
+        secHeaderElement.removeChild(assertionNode);
+        secHeaderElement.appendChild(assertionNode);
+
+        String outputString = 
+            org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(encryptedDoc);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Encrypted message:");
+            LOG.debug(outputString);
+        }
+        
+        verify(encryptedDoc);
+    }
+
+    
+    /**
+     * Verifies the soap envelope
+     * <p/>
+     * 
+     * @param envelope 
+     * @throws Exception Thrown when there is a problem in verification
+     */
+    private java.util.List verify(Document doc) throws Exception {
+        return secEngine.processSecurityHeader(doc, null, this, crypto);
+    }
+
+    public void handle(Callback[] callbacks)
+        throws IOException, UnsupportedCallbackException {
+        for (int i = 0; i < callbacks.length; i++) {
+            if (callbacks[i] instanceof WSPasswordCallback) {
+                WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
+                /*
+                 * here call a function/method to lookup the password for
+                 * the given identifier (e.g. a user name or keystore alias)
+                 * e.g.: pc.setPassword(passStore.getPassword(pc.getIdentfifier))
+                 * for Testing we supply a fixed name here.
+                 */
+                pc.setPassword("security");
+            } else {
+                throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
+            }
+        }
+    }
+}

Propchange: webservices/wss4j/trunk/test/wssec/TestWSSecurityWSS178.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: webservices/wss4j/trunk/test/wssec/TestWSSecurityWSS178.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date



---------------------------------------------------------------------
To unsubscribe, e-mail: wss4j-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: wss4j-dev-help@ws.apache.org