Author: coheigea
Date: Tue May 15 16:05:40 2012
New Revision: 1338769
URL: http://svn.apache.org/viewvc?rev=1338769&view=rev
Log:
Adding signature verification support to the RACS
Modified:
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/SAMLSSOResponseValidator.java
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=1338769&r1=1338768&r2=1338769&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
Tue May 15 16:05:40 2012
@@ -19,11 +19,14 @@
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.Properties;
import java.util.ResourceBundle;
import java.util.UUID;
import java.util.logging.Logger;
@@ -42,6 +45,7 @@ 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;
@@ -54,6 +58,8 @@ import org.apache.cxf.rs.security.saml.D
import org.apache.cxf.rs.security.saml.sso.state.RequestState;
import org.apache.cxf.rs.security.saml.sso.state.ResponseState;
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;
@@ -66,6 +72,8 @@ public class RequestAssertionConsumerSer
private boolean supportDeflateEncoding = true;
private boolean supportBase64Encoding = true;
+ private Crypto signatureCrypto;
+ private String signaturePropertiesFile;
@Context
private MessageContext jaxrsContext;
@@ -84,34 +92,30 @@ 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,
@FormParam(SSOConstants.RELAY_STATE)
String relayState) {
- if (relayState == null) {
- reportError("MISSING_RELAY_STATE");
- throw new WebApplicationException(400);
- }
- if (relayState.getBytes().length < 0 || relayState.getBytes().length >
80) {
- reportError("INVALID_RELAY_STATE");
- throw new WebApplicationException(400);
- }
- RequestState requestState =
getStateProvider().removeRequestState(relayState);
- if (requestState == null) {
- reportError("MISSING_REQUEST_STATE");
- throw new WebApplicationException(400);
- }
- if (isStateExpired(requestState.getCreatedAt())) {
- reportError("EXPIRED_REQUEST_STATE");
- throw new WebApplicationException(400);
- }
-
+ RequestState requestState = processRelayState(relayState);
URI targetURI = getTargetURI(requestState.getTargetAddress());
org.opensaml.saml2.core.Response samlResponse =
- readSAMLResponse(encodedSamlResponse);
+ readSAMLResponse(true, encodedSamlResponse);
- validateSamlResponse(samlResponse, requestState);
+ validateSamlResponse(true, samlResponse, requestState);
// Set the security context
String securityContextKey = UUID.randomUUID().toString();
@@ -131,22 +135,70 @@ public class RequestAssertionConsumerSer
}
@GET
- public Response getSamlResponse(@QueryParam(SSOConstants.SAML_RESPONSE)
String samlResponse,
+ public Response getSamlResponse(@QueryParam(SSOConstants.SAML_RESPONSE)
String encodedSamlResponse,
@QueryParam(SSOConstants.RELAY_STATE)
String relayState) {
- return processSamlResponse(relayState, samlResponse);
+ RequestState requestState = processRelayState(relayState);
+ URI targetURI = getTargetURI(requestState.getTargetAddress());
+
+ org.opensaml.saml2.core.Response samlResponse =
+ readSAMLResponse(false, encodedSamlResponse);
+
+ validateSamlResponse(false, samlResponse, requestState);
+
+ // Set the security context
+ String securityContextKey = UUID.randomUUID().toString();
+
+ long currentTime = System.currentTimeMillis();
+ ResponseState responseState = new ResponseState(relayState,
currentTime);
+ getStateProvider().setResponseState(securityContextKey, responseState);
+
+ String contextCookie =
createCookie(SSOConstants.SECURITY_CONTEXT_TOKEN,
+ securityContextKey,
+ requestState.getWebAppContext(),
+ requestState.getWebAppDomain());
+
+ // Finally, redirect to the service provider endpoint
+ return Response.seeOther(targetURI).header("Set-Cookie",
contextCookie).build();
}
- private org.opensaml.saml2.core.Response readSAMLResponse(String
samlResponse) {
+ private RequestState processRelayState(String relayState) {
+ if (relayState == null) {
+ reportError("MISSING_RELAY_STATE");
+ throw new WebApplicationException(400);
+ }
+ if (relayState.getBytes().length < 0 || relayState.getBytes().length >
80) {
+ reportError("INVALID_RELAY_STATE");
+ throw new WebApplicationException(400);
+ }
+ RequestState requestState =
getStateProvider().removeRequestState(relayState);
+ if (requestState == null) {
+ reportError("MISSING_REQUEST_STATE");
+ throw new WebApplicationException(400);
+ }
+ if (isStateExpired(requestState.getCreatedAt())) {
+ reportError("EXPIRED_REQUEST_STATE");
+ throw new WebApplicationException(400);
+ }
+ return requestState;
+ }
+
+ private org.opensaml.saml2.core.Response readSAMLResponse(
+ boolean postBinding,
+ String samlResponse
+ ) {
if (StringUtils.isEmpty(samlResponse)) {
reportError("MISSING_SAML_RESPONSE");
throw new WebApplicationException(400);
}
- String samlResponseDecoded = null;
- try {
- samlResponseDecoded = URLDecoder.decode(samlResponse, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new WebApplicationException(400);
+ String samlResponseDecoded = samlResponse;
+ // URL Decoding only applies for the re-direct binding
+ if (!postBinding) {
+ try {
+ samlResponseDecoded = URLDecoder.decode(samlResponse, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new WebApplicationException(400);
+ }
}
InputStream tokenStream = null;
if (isSupportBase64Encoding()) {
@@ -174,6 +226,7 @@ public class RequestAssertionConsumerSer
} catch (Exception ex) {
throw new WebApplicationException(400);
}
+
XMLObject responseObject = null;
try {
responseObject =
OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
@@ -186,12 +239,14 @@ public class RequestAssertionConsumerSer
return (org.opensaml.saml2.core.Response)responseObject;
}
- protected void validateSamlResponse(org.opensaml.saml2.core.Response
samlResponse,
- RequestState requestState) {
+ protected void validateSamlResponse(
+ boolean postBinding,
+ org.opensaml.saml2.core.Response samlResponse,
+ RequestState requestState
+ ) {
try {
SAMLProtocolResponseValidator protocolValidator = new
SAMLProtocolResponseValidator();
- // TODO Configure Crypto & CallbackHandler object here to validate
signatures
- protocolValidator.validateSamlResponse(samlResponse, null, null);
+ protocolValidator.validateSamlResponse(samlResponse,
getSignatureCrypto(), null);
SAMLSSOResponseValidator ssoResponseValidator = new
SAMLSSOResponseValidator();
ssoResponseValidator.setAssertionConsumerURL((String)jaxrsContext.get(Message.REQUEST_URL));
@@ -227,4 +282,56 @@ 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/SAMLSSOResponseValidator.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java?rev=1338769&r1=1338768&r2=1338769&view=diff
==============================================================================
---
cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
(original)
+++
cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
Tue May 15 16:05:40 2012
@@ -68,9 +68,9 @@ public class SAMLSSOResponseValidator {
}
// The Response must contain a Destination that matches the
assertionConsumerURL if it is
- // signed and received over the POST Binding.
+ // signed and received over the redirect Binding.
String destination = samlResponse.getDestination();
- if (postBinding && samlResponse.isSigned()
+ if (!postBinding && samlResponse.isSigned()
&& (destination == null ||
!destination.equals(assertionConsumerURL))) {
LOG.fine("The Response must contain a destination that matches the
assertion consumer URL");
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidSAMLsecurity");