You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2012/05/17 16:12:07 UTC

svn commit: r1339609 - /cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/

Author: coheigea
Date: Thu May 17 14:12:06 2012
New Revision: 1339609

URL: http://svn.apache.org/viewvc?rev=1339609&view=rev
Log:
Added support for signing AuthnRequests using the redirect binding

Modified:
    cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractSSOSpHandler.java
    cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java
    cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java
    cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SSOConstants.java
    cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlRedirectBindingFilter.java

Modified: cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractSSOSpHandler.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractSSOSpHandler.java?rev=1339609&r1=1339608&r2=1339609&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractSSOSpHandler.java (original)
+++ cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractSSOSpHandler.java Thu May 17 14:12:06 2012
@@ -18,20 +18,70 @@
  */
 package org.apache.cxf.rs.security.saml.sso;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
 import java.util.Date;
+import java.util.Properties;
+import java.util.logging.Logger;
 
+import javax.security.auth.callback.CallbackHandler;
+
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.jaxrs.utils.HttpUtils;
 import org.apache.cxf.rs.security.saml.sso.state.SPStateManager;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoFactory;
 import org.apache.ws.security.saml.ext.OpenSAMLUtil;
 
 public class AbstractSSOSpHandler {
+    private static final Logger LOG = 
+            LogUtils.getL7dLogger(AbstractSSOSpHandler.class);
+    
     private SPStateManager stateProvider;
     private long stateTimeToLive = SSOConstants.DEFAULT_STATE_TIME;
+    private Crypto signatureCrypto;
+    private String signaturePropertiesFile;
+    private CallbackHandler callbackHandler;
+    private String callbackHandlerClass;
     
     static {
         OpenSAMLUtil.initSamlEngine();
     }
     
+    public void setSignatureCrypto(Crypto crypto) {
+        signatureCrypto = crypto;
+    }
+    
+    /**
+     * Set the String corresponding to the signature Properties class
+     * @param signaturePropertiesFile the String corresponding to the signature properties file
+     */
+    public void setSignaturePropertiesFile(String signaturePropertiesFile) {
+        this.signaturePropertiesFile = signaturePropertiesFile;
+        LOG.fine("Setting signature properties: " + signaturePropertiesFile);
+    }
+    
+    /**
+     * Set the CallbackHandler object. 
+     * @param callbackHandler the CallbackHandler object. 
+     */
+    public void setCallbackHandler(CallbackHandler callbackHandler) {
+        this.callbackHandler = callbackHandler;
+        LOG.fine("Setting callbackHandler: " + callbackHandler);
+    }
+    
+    /**
+     * Set the String corresponding to the CallbackHandler class. 
+     * @param callbackHandlerClass the String corresponding to the CallbackHandler class. 
+     */
+    public void setCallbackHandlerClass(String callbackHandlerClass) {
+        this.callbackHandlerClass = callbackHandlerClass;
+        LOG.fine("Setting callbackHandlerClass: " + callbackHandlerClass);
+    }
+    
     //TODO: support attaching a signature to the cookie value
     protected String createCookie(String name, 
                                   String value, 
@@ -93,4 +143,83 @@ public class AbstractSSOSpHandler {
     public long getStateTimeToLive() {
         return stateTimeToLive;
     }
+    
+    protected static Properties getProps(Object o) {
+        Properties properties = null;
+        if (o instanceof Properties) {
+            properties = (Properties)o;
+        } else if (o instanceof String) {
+            URL url = null;
+            try {
+                url = ClassLoaderUtils.getResource((String)o, AbstractSSOSpHandler.class);
+                if (url == null) {
+                    url = new URL((String)o);
+                }
+                if (url != null) {
+                    properties = new Properties();
+                    InputStream ins = url.openStream();
+                    properties.load(ins);
+                    ins.close();
+                }
+            } catch (IOException e) {
+                LOG.fine(e.getMessage());
+                properties = null;
+            }
+        } else if (o instanceof URL) {
+            properties = new Properties();
+            try {
+                InputStream ins = ((URL)o).openStream();
+                properties.load(ins);
+                ins.close();
+            } catch (IOException e) {
+                LOG.fine(e.getMessage());
+                properties = null;
+            }            
+        }
+        return properties;
+    }
+    
+    protected Crypto getSignatureCrypto() {
+        if (signatureCrypto == null && signaturePropertiesFile != null) {
+            Properties sigProperties = getProps(signaturePropertiesFile);
+            if (sigProperties == null) {
+                LOG.fine("Cannot load signature properties using: " + signaturePropertiesFile);
+                return null;
+            }
+            try {
+                signatureCrypto = CryptoFactory.getInstance(sigProperties);
+            } catch (WSSecurityException ex) {
+                LOG.fine("Error in loading the signature Crypto object: " + ex.getMessage());
+                return null;
+            }
+        }
+        return signatureCrypto;
+    }
+    
+    protected CallbackHandler getCallbackHandler() {
+        if (callbackHandler == null && callbackHandlerClass != null) {
+            callbackHandler = getCallbackHandler(callbackHandlerClass);
+            if (callbackHandler == null) {
+                LOG.fine("Cannot load CallbackHandler using: " + callbackHandlerClass);
+                return null;
+            }
+        }
+        return callbackHandler;
+    }
+    
+    private CallbackHandler getCallbackHandler(Object o) {
+        CallbackHandler handler = null;
+        if (o instanceof CallbackHandler) {
+            handler = (CallbackHandler)o;
+        } else if (o instanceof String) {
+            try {
+                handler = 
+                    (CallbackHandler)ClassLoaderUtils.loadClass((String)o, this.getClass()).newInstance();
+            } catch (Exception e) {
+                LOG.fine(e.getMessage());
+                handler = null;
+            }
+        }
+        return handler;
+    }
 }

Modified: cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java?rev=1339609&r1=1339608&r2=1339609&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java (original)
+++ cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java Thu May 17 14:12:06 2012
@@ -63,6 +63,16 @@ public abstract class AbstractServicePro
     private String assertionConsumerServiceAddress;
     private String webAppDomain;
     private AuthnRequestBuilder authnRequestBuilder = new DefaultAuthnRequestBuilder();
+    private boolean signRequest;
+    private String signatureUsername;
+    
+    public void setSignRequest(boolean signRequest) {
+        this.signRequest = signRequest;
+    }
+    
+    public boolean isSignRequest() {
+        return signRequest;
+    }
     
     public void setAuthnRequestBuilder(AuthnRequestBuilder authnRequestBuilder) {
         this.authnRequestBuilder = authnRequestBuilder;
@@ -84,7 +94,24 @@ public abstract class AbstractServicePro
     public String getIdpServiceAddress() {
         return idpServiceAddress;
     }
-
+    
+    /**
+     * Set the username/alias to use to sign any request
+     * @param signatureUsername the username/alias to use to sign any request
+     */
+    public void setSignatureUsername(String signatureUsername) {
+        this.signatureUsername = signatureUsername;
+        LOG.fine("Setting signatureUsername: " + signatureUsername);
+    }
+    
+    /**
+     * Get the username/alias to use to sign any request
+     * @return the username/alias to use to sign any request
+     */
+    public String getSignatureUsername() {
+        return signatureUsername;
+    }
+    
     private String getIssuerId(Message m) {
         if (issuerId == null) {
             return new UriInfoImpl(m).getBaseUri().toString();

Modified: cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java?rev=1339609&r1=1339608&r2=1339609&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java (original)
+++ cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java Thu May 17 14:12:06 2012
@@ -19,15 +19,12 @@
 package org.apache.cxf.rs.security.saml.sso;
 
 import java.io.ByteArrayInputStream;
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
-import java.net.URL;
 import java.net.URLDecoder;
 import java.util.Date;
-import java.util.Properties;
 import java.util.ResourceBundle;
 import java.util.UUID;
 import java.util.logging.Logger;
@@ -47,7 +44,6 @@ import javax.ws.rs.core.Response;
 
 import org.w3c.dom.Document;
 
-import org.apache.cxf.common.classloader.ClassLoaderUtils;
 import org.apache.cxf.common.i18n.BundleUtils;
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.common.util.Base64Exception;
@@ -61,8 +57,6 @@ import org.apache.cxf.rs.security.saml.s
 import org.apache.cxf.rs.security.saml.sso.state.ResponseState;
 import org.apache.cxf.transport.http.AbstractHTTPDestination;
 import org.apache.ws.security.WSSecurityException;
-import org.apache.ws.security.components.crypto.Crypto;
-import org.apache.ws.security.components.crypto.CryptoFactory;
 import org.apache.ws.security.saml.ext.OpenSAMLUtil;
 import org.opensaml.xml.XMLObject;
 
@@ -75,8 +69,6 @@ public class RequestAssertionConsumerSer
     
     private boolean supportDeflateEncoding = true;
     private boolean supportBase64Encoding = true;
-    private Crypto signatureCrypto;
-    private String signaturePropertiesFile;
     private boolean enforceAssertionsSigned = true;
 
     @Context 
@@ -103,19 +95,6 @@ public class RequestAssertionConsumerSer
         return supportBase64Encoding;
     }
     
-    public void setSignatureCrypto(Crypto crypto) {
-        signatureCrypto = crypto;
-    }
-    
-    /**
-     * Set the String corresponding to the signature Properties class
-     * @param signaturePropertiesFile the String corresponding to the signature properties file
-     */
-    public void setSignaturePropertiesFile(String signaturePropertiesFile) {
-        this.signaturePropertiesFile = signaturePropertiesFile;
-        LOG.fine("Setting signature properties: " + signaturePropertiesFile);
-    }
-    
     @POST
     @Produces(MediaType.APPLICATION_FORM_URLENCODED)
     public Response processSamlResponse(@FormParam(SSOConstants.SAML_RESPONSE) String encodedSamlResponse,
@@ -330,56 +309,4 @@ public class RequestAssertionConsumerSer
         LOG.warning(errorMsg.toString());
     }
     
-    private Crypto getSignatureCrypto() {
-        if (signatureCrypto == null && signaturePropertiesFile != null) {
-            Properties sigProperties = getProps(signaturePropertiesFile);
-            if (sigProperties == null) {
-                LOG.fine("Cannot load signature properties using: " + signaturePropertiesFile);
-                return null;
-            }
-            try {
-                signatureCrypto = CryptoFactory.getInstance(sigProperties);
-            } catch (WSSecurityException ex) {
-                LOG.fine("Error in loading the signature Crypto object: " + ex.getMessage());
-                return null;
-            }
-        }
-        return signatureCrypto;
-    }
-    
-    private static Properties getProps(Object o) {
-        Properties properties = null;
-        if (o instanceof Properties) {
-            properties = (Properties)o;
-        } else if (o instanceof String) {
-            URL url = null;
-            try {
-                url = ClassLoaderUtils.getResource((String)o, RequestAssertionConsumerService.class);
-                if (url == null) {
-                    url = new URL((String)o);
-                }
-                if (url != null) {
-                    properties = new Properties();
-                    InputStream ins = url.openStream();
-                    properties.load(ins);
-                    ins.close();
-                }
-            } catch (IOException e) {
-                LOG.fine(e.getMessage());
-                properties = null;
-            }
-        } else if (o instanceof URL) {
-            properties = new Properties();
-            try {
-                InputStream ins = ((URL)o).openStream();
-                properties.load(ins);
-                ins.close();
-            } catch (IOException e) {
-                LOG.fine(e.getMessage());
-                properties = null;
-            }            
-        }
-        return properties;
-    }
-    
 }

Modified: cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SSOConstants.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SSOConstants.java?rev=1339609&r1=1339608&r2=1339609&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SSOConstants.java (original)
+++ cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SSOConstants.java Thu May 17 14:12:06 2012
@@ -18,13 +18,20 @@
  */
 package org.apache.cxf.rs.security.saml.sso;
 
+import org.apache.ws.security.WSConstants;
+
 public final class SSOConstants {
     public static final String SAML_REQUEST = "SAMLRequest";
     public static final String SAML_RESPONSE = "SAMLResponse"; 
     public static final String RELAY_STATE = "RelayState";
+    public static final String SIG_ALG = "SigAlg";
+    public static final String SIGNATURE = "Signature";
     public static final String SECURITY_CONTEXT_TOKEN = "org.apache.cxf.websso.context";
     public static final long DEFAULT_STATE_TIME = 2L * 60L * 1000L;
     
+    public static final String RSA_SHA1 = WSConstants.RSA_SHA1;
+    public static final String DSA_SHA1 = WSConstants.DSA;
+    
     
     private SSOConstants() {
     }

Modified: cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlRedirectBindingFilter.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlRedirectBindingFilter.java?rev=1339609&r1=1339608&r2=1339609&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlRedirectBindingFilter.java (original)
+++ cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlRedirectBindingFilter.java Thu May 17 14:12:06 2012
@@ -18,6 +18,12 @@
  */
 package org.apache.cxf.rs.security.saml.sso;
 
+import java.net.URLEncoder;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.cert.X509Certificate;
+
+import javax.security.auth.callback.CallbackHandler;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
@@ -25,6 +31,11 @@ import javax.ws.rs.core.UriBuilder;
 
 import org.apache.cxf.jaxrs.model.ClassResourceInfo;
 import org.apache.cxf.message.Message;
+import org.apache.ws.security.WSPasswordCallback;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoType;
+import org.apache.ws.security.util.Base64;
 
 public class SamlRedirectBindingFilter extends AbstractServiceProviderFilter {
     
@@ -36,7 +47,10 @@ public class SamlRedirectBindingFilter e
                 SamlRequestInfo info = createSamlRequestInfo(m);
                 UriBuilder ub = UriBuilder.fromUri(getIdpServiceAddress());
                 ub.queryParam(SSOConstants.SAML_REQUEST, info.getEncodedSamlRequest());
-                ub.queryParam(SSOConstants.RELAY_STATE, info.getRelayState());    
+                ub.queryParam(SSOConstants.RELAY_STATE, info.getRelayState());
+                if (isSignRequest()) {
+                    signRequest(ub);
+                }
                 
                 String contextCookie = createCookie(SSOConstants.RELAY_STATE,
                                                     info.getRelayState(),
@@ -49,10 +63,75 @@ public class SamlRedirectBindingFilter e
                                .header("Set-Cookie", contextCookie)
                                .build();
             } catch (Exception ex) {
+                ex.printStackTrace();
                 throw new WebApplicationException(ex);
             }
         }
     }
     
+    /**
+     * Sign a request according to the redirect binding spec for Web SSO
+     */
+    private void signRequest(UriBuilder ub) throws Exception {
+        Crypto crypto = getSignatureCrypto();
+        if (crypto == null) {
+            LOG.fine("No crypto instance of properties file configured for signature");
+            throw new WebApplicationException();
+        }
+        String signatureUser = getSignatureUsername();
+        if (signatureUser == null) {
+            LOG.fine("No user configured for signature");
+            throw new WebApplicationException();
+        }
+        CallbackHandler callbackHandler = getCallbackHandler();
+        if (callbackHandler == null) {
+            LOG.fine("No CallbackHandler configured to supply a password for signature");
+            throw new WebApplicationException();
+        }
+        
+        CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
+        cryptoType.setAlias(signatureUser);
+        X509Certificate[] issuerCerts = crypto.getX509Certificates(cryptoType);
+        if (issuerCerts == null) {
+            throw new WSSecurityException(
+                "No issuer certs were found to sign the request using name: " + signatureUser
+            );
+        }
+
+        String sigAlgo = SSOConstants.RSA_SHA1;
+        String pubKeyAlgo = issuerCerts[0].getPublicKey().getAlgorithm();
+        String jceSigAlgo = "SHA1withRSA";
+        LOG.fine("automatic sig algo detection: " + pubKeyAlgo);
+        if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
+            sigAlgo = SSOConstants.DSA_SHA1;
+            jceSigAlgo = "SHA1withDSA";
+        }
+        LOG.fine("Using Signature algorithm " + sigAlgo);
+        ub.queryParam(SSOConstants.SIG_ALG, URLEncoder.encode(sigAlgo, "UTF-8"));
+        
+        // Get the password
+        WSPasswordCallback[] cb = {new WSPasswordCallback(signatureUser, WSPasswordCallback.SIGNATURE)};
+        callbackHandler.handle(cb);
+        String password = cb[0].getPassword();
+        
+        // Get the private key
+        PrivateKey privateKey = null;
+        try {
+            privateKey = crypto.getPrivateKey(signatureUser, password);
+        } catch (Exception ex) {
+            throw new WSSecurityException(ex.getMessage(), ex);
+        }
+        
+        // Sign the request
+        Signature signature = Signature.getInstance(jceSigAlgo);
+        signature.initSign(privateKey);
+        signature.update(ub.toString().getBytes("UTF-8"));
+        byte[] signBytes = signature.sign();
+        
+        String encodedSignature = Base64.encode(signBytes);
+        
+        ub.queryParam(SSOConstants.SIGNATURE, URLEncoder.encode(encodedSignature, "UTF-8"));
+        
+    }
     
 }