You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ws.apache.org by co...@apache.org on 2014/04/24 15:12:56 UTC

svn commit: r1589714 - in /webservices/wss4j/branches/1_6_x-fixes/src: main/java/org/apache/ws/security/ main/java/org/apache/ws/security/message/ main/java/org/apache/ws/security/processor/ test/java/org/apache/ws/security/saml/

Author: coheigea
Date: Thu Apr 24 13:12:56 2014
New Revision: 1589714

URL: http://svn.apache.org/r1589714
Log:
[WSS-497] - Support for SAML 2.0 EncryptedAssertion Element


Conflicts:
	src/main/java/org/apache/ws/security/message/WSSecSAMLToken.java
	src/test/java/org/apache/ws/security/saml/SamlTokenTest.java

Added:
    webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/processor/EncryptedAssertionProcessor.java
Modified:
    webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSConstants.java
    webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSSConfig.java
    webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSSecurityEngine.java
    webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/message/WSSecSAMLToken.java
    webservices/wss4j/branches/1_6_x-fixes/src/test/java/org/apache/ws/security/saml/SamlTokenTest.java

Modified: webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSConstants.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSConstants.java?rev=1589714&r1=1589713&r2=1589714&view=diff
==============================================================================
--- webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSConstants.java (original)
+++ webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSConstants.java Thu Apr 24 13:12:56 2014
@@ -178,6 +178,7 @@ public final class WSConstants {
     public static final String SALT_LN = "Salt";
     public static final String ITERATION_LN = "Iteration";
     public static final String ASSERTION_LN = "Assertion";
+    public static final String ENCRYPED_ASSERTION_LN = "EncryptedAssertion";
     public static final String PW_DIGEST = "PasswordDigest";
     public static final String PW_TEXT = "PasswordText";
     public static final String PW_NONE = "PasswordNone";

Modified: webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSSConfig.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSSConfig.java?rev=1589714&r1=1589713&r2=1589714&view=diff
==============================================================================
--- webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSSConfig.java (original)
+++ webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSSConfig.java Thu Apr 24 13:12:56 2014
@@ -119,6 +119,10 @@ public class WSSConfig {
                 org.apache.ws.security.processor.SAMLTokenProcessor.class
             );
             tmp.put(
+                WSSecurityEngine.ENCRYPTED_ASSERTION,
+                org.apache.ws.security.processor.EncryptedAssertionProcessor.class
+            );
+            tmp.put(
                 WSSecurityEngine.ENCRYPTED_KEY,
                 org.apache.ws.security.processor.EncryptedKeyProcessor.class
             );

Modified: webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSSecurityEngine.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSSecurityEngine.java?rev=1589714&r1=1589713&r2=1589714&view=diff
==============================================================================
--- webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSSecurityEngine.java (original)
+++ webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/WSSecurityEngine.java Thu Apr 24 13:12:56 2014
@@ -107,6 +107,12 @@ public class WSSecurityEngine {
      */
     public static final QName SAML2_TOKEN = 
         new QName(WSConstants.SAML2_NS, WSConstants.ASSERTION_LN);
+    
+    /**
+     * <code>saml:EncryptedAssertion</code> as defined by SAML v2.0 specification
+     */
+    public static final QName ENCRYPTED_ASSERTION = 
+        new QName(WSConstants.SAML2_NS, WSConstants.ENCRYPED_ASSERTION_LN);
 
     /**
      * <code>wsc:DerivedKeyToken</code> as defined by WS-SecureConversation specification
@@ -395,7 +401,9 @@ public class WSSecurityEngine {
                 if (p != null) {
                     List<WSSecurityEngineResult> results = 
                         p.handleToken((Element) node, requestData, wsDocInfo);
-                    returnResults.addAll(0, results);
+                    if (!results.isEmpty()) {
+                        returnResults.addAll(0, results);
+                    }
                 } else {
                     if (doDebug) {
                         log.debug(

Modified: webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/message/WSSecSAMLToken.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/message/WSSecSAMLToken.java?rev=1589714&r1=1589713&r2=1589714&view=diff
==============================================================================
--- webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/message/WSSecSAMLToken.java (original)
+++ webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/message/WSSecSAMLToken.java Thu Apr 24 13:12:56 2014
@@ -41,6 +41,8 @@ public class WSSecSAMLToken extends WSSe
     private Document document = null;
     
     private AssertionWrapper saml = null;
+    
+    private Element samlElement;
 
     public WSSecSAMLToken() {
         super();
@@ -79,13 +81,26 @@ public class WSSecSAMLToken extends WSSe
      */
     public void prependToHeader(WSSecHeader secHeader) {
         try {
-            Element element = (Element) saml.toDOM(document);
-            WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), element);
+            Element element = getElement();
+            if (element != null) {
+                WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), element);
+            }
         } catch (WSSecurityException ex) {
             throw new RuntimeException(ex.toString(), ex);
         }
     }
     
