You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2011/08/24 18:46:56 UTC

svn commit: r1161199 - in /cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security: saml/SamlEnvelopedInHandler.java saml/SamlEnvelopedOutInterceptor.java xml/XmlSigInHandler.java xml/XmlSigOutInterceptor.java

Author: sergeyb
Date: Wed Aug 24 16:46:55 2011
New Revision: 1161199

URL: http://svn.apache.org/viewvc?rev=1161199&view=rev
Log:
[CXF-3587] Optionally signing content exclusive of SAML tokens

Modified:
    cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedInHandler.java
    cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedOutInterceptor.java
    cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java
    cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigOutInterceptor.java

Modified: cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedInHandler.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedInHandler.java?rev=1161199&r1=1161198&r2=1161199&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedInHandler.java (original)
+++ cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedInHandler.java Wed Aug 24 16:46:55 2011
@@ -27,9 +27,9 @@ import javax.xml.stream.XMLStreamReader;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
-import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
+
 import org.apache.cxf.helpers.DOMUtils;
 import org.apache.cxf.jaxrs.model.ClassResourceInfo;
 import org.apache.cxf.message.Message;
@@ -91,16 +91,11 @@ public class SamlEnvelopedInHandler exte
     }
     
     private Element getActualBody(Element root) {
-        NodeList list = root.getChildNodes();
-        for (int i = 0; i < list.getLength(); i++) {
-            Node node = list.item(i);
-            if (node instanceof Element) {
-                root.removeChild(node);
-                return (Element)node;
-            }
+        Element node = DOMUtils.getFirstElement(root);
+        if (node != null) {
+            root.removeChild(node);
         }
-        return null;
-       
+        return node;
     }
     
     protected Element getNode(Element parent, String ns, String name) {

Modified: cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedOutInterceptor.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedOutInterceptor.java?rev=1161199&r1=1161198&r2=1161199&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedOutInterceptor.java (original)
+++ cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedOutInterceptor.java Wed Aug 24 16:46:55 2011
@@ -18,25 +18,26 @@
  */
 package org.apache.cxf.rs.security.saml;
 
+import javax.xml.namespace.QName;
+
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-
 import org.apache.cxf.helpers.DOMUtils;
+import org.apache.cxf.helpers.XMLUtils;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.rs.security.xml.AbstractXmlSecOutInterceptor;
 import org.apache.cxf.rs.security.xml.XmlEncOutInterceptor;
 import org.apache.cxf.rs.security.xml.XmlSigOutInterceptor;
 import org.apache.ws.security.saml.ext.AssertionWrapper;
 
+
 public class SamlEnvelopedOutInterceptor extends AbstractXmlSecOutInterceptor {
 
-    private static final String DEFAULT_ENV_NAME = "Envelope";
-    private static final String DEFAULT_ENV_NAMESPACE = "http://org.apache.cxf/rs/env";
     private static final String DEFAULT_ENV_PREFIX = "env";
-    private String envelopeName = DEFAULT_ENV_NAME;
-    private String envelopeNamespace = DEFAULT_ENV_NAMESPACE;
-    private String envelopePrefix = DEFAULT_ENV_PREFIX;
+    private static final QName DEFAULT_ENV_QNAME = 
+        new QName("http://org.apache.cxf/rs/env", "Envelope", DEFAULT_ENV_PREFIX);
+    private QName envelopeQName = DEFAULT_ENV_QNAME;
     
     public SamlEnvelopedOutInterceptor() {
         // SAML assertions may contain enveloped XML signatures so
@@ -56,15 +57,25 @@ public class SamlEnvelopedOutInterceptor
     private Document createEnvelopedSamlToken(Message message, Document payloadDoc) 
         throws Exception {
         
+        Element docEl = payloadDoc.getDocumentElement();
+        AssertionWrapper assertion = SAMLUtils.createAssertion(message);
+        
+        QName rootName = DOMUtils.getElementQName(payloadDoc.getDocumentElement());
+        if (rootName.equals(envelopeQName)) {
+            docEl.appendChild(assertion.toDOM(payloadDoc));
+            return payloadDoc;
+        }
+        
         Document newDoc = DOMUtils.createDocument();
+        
         Element root = 
-            newDoc.createElementNS(envelopeNamespace, envelopePrefix + ":" + envelopeName);
+            newDoc.createElementNS(envelopeQName.getNamespaceURI(), 
+                    envelopeQName.getPrefix() + ":" + envelopeQName.getLocalPart());
         newDoc.appendChild(root);
-        AssertionWrapper assertion = SAMLUtils.createAssertion(message);
+        
         Element assertionEl = assertion.toDOM(newDoc);
         root.appendChild(assertionEl);
         
-        Element docEl = payloadDoc.getDocumentElement();
         payloadDoc.removeChild(docEl);
         newDoc.adoptNode(docEl);
         root.appendChild(docEl);
@@ -72,33 +83,15 @@ public class SamlEnvelopedOutInterceptor
     }
 
 
-    public void setEnvelopeName(String envelopeName) {
-        this.envelopeName = envelopeName;
-    }
-
-
-    public String getEnvelopeName() {
-        return envelopeName;
-    }
-
-
-    public void setEnvelopeNamespace(String envelopeNamespace) {
-        this.envelopeNamespace = envelopeNamespace;
-    }
-
-
-    public String getEnvelopeNamespace() {
-        return envelopeNamespace;
+    public void setEnvelopeName(String expandedName) {
+        setEnvelopeQName(XMLUtils.convertStringToQName(expandedName, DEFAULT_ENV_PREFIX));
     }
-
-
-    public void setEnvelopePrefix(String envelopePrefix) {
-        this.envelopePrefix = envelopePrefix;
-    }
-
-
-    public String getEnvelopePrefix() {
-        return envelopePrefix;
+    
+    public void setEnvelopeQName(QName name) {
+        if (name.getPrefix().length() == 0) {
+            name = new QName(name.getNamespaceURI(), name.getLocalPart(), DEFAULT_ENV_PREFIX);
+        }
+        this.envelopeQName = name;
     }
     
 }

Modified: cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java?rev=1161199&r1=1161198&r2=1161199&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java (original)
+++ cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java Wed Aug 24 16:46:55 2011
@@ -28,8 +28,6 @@ import javax.xml.stream.XMLStreamReader;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
 
 import org.apache.cxf.helpers.DOMUtils;
 import org.apache.cxf.jaxrs.ext.RequestHandler;
@@ -56,6 +54,7 @@ public class XmlSigInHandler extends Abs
         this.removeSignature = remove;
     }
     
+    
     public Response handleRequest(Message message, ClassResourceInfo resourceClass) {
         
         Document doc = getDocument(message);
@@ -66,7 +65,7 @@ public class XmlSigInHandler extends Abs
         Element root = doc.getDocumentElement();
         Element sigElement = getSignatureElement(root);
         if (sigElement == null) {
-            throwFault("Enveloped Signature is not available", null);
+            throwFault("XML Signature is not available", null);
         }
         
         Crypto crypto = null;
@@ -84,8 +83,9 @@ public class XmlSigInHandler extends Abs
             throwFault("Crypto can not be loaded", ex);
         }
         boolean valid = false;
+        Reference ref = null;
         try {
-            XMLSignature signature = new XMLSignature(sigElement, "");
+            XMLSignature signature = new XMLSignature(sigElement, "");    
             // See also WSS4J SAMLUtil.getCredentialFromKeyInfo 
             KeyInfo keyInfo = signature.getKeyInfo();
             
@@ -99,7 +99,8 @@ public class XmlSigInHandler extends Abs
                 }
             }
             // is this call redundant given that signature.checkSignatureValue uses References ?
-            validateReference(root, signature);
+            ref = getReference(signature);
+            validateReference(root, ref);
             
             // validate trust 
             new TrustValidator().validateTrust(crypto, cert, keyInfo.getPublicKey());
@@ -112,7 +113,8 @@ public class XmlSigInHandler extends Abs
         }
         if (removeSignature) {
             if (!isEnveloping(root)) {
-                root.removeAttribute("ID");
+                Element signedEl = getSignedElement(root, ref);
+                signedEl.removeAttribute("ID");
                 root.removeChild(sigElement);
             } else {
                 Element actualBody = getActualBody(root);
@@ -138,24 +140,19 @@ public class XmlSigInHandler extends Abs
         if (objectNode == null) {
             throwFault("Object envelope is not available", null);
         }
-        NodeList list = objectNode.getChildNodes();
-        for (int i = 0; i < list.getLength(); i++) {
-            Node node = list.item(i);
-            if (node instanceof Element) {
-                objectNode.removeChild(node);
-                return (Element)node;
-            }
+        Element node = DOMUtils.getFirstElement(objectNode);
+        if (node == null) {
+            throwFault("No signed data is found", null);
         }
-        throwFault("No signed data is found", null);
-        return null;
+        return node;
        
     }
     
-    private Element getSignatureElement(Element root) {
-        if (isEnveloping(root)) {    
-            return root;
+    private Element getSignatureElement(Element sigParentElement) {
+        if (isEnveloping(sigParentElement)) {    
+            return sigParentElement;
         }
-        return getNode(root, Constants.SignatureSpecNS, "Signature", 0);
+        return DOMUtils.getFirstChildWithName(sigParentElement, Constants.SignatureSpecNS, "Signature");
     }
     
     protected boolean isEnveloping(Element root) {
@@ -163,18 +160,21 @@ public class XmlSigInHandler extends Abs
                 && "Signature".equals(root.getLocalName());
     }
     
-    
-    protected void validateReference(Element root, XMLSignature sig) {
-        Reference ref = null;
+    protected Reference getReference(XMLSignature sig) {
         int count = sig.getSignedInfo().getLength();
         if (count != 1) {
             throwFault("Multiple Signature Reference are not currently supported", null);
         }
         try {
-            ref = sig.getSignedInfo().item(0);
+            return sig.getSignedInfo().item(0);
         } catch (XMLSecurityException ex) {
             throwFault("Signature Reference is not available", ex);
         }
+        return null;
+    }
+    
+    protected void validateReference(Element root, Reference ref) {
+        boolean detached = false;
         if (!isEnveloping(root)) {
             String rootId = root.getAttribute("ID");
             String refId = ref.getURI();
@@ -182,17 +182,25 @@ public class XmlSigInHandler extends Abs
                 // or fragment must be expected ?
                 return;
             }
-            if (!refId.startsWith("#") || refId.length() <= 1 || !refId.substring(1).equals(rootId)) {
+            if (!refId.startsWith("#") || refId.length() <= 1) {
+                throwFault("Only local Signature References are supported", null);
+            }
+            
+            Element signedEl = getSignedElement(root, ref);
+            if (signedEl != null) {
+                detached = signedEl != root;
+            } else {
                 throwFault("Signature Reference ID is invalid", null);
             }
         }
+        
         Transforms transforms = null;
         try {
             transforms = ref.getTransforms();
         } catch (XMLSecurityException ex) {
             throwFault("Signature transforms can not be obtained", ex);
         }
-        if (!isEnveloping(root)) {
+        if (!isEnveloping(root) && !detached) {
             boolean isEnveloped = false;
             for (int i = 0; i < transforms.getLength(); i++) {
                 try {
@@ -210,4 +218,15 @@ public class XmlSigInHandler extends Abs
             }
         }
     }
+    
+    private Element getSignedElement(Element root, Reference ref) {
+        String rootId = root.getAttribute("ID");
+        String expectedID = ref.getURI().substring(1);
+        
+        if (!expectedID.equals(rootId)) {
+            return (Element)DOMUtils.findChildWithAtt(root, null, "ID", expectedID);
+        } else {
+            return root;
+        }
+    }
 }

Modified: cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigOutInterceptor.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigOutInterceptor.java?rev=1161199&r1=1161198&r2=1161199&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigOutInterceptor.java (original)
+++ cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigOutInterceptor.java Wed Aug 24 16:46:55 2011
@@ -23,6 +23,8 @@ import java.security.cert.X509Certificat
 import java.util.UUID;
 import java.util.logging.Logger;
 
+import javax.xml.namespace.QName;
+
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
@@ -30,6 +32,7 @@ import org.w3c.dom.Element;
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.common.util.StringUtils;
 import org.apache.cxf.helpers.DOMUtils;
+import org.apache.cxf.helpers.XMLUtils;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.rs.security.common.CryptoLoader;
 import org.apache.cxf.rs.security.common.SecurityUtils;
@@ -44,9 +47,14 @@ import org.opensaml.xml.signature.Signat
 
 
 public class XmlSigOutInterceptor extends AbstractXmlSecOutInterceptor {
+    public static final String DEFAULT_ENV_PREFIX = "env";
+    public static final QName DEFAULT_ENV_QNAME = 
+        new QName("http://org.apache.cxf/rs/env", "Envelope", DEFAULT_ENV_PREFIX);
+    
     private static final Logger LOG = 
         LogUtils.getL7dLogger(XmlSigOutInterceptor.class);
     
+    private QName envelopeQName;
     private boolean enveloping;
     private String defaultSigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
     private String digestAlgo = Constants.ALGO_ID_DIGEST_SHA1;
@@ -69,13 +77,17 @@ public class XmlSigOutInterceptor extend
     
     protected Document processDocument(Message message, Document doc) 
         throws Exception {
-        return createEnvelopedSignature(message, doc);
+        return createSignature(message, doc);
     }
     
     // enveloping & detached sigs will be supported too
-    private Document createEnvelopedSignature(Message message, Document doc) 
+    private Document createSignature(Message message, Document doc) 
         throws Exception {
         
+        if (enveloping && envelopeQName != null) {
+            throw new IllegalStateException("Enveloping XMLSignature can not have custom envelope names");
+        }
+        
         String userNameKey = SecurityConstants.SIGNATURE_USERNAME;
         
         CryptoLoader loader = new CryptoLoader();
@@ -119,6 +131,8 @@ public class XmlSigOutInterceptor extend
         XMLSignature sig = null;
         if (enveloping) {
             sig = prepareEnvelopingSignature(doc, id, referenceId, sigAlgo);
+        } else if (envelopeQName != null) {
+            sig = prepareDetachedSignature(doc, id, referenceId, sigAlgo);
         } else {
             sig = prepareEnvelopedSignature(doc, id, referenceId, sigAlgo);
         }
@@ -153,6 +167,31 @@ public class XmlSigOutInterceptor extend
         return sig;
     }
     
+    private XMLSignature prepareDetachedSignature(Document doc, 
+            String id, 
+            String referenceId,
+            String sigAlgo) throws Exception {
+        Element docEl = doc.getDocumentElement();
+        Document newDoc = DOMUtils.createDocument();
+        doc.removeChild(docEl);
+        newDoc.adoptNode(docEl);
+        docEl.setAttribute("ID", id);
+        
+        Element root = newDoc.createElementNS(envelopeQName.getNamespaceURI(), 
+                envelopeQName.getPrefix() + ":" + envelopeQName.getLocalPart());
+        root.appendChild(docEl);
+        newDoc.appendChild(root);
+        
+        XMLSignature sig = new XMLSignature(newDoc, "", sigAlgo);
+        root.appendChild(sig.getElement());
+        
+        Transforms transforms = new Transforms(newDoc);
+        transforms.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
+        
+        sig.addDocument(referenceId, transforms, digestAlgo);
+        return sig;
+    }
+    
     private XMLSignature prepareEnvelopedSignature(Document doc, 
             String id, 
             String referenceURI,
@@ -168,4 +207,15 @@ public class XmlSigOutInterceptor extend
         sig.addDocument(referenceURI, transforms, digestAlgo);
         return sig;
     }
+    
+    public void setEnvelopeName(String expandedName) {
+        setEnvelopeQName(XMLUtils.convertStringToQName(expandedName, DEFAULT_ENV_PREFIX));
+    }
+    
+    public void setEnvelopeQName(QName name) {
+        if (name.getPrefix().length() == 0) {
+            name = new QName(name.getNamespaceURI(), name.getLocalPart(), DEFAULT_ENV_PREFIX);
+        }
+        this.envelopeQName = name;
+    }
 }