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