+    public Element getElement() throws WSSecurityException {
+        if (samlElement != null) {
+            return samlElement;
+        }
+        if (saml == null) {
+            return null;
+        }
+        samlElement = saml.toDOM(document);
+        return samlElement;
+    }
+    
     /**
      * Get the id generated during <code>prepare()</code>.
      * 

Added: webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/processor/EncryptedAssertionProcessor.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/processor/EncryptedAssertionProcessor.java?rev=1589714&view=auto
==============================================================================
--- webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/processor/EncryptedAssertionProcessor.java (added)
+++ webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/processor/EncryptedAssertionProcessor.java Thu Apr 24 13:12:56 2014
@@ -0,0 +1,83 @@
+/**
+ * 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.processor;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.xml.namespace.QName;
+
+import org.apache.ws.security.WSConstants;
+import org.apache.ws.security.WSDocInfo;
+import org.apache.ws.security.WSSecurityEngineResult;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.handler.RequestData;
+import org.apache.ws.security.util.WSSecurityUtil;
+import org.w3c.dom.Element;
+
+/**
+ * This will process incoming <code>saml2:EncryptedAssertion</code> elements. EncryptedKey
+ * children are not supported, only an EncryptedData structure.
+ */
+public class EncryptedAssertionProcessor implements Processor {
+    
+    private static final org.slf4j.Logger LOG = 
+        org.slf4j.LoggerFactory.getLogger(EncryptedAssertionProcessor.class);
+    
+    public List<WSSecurityEngineResult> handleToken(
+        Element elem,
+        RequestData request,
+        WSDocInfo wsDocInfo
+    ) throws WSSecurityException {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Found EncryptedAssertion element");
+        }
+        
+        Element encryptedDataElement =
+            WSSecurityUtil.getDirectChildElement(elem, WSConstants.ENC_DATA_LN, WSConstants.ENC_NS);
+        if (encryptedDataElement == null) {
+            // Maybe it has already been decrypted...
+            return Collections.emptyList();
+        }
+        
+        // Type must be "Element" if specified
+        String typeStr = encryptedDataElement.getAttribute("Type");
+        if (typeStr != null && !(WSConstants.ENC_NS + "Element").equals(typeStr)) {
+            throw new WSSecurityException(
+                WSSecurityException.INVALID_SECURITY, "badElement", 
+                new Object[] {"Element", typeStr}
+            );
+        }
+        
+        // Now hand it off to another processor (EncryptedDataProcessor)
+        QName el = 
+            new QName(encryptedDataElement.getNamespaceURI(), encryptedDataElement.getLocalName());
+        Processor proc = request.getWssConfig().getProcessor(el);
+        if (proc != null) {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Processing decrypted element with: " + proc.getClass().getName());
+            }
+            return proc.handleToken(encryptedDataElement, request, wsDocInfo);
+        }
+        
+        return Collections.emptyList();
+    }
+    
+}

Modified: webservices/wss4j/branches/1_6_x-fixes/src/test/java/org/apache/ws/security/saml/SamlTokenTest.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_6_x-fixes/src/test/java/org/apache/ws/security/saml/SamlTokenTest.java?rev=1589714&r1=1589713&r2=1589714&view=diff
==============================================================================
--- webservices/wss4j/branches/1_6_x-fixes/src/test/java/org/apache/ws/security/saml/SamlTokenTest.java (original)
+++ webservices/wss4j/branches/1_6_x-fixes/src/test/java/org/apache/ws/security/saml/SamlTokenTest.java Thu Apr 24 13:12:56 2014
@@ -19,6 +19,15 @@
 
 package org.apache.ws.security.saml;
 
