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 2015/03/16 18:51:32 UTC
[4/4] cxf-fediz git commit: Properly validate SAML SSO responses
Properly validate SAML SSO responses
Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/799afc9a
Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/799afc9a
Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/799afc9a
Branch: refs/heads/master
Commit: 799afc9a2ad771eebc42533c6d4727642948ca42
Parents: a18bc7c
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Mon Mar 16 17:45:50 2015 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Mon Mar 16 17:45:50 2015 +0000
----------------------------------------------------------------------
.../TrustedIdpSAMLProtocolHandler.java | 222 +++++++------------
1 file changed, 80 insertions(+), 142 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/799afc9a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpSAMLProtocolHandler.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpSAMLProtocolHandler.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpSAMLProtocolHandler.java
index 18465f8..2cc03c3 100644
--- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpSAMLProtocolHandler.java
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpSAMLProtocolHandler.java
@@ -19,7 +19,6 @@
package org.apache.cxf.fediz.service.idp.protocols;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -29,6 +28,8 @@ import java.net.URL;
import java.net.URLEncoder;
import java.security.PrivateKey;
import java.security.Signature;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.zip.DataFormatException;
@@ -38,10 +39,12 @@ import javax.ws.rs.core.UriBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+
import org.apache.cxf.common.util.Base64Exception;
import org.apache.cxf.common.util.Base64Utility;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.fediz.core.FederationConstants;
+import org.apache.cxf.fediz.core.exception.ProcessingException;
import org.apache.cxf.fediz.core.util.CertsUtils;
import org.apache.cxf.fediz.service.idp.domain.Idp;
import org.apache.cxf.fediz.service.idp.domain.TrustedIdp;
@@ -52,15 +55,18 @@ import org.apache.cxf.jaxrs.utils.ExceptionUtils;
import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
import org.apache.cxf.rs.security.saml.sso.AuthnRequestBuilder;
import org.apache.cxf.rs.security.saml.sso.DefaultAuthnRequestBuilder;
+import org.apache.cxf.rs.security.saml.sso.SAMLProtocolResponseValidator;
+import org.apache.cxf.rs.security.saml.sso.SAMLSSOResponseValidator;
import org.apache.cxf.rs.security.saml.sso.SSOConstants;
+import org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse;
import org.apache.cxf.staxutils.StaxUtils;
import org.apache.cxf.ws.security.tokenstore.SecurityToken;
+import org.apache.wss4j.common.crypto.CertificateStore;
import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.Merlin;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.saml.OpenSAMLUtil;
import org.apache.wss4j.common.util.DOM2Writer;
-import org.apache.wss4j.common.util.XMLUtils;
-import org.apache.wss4j.dom.WSConstants;
import org.apache.xml.security.stax.impl.util.IDGenerator;
import org.apache.xml.security.utils.Base64;
import org.opensaml.saml2.core.AuthnRequest;
@@ -147,37 +153,29 @@ public class TrustedIdpSAMLProtocolHandler implements TrustedIdpProtocolHandler
public SecurityToken mapSignInResponse(RequestContext context, Idp idp, TrustedIdp trustedIdp) {
try {
- String relayState = (String) WebUtils.getAttributeFromFlowScope(context,
- SSOConstants.RELAY_STATE);
+ //String relayState = (String) WebUtils.getAttributeFromFlowScope(context,
+ // SSOConstants.RELAY_STATE);
// TODO Validate RelayState
- System.out.println("RS: " + relayState);
String encodedSAMLResponse = (String) WebUtils.getAttributeFromFlowScope(context,
SSOConstants.SAML_RESPONSE);
// Read the response + convert to an OpenSAML Response Object
- Element responseElement = readSAMLResponse(false, encodedSAMLResponse);
- // org.opensaml.saml2.core.Response samlResponse = convertToResponse(responseElement);
-
+ org.opensaml.saml2.core.Response samlResponse = readSAMLResponse(encodedSAMLResponse);
+
+ Crypto crypto = getCrypto(trustedIdp.getCertificate());
+ validateSamlResponseProtocol(samlResponse, crypto);
// Validate the Response
- /*
- * TODOvalidateSamlResponseProtocol(samlResponse);
SSOValidatorResponse validatorResponse =
- validateSamlSSOResponse(false, samlResponse, requestState);
+ validateSamlSSOResponse(samlResponse, idp, trustedIdp, context);
- String assertion = validatorResponse.getAssertion();
- SamlAssertionWrapper wrapper = new SamlAssertionWrapper(assertion);
- */
- Element assertionElement =
- XMLUtils.getDirectChildElement(responseElement, "Assertion", WSConstants.SAML2_NS);
// Create new Security token with new id.
// Parameters for freshness computation are copied from original IDP_TOKEN
String id = IDGenerator.generateID("_");
- SecurityToken idpToken = new SecurityToken(id);
- // new SecurityToken(id, new Date(), validatorResponse.getSessionNotOnOrAfter());
- // TODO new Date() above incorrect
+ SecurityToken idpToken =
+ new SecurityToken(id, validatorResponse.getCreated(), validatorResponse.getSessionNotOnOrAfter());
- idpToken.setToken(assertionElement);
+ idpToken.setToken(validatorResponse.getAssertionElement());
// LOG.info("[IDP_TOKEN={}] for user '{}' created from [RP_TOKEN={}] issued by home realm [{}/{}]",
// id, wfResp.getUsername(), wfResp.getUniqueTokenId(), whr, wfResp.getIssuer());
//.debug("Created date={}", wfResp.getTokenCreated());
@@ -213,7 +211,7 @@ public class TrustedIdpSAMLProtocolHandler implements TrustedIdpProtocolHandler
Idp config,
UriBuilder ub
) throws Exception {
- Crypto crypto = CertsUtils.createCrypto(config.getCertificate());
+ Crypto crypto = getCrypto(config.getCertificate());
if (crypto == null) {
LOG.error("No crypto instance of properties file configured for signature");
throw new IllegalStateException("Invalid IdP configuration");
@@ -261,44 +259,57 @@ public class TrustedIdpSAMLProtocolHandler implements TrustedIdpProtocolHandler
ub.queryParam(SSOConstants.SIGNATURE, URLEncoder.encode(encodedSignature, "UTF-8"));
}
-
- private Element readSAMLResponse(
- boolean postBinding, String samlResponse
- ) {
+ private Crypto getCrypto(String certificate) throws ProcessingException {
+ if (certificate == null) {
+ return null;
+ }
+
+ // First see if it's a certificate file
+ InputStream is = null;
+ try {
+ is = Merlin.loadInputStream(Thread.currentThread().getContextClassLoader(), certificate);
+
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ X509Certificate cert = (X509Certificate) certFactory.generateCertificate(is);
+ return new CertificateStore(new X509Certificate[]{cert});
+ } catch (WSSecurityException ex) {
+ LOG.error("Failed to load keystore " + certificate, ex);
+ throw new RuntimeException("Failed to load keystore " + certificate);
+ } catch (IOException ex) {
+ LOG.error("Failed to read keystore", ex);
+ throw new RuntimeException("Failed to read keystore");
+ } catch (CertificateException ex) {
+ // This is ok as it could be a WSS4J properties file
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Do nothing
+ }
+ }
+ }
+
+ // Maybe it's a WSS4J properties file...
+ return CertsUtils.createCrypto(certificate);
+ }
+
+ private org.opensaml.saml2.core.Response readSAMLResponse(String samlResponse) {
if (StringUtils.isEmpty(samlResponse)) {
throw ExceptionUtils.toBadRequestException(null, null);
}
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 ExceptionUtils.toBadRequestException(null, null);
- }
- }
- */
+
InputStream tokenStream = null;
- // (isSupportBase64Encoding()) { TODO
try {
byte[] deflatedToken = Base64Utility.decode(samlResponseDecoded);
- tokenStream = !postBinding //&& isSupportDeflateEncoding()
- ? new DeflateEncoderDecoder().inflateToken(deflatedToken)
- : new ByteArrayInputStream(deflatedToken);
+ tokenStream = new DeflateEncoderDecoder().inflateToken(deflatedToken);
} catch (Base64Exception ex) {
throw ExceptionUtils.toBadRequestException(ex, null);
} catch (DataFormatException ex) {
throw ExceptionUtils.toBadRequestException(ex, null);
}
- /*} else { TODO
- try {
- tokenStream = new ByteArrayInputStream(samlResponseDecoded.getBytes("UTF-8"));
- } catch (UnsupportedEncodingException ex) {
- throw ExceptionUtils.toBadRequestException(ex, null);
- }
- }*/
Document responseDoc = null;
try {
@@ -306,17 +317,12 @@ public class TrustedIdpSAMLProtocolHandler implements TrustedIdpProtocolHandler
} catch (Exception ex) {
throw new WebApplicationException(400);
}
-
+
LOG.debug("Received response: " + DOM2Writer.nodeToString(responseDoc.getDocumentElement()));
- return responseDoc.getDocumentElement();
-
- }
-
- protected org.opensaml.saml2.core.Response convertToResponse(Element samlResponseElement) {
XMLObject responseObject = null;
try {
- responseObject = OpenSAMLUtil.fromDom(samlResponseElement);
+ responseObject = OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
} catch (WSSecurityException ex) {
throw ExceptionUtils.toBadRequestException(ex, null);
}
@@ -324,122 +330,54 @@ public class TrustedIdpSAMLProtocolHandler implements TrustedIdpProtocolHandler
throw ExceptionUtils.toBadRequestException(null, null);
}
return (org.opensaml.saml2.core.Response)responseObject;
- }
+ }
+
/**
* Validate the received SAML Response as per the protocol
- protected void validateSamlResponseProtocol(
- org.opensaml.saml2.core.Response samlResponse
+ */
+ private void validateSamlResponseProtocol(
+ org.opensaml.saml2.core.Response samlResponse, Crypto crypto
) {
try {
SAMLProtocolResponseValidator protocolValidator = new SAMLProtocolResponseValidator();
- protocolValidator.setKeyInfoMustBeAvailable(true); // TODO
- protocolValidator.validateSamlResponse(samlResponse, getSignatureCrypto(), null);
+ protocolValidator.setKeyInfoMustBeAvailable(true);
+ protocolValidator.validateSamlResponse(samlResponse, crypto, null);
} catch (WSSecurityException ex) {
LOG.debug(ex.getMessage(), ex);
+ ex.printStackTrace();
throw ExceptionUtils.toBadRequestException(null, null);
}
}
- */
+
/**
* Validate the received SAML Response as per the Web SSO profile
- protected SSOValidatorResponse validateSamlSSOResponse(
- boolean postBinding,
+ */
+ private SSOValidatorResponse validateSamlSSOResponse(
org.opensaml.saml2.core.Response samlResponse,
Idp idp,
- TrustedIdp trustedIdp
+ TrustedIdp trustedIdp,
+ RequestContext requestContext
) {
try {
SAMLSSOResponseValidator ssoResponseValidator = new SAMLSSOResponseValidator();
- ssoResponseValidator.setAssertionConsumerURL(idp.getIdpUrl());
+ ssoResponseValidator.setAssertionConsumerURL(idp.getIdpUrl().toString());
- // ssoResponseValidator.setClientAddress(client_ip);
+ HttpServletRequest servletRequest = WebUtils.getHttpServletRequest(requestContext);
+ ssoResponseValidator.setClientAddress(servletRequest.getRemoteAddr());
ssoResponseValidator.setIssuerIDP(trustedIdp.getUrl());
- // ssoResponseValidator.setRequestId(requestState.getSamlRequestId());
+ // TODO ssoResponseValidator.setRequestId(requestState.getSamlRequestId());
ssoResponseValidator.setSpIdentifier(idp.getRealm());
- ssoResponseValidator.setEnforceAssertionsSigned(true); // TODO
- // ssoResponseValidator.setEnforceKnownIssuer(enforceKnownIssuer);
+ ssoResponseValidator.setEnforceAssertionsSigned(true);
+ ssoResponseValidator.setEnforceKnownIssuer(true);
- return ssoResponseValidator.validateSamlResponse(samlResponse, postBinding);
+ return ssoResponseValidator.validateSamlResponse(samlResponse, false);
} catch (WSSecurityException ex) {
LOG.debug(ex.getMessage(), ex);
throw ExceptionUtils.toBadRequestException(ex, null);
}
}
- */
-
-/*
- private FedizContext getFedizContext(Idp idpConfig,
- TrustedIdp trustedIdpConfig) throws ProcessingException {
-
- ContextConfig config = new ContextConfig();
-
- config.setName("whatever");
-
- // Configure certificate store
- String certificate = trustedIdpConfig.getCertificate();
- boolean isCertificateLocation = !certificate.startsWith("-----BEGIN CERTIFICATE");
- if (isCertificateLocation) {
- CertificateStores certStores = new CertificateStores();
- TrustManagersType tm0 = new TrustManagersType();
- KeyStoreType ks0 = new KeyStoreType();
- ks0.setType("PEM");
- // ks0.setType("JKS");
- // ks0.setPassword("changeit");
- ks0.setFile(trustedIdpConfig.getCertificate());
- tm0.setKeyStore(ks0);
- certStores.getTrustManager().add(tm0);
- config.setCertificateStores(certStores);
- }
-
- // Configure trusted IDP
- TrustedIssuers trustedIssuers = new TrustedIssuers();
- TrustedIssuerType ti0 = new TrustedIssuerType();
- ti0.setCertificateValidation(ValidationType.PEER_TRUST);
- ti0.setName(trustedIdpConfig.getName());
- // ti0.setSubject(".*CN=www.sts.com.*");
- trustedIssuers.getIssuer().add(ti0);
- config.setTrustedIssuers(trustedIssuers);
-
- FederationProtocolType protocol = new FederationProtocolType();
- config.setProtocol(protocol);
-
- AudienceUris audienceUris = new AudienceUris();
- audienceUris.getAudienceItem().add(idpConfig.getRealm());
- config.setAudienceUris(audienceUris);
-
- FedizContext fedContext = new FedizContext(config);
- if (!isCertificateLocation) {
- CertificateStore cs = null;
-
- X509Certificate cert;
- try {
- cert = parseCertificate(trustedIdpConfig.getCertificate());
- } catch (Exception ex) {
- LOG.error("Failed to parse trusted certificate", ex);
- throw new ProcessingException("Failed to parse trusted certificate");
- }
- cs = new CertificateStore(Collections.singletonList(cert).toArray(new X509Certificate[0]));
-
- TrustManager tm = new TrustManager(cs);
- fedContext.getCertificateStores().add(tm);
- }
- fedContext.init();
- return fedContext;
- }
-
- private X509Certificate parseCertificate(String certificate)
- throws CertificateException, Base64DecodingException {
-
- //before decoding we need to get rod off the prefix and suffix
- byte [] decoded = Base64.decode(certificate.replaceAll("-----BEGIN CERTIFICATE-----", "").
- replaceAll("-----END CERTIFICATE-----", ""));
-
- return (X509Certificate)CertificateFactory.getInstance("X.509").
- generateCertificate(new ByteArrayInputStream(decoded));
- }
-*/
}