+import java.security.Key;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
 import org.apache.ws.security.WSConstants;
 import org.apache.ws.security.WSSConfig;
 import org.apache.ws.security.WSSecurityEngine;
@@ -26,32 +35,39 @@ import org.apache.ws.security.WSSecurity
 import org.apache.ws.security.WSSecurityException;
 import org.apache.ws.security.common.CustomHandler;
 import org.apache.ws.security.common.CustomSamlAssertionValidator;
+import org.apache.ws.security.common.KeystoreCallbackHandler;
 import org.apache.ws.security.common.SAML1CallbackHandler;
 import org.apache.ws.security.common.SAML2CallbackHandler;
 import org.apache.ws.security.common.SAMLElementCallbackHandler;
 import org.apache.ws.security.common.SOAPUtil;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoFactory;
+import org.apache.ws.security.components.crypto.CryptoType;
 import org.apache.ws.security.handler.RequestData;
 import org.apache.ws.security.handler.WSHandlerConstants;
 import org.apache.ws.security.message.WSSecHeader;
 import org.apache.ws.security.message.WSSecSAMLToken;
+import org.apache.ws.security.message.token.SecurityTokenReference;
 import org.apache.ws.security.saml.ext.AssertionWrapper;
 import org.apache.ws.security.saml.ext.SAMLParms;
 import org.apache.ws.security.saml.ext.bean.SubjectConfirmationDataBean;
 import org.apache.ws.security.saml.ext.builder.SAML1Constants;
 import org.apache.ws.security.util.WSSecurityUtil;
+import org.apache.ws.security.util.XMLUtils;
+import org.apache.xml.security.encryption.EncryptedData;
+import org.apache.xml.security.encryption.EncryptedKey;
+import org.apache.xml.security.encryption.XMLCipher;
+import org.apache.xml.security.keys.KeyInfo;
 import org.joda.time.DateTime;
-import org.opensaml.Configuration;
 import org.opensaml.common.SAMLObjectBuilder;
 import org.opensaml.saml2.core.AttributeValue;
 import org.opensaml.saml2.core.Conditions;
+import org.opensaml.xml.Configuration;
 import org.opensaml.xml.XMLObjectBuilder;
 import org.opensaml.xml.XMLObjectBuilderFactory;
 import org.opensaml.xml.schema.XSAny;
 import org.w3c.dom.Document;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Properties;
+import org.w3c.dom.Element;
 
 /**
  * Test-case for sending and processing an unsigned (sender vouches) SAML Assertion.
@@ -881,6 +897,118 @@ public class SamlTokenTest extends org.j
     }
     
     /**
+     * Test that creates, sends and processes an unsigned SAML 2 authentication assertion, which
+     * is encrypted in a saml2:EncryptedAssertion Element in the security header
+     */
+    @org.junit.Test
+    public void testSAML2EncryptedAssertion() throws Exception {
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+        callbackHandler.setIssuer("www.example.com");
+        
+        SAMLParms samlParms = new SAMLParms();
+        samlParms.setCallbackHandler(callbackHandler);
+        AssertionWrapper assertion = new AssertionWrapper(samlParms);
+        
+        WSSecSAMLToken wsSign = new WSSecSAMLToken();
+
+        Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        
+        wsSign.prepare(doc, assertion);
+        
+        // Get the Element + add it to the security header as an EncryptedAssertion
+        Element assertionElement = wsSign.getElement();
+        Element encryptedAssertionElement = 
+            doc.createElementNS(WSConstants.SAML2_NS, WSConstants.ENCRYPED_ASSERTION_LN);
+        encryptedAssertionElement.appendChild(assertionElement);
+        secHeader.getSecurityHeader().appendChild(encryptedAssertionElement);
+        
+        // Encrypt the Assertion
+        KeyGenerator keygen = KeyGenerator.getInstance("AES");
+        keygen.init(128);
+        SecretKey secretKey = keygen.generateKey();
+        Crypto crypto = CryptoFactory.getInstance("wss40.properties");
+        CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
+        cryptoType.setAlias("wss40");
+        X509Certificate[] certs = crypto.getX509Certificates(cryptoType);
+        assertTrue(certs != null && certs.length > 0 && certs[0] != null);
+        
+        encryptElement(doc, assertionElement, WSConstants.AES_128, secretKey,
+                WSConstants.KEYTRANSPORT_RSAOEP, certs[0], false);
+        
+        if (LOG.isDebugEnabled()) {
+            String outputString = 
+                XMLUtils.PrettyDocumentToString(doc);
+            LOG.debug(outputString);
+        }
+        
+        List<WSSecurityEngineResult> results = 
+            secEngine.processSecurityHeader(doc, null, new KeystoreCallbackHandler(), crypto);
+        
+        WSSecurityEngineResult actionResult =
+            WSSecurityUtil.fetchActionResult(results, WSConstants.ST_UNSIGNED);
+        AssertionWrapper receivedSamlAssertion =
+            (AssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
+        assertTrue(receivedSamlAssertion != null);
+        assertTrue(receivedSamlAssertion.getElement() != null);
+        assertTrue("Assertion".equals(receivedSamlAssertion.getElement().getLocalName()));
+        
+        actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.ENCR);
+        assertTrue(actionResult != null);
+    }
+    
+    private void encryptElement(
+        Document document,
+        Element elementToEncrypt,
+        String algorithm,
+        Key encryptingKey,
+        String keyTransportAlgorithm,
+        X509Certificate wrappingCert,
+        boolean content
+    ) throws Exception {
+        XMLCipher cipher = XMLCipher.getInstance(algorithm);
+        cipher.init(XMLCipher.ENCRYPT_MODE, encryptingKey);
+
+        if (wrappingCert != null) {
+            XMLCipher newCipher = XMLCipher.getInstance(keyTransportAlgorithm);
+            newCipher.init(XMLCipher.WRAP_MODE, wrappingCert.getPublicKey());
+
+            EncryptedKey encryptedKey = newCipher.encryptKey(document, encryptingKey);
+            // Create a KeyInfo for the EncryptedKey
+            KeyInfo encryptedKeyKeyInfo = encryptedKey.getKeyInfo();
+            if (encryptedKeyKeyInfo == null) {
+                encryptedKeyKeyInfo = new KeyInfo(document);
+                encryptedKeyKeyInfo.getElement().setAttributeNS(
+                    "http://www.w3.org/2000/xmlns/", "xmlns:dsig", "http://www.w3.org/2000/09/xmldsig#"
+                );
+                encryptedKey.setKeyInfo(encryptedKeyKeyInfo);
+            }
+            
+            SecurityTokenReference securityTokenReference = new SecurityTokenReference(document);
+            securityTokenReference.addWSSENamespace();
+            securityTokenReference.setKeyIdentifierSKI(wrappingCert, null);
+            encryptedKeyKeyInfo.addUnknownElement(securityTokenReference.getElement());
+
+            // Create a KeyInfo for the EncryptedData
+            EncryptedData builder = cipher.getEncryptedData();
+            KeyInfo builderKeyInfo = builder.getKeyInfo();
+            if (builderKeyInfo == null) {
+                builderKeyInfo = new KeyInfo(document);
+                builderKeyInfo.getElement().setAttributeNS(
+                    "http://www.w3.org/2000/xmlns/", "xmlns:dsig", "http://www.w3.org/2000/09/xmldsig#"
+                );
+                builder.setKeyInfo(builderKeyInfo);
+            }
+
+            builderKeyInfo.add(encryptedKey);
+        }
+
+        cipher.doFinal(document, elementToEncrypt, content);
+    }
+
+    /**
      * Verifies the soap envelope
      * <p/>
      * 
@@ -889,11 +1017,11 @@ public class SamlTokenTest extends org.j
      */
     private List<WSSecurityEngineResult> verify(Document doc) throws Exception {
         List<WSSecurityEngineResult> results = 
-            secEngine.processSecurityHeader(doc, null, null, null);
+                secEngine.processSecurityHeader(doc, null, null, null);
         String outputString = 
             org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
         assertTrue(outputString.indexOf("counter_port_type") > 0 ? true : false);
         return results;
     }
-    
+
 }