cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cohei...@apache.org
Subject [1/5] cxf-fediz git commit: Refactor SAMLAuthnParser
Date Thu, 03 Nov 2016 17:30:01 GMT
Repository: cxf-fediz
Updated Branches:
  refs/heads/master b8aa7ea52 -> 6fef44bb1


Refactor SAMLAuthnParser


Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/d2830809
Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/d2830809
Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/d2830809

Branch: refs/heads/master
Commit: d2830809c15b066c341cee422a4b54f8ae06be2e
Parents: b8aa7ea
Author: Colm O hEigeartaigh <coheigea@apache.org>
Authored: Thu Nov 3 12:01:43 2016 +0000
Committer: Colm O hEigeartaigh <coheigea@apache.org>
Committed: Thu Nov 3 12:01:43 2016 +0000

----------------------------------------------------------------------
 .../cxf/fediz/service/idp/IdpConstants.java     |  10 +-
 .../idp/beans/samlsso/AuthnRequestParser.java   | 288 +++++++++++++++++--
 .../beans/samlsso/AuthnRequestValidator.java    | 269 -----------------
 .../idp/beans/samlsso/SamlResponseCreator.java  |  14 +-
 .../service/idp/samlsso/SAMLAuthnRequest.java   |  74 +++++
 .../WEB-INF/flows/saml-signin-request.xml       |  32 +--
 6 files changed, 355 insertions(+), 332 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/d2830809/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java
b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java
index 95a9fc4..d33bbc1 100644
--- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java
@@ -29,14 +29,16 @@ public final class IdpConstants {
     public static final String TRUSTED_IDP_CONTEXT = "trusted_idp_context";
     
     /**
-     * A key used to store a parsed SAMLRequest as an OpenSAML AuthnRequest Object
+     * A key used to store the home realm for the given request.
      */
-    public static final String SAML_AUTHN_REQUEST = "saml_authn_request";
+    public static final String HOME_REALM = "home_realm";
     
     /**
-     * A key used to store the home realm for the given request.
+     * The SAML Authn Request
      */
-    public static final String HOME_REALM = "home_realm";
+    public static final String SAML_AUTHN_REQUEST = "saml_authn_request";
+    
+    
     
     private IdpConstants() {
         // complete

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/d2830809/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
index b9e15f8..53feb73 100644
--- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
@@ -21,19 +21,48 @@ package org.apache.cxf.fediz.service.idp.beans.samlsso;
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
 
 import org.w3c.dom.Document;
 
 import org.apache.cxf.common.util.Base64Utility;
 import org.apache.cxf.fediz.core.exception.ProcessingException;
+import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE;
+import org.apache.cxf.fediz.core.util.CertsUtils;
 import org.apache.cxf.fediz.service.idp.IdpConstants;
+import org.apache.cxf.fediz.service.idp.domain.Application;
 import org.apache.cxf.fediz.service.idp.domain.Idp;
+import org.apache.cxf.fediz.service.idp.samlsso.SAMLAuthnRequest;
 import org.apache.cxf.fediz.service.idp.util.WebUtils;
 import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
+import org.apache.cxf.rs.security.saml.sso.SSOConstants;
 import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.wss4j.common.crypto.CertificateStore;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.ext.WSSecurityException;
 import org.apache.wss4j.common.saml.OpenSAMLUtil;
+import org.apache.wss4j.common.saml.SAMLKeyInfo;
+import org.apache.wss4j.common.saml.SAMLUtil;
 import org.apache.wss4j.common.util.DOM2Writer;
+import org.apache.wss4j.dom.WSDocInfo;
+import org.apache.wss4j.dom.engine.WSSConfig;
+import org.apache.wss4j.dom.handler.RequestData;
+import org.apache.wss4j.dom.saml.WSSSAMLKeyInfoProcessor;
+import org.apache.wss4j.dom.validate.Credential;
+import org.apache.wss4j.dom.validate.SignatureTrustValidator;
+import org.apache.wss4j.dom.validate.Validator;
+import org.apache.xml.security.utils.Base64;
 import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;
+import org.opensaml.security.credential.BasicCredential;
+import org.opensaml.security.x509.BasicX509Credential;
+import org.opensaml.xmlsec.signature.KeyInfo;
+import org.opensaml.xmlsec.signature.Signature;
+import org.opensaml.xmlsec.signature.support.SignatureException;
+import org.opensaml.xmlsec.signature.support.SignatureValidator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
@@ -47,33 +76,42 @@ public class AuthnRequestParser {
 
     private static final Logger LOG = LoggerFactory.getLogger(AuthnRequestParser.class);
     private boolean supportDeflateEncoding;
+    private boolean requireSignature = true;
 
-    public void parseSAMLRequest(RequestContext context, Idp idp, String samlRequest) throws
ProcessingException {
+    public void parseSAMLRequest(RequestContext context, Idp idp, String samlRequest,
+                                 String signature, String relayState) throws ProcessingException
{
         LOG.debug("Received SAML Request: {}", samlRequest);
         
-        AuthnRequest parsedRequest = null;
         if (samlRequest == null) {
-            WebUtils.removeAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
+            WebUtils.removeAttribute(context, IdpConstants.SAML_AUTHN_REQUEST);
+            throw new ProcessingException(TYPE.BAD_REQUEST);
         } else {
-            parsedRequest = 
-                (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
-            if (parsedRequest == null) {
-                try {
-                    parsedRequest = extractRequest(context, samlRequest);
-                    WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST,
parsedRequest);
-                    LOG.debug("SAML Request with id '{}' successfully parsed", parsedRequest.getID());
-                } catch (Exception ex) {
-                    LOG.warn("Error parsing request: {}", ex.getMessage());
-                }
+            AuthnRequest parsedRequest = null;
+            try {
+                parsedRequest = extractRequest(context, samlRequest);
+            } catch (Exception ex) {
+                LOG.warn("Error parsing request: {}", ex.getMessage());
+                throw new ProcessingException(TYPE.BAD_REQUEST);
             }
+            
+            // Store various attributes from the AuthnRequest
+            SAMLAuthnRequest authnRequest = new SAMLAuthnRequest(parsedRequest);
+            WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST, authnRequest);
+            
+            validateSignature(context, parsedRequest, idp, signature, relayState, 
+                              samlRequest, authnRequest.getIssuer());
+            validateRequest(parsedRequest);
+            
+            LOG.debug("SAML Request with id '{}' successfully parsed", parsedRequest.getID());
         }
     }
     
     public String retrieveRealm(RequestContext context) {
-        AuthnRequest authnRequest = 
-            (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
-        if (authnRequest != null && authnRequest.getIssuer() != null) {
-            String issuer = authnRequest.getIssuer().getValue();
+        SAMLAuthnRequest authnRequest = 
+            (SAMLAuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
+        
+        if (authnRequest != null) {
+            String issuer = authnRequest.getIssuer();
             LOG.debug("Parsed SAML AuthnRequest Issuer: {}", issuer);
             return issuer;
         }
@@ -83,11 +121,11 @@ public class AuthnRequestParser {
     }
     
     public String retrieveConsumerURL(RequestContext context) {
-        AuthnRequest authnRequest = 
-            (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
+        SAMLAuthnRequest authnRequest = 
+            (SAMLAuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
 
-        if (authnRequest != null && authnRequest.getAssertionConsumerServiceURL()
!= null) {
-            String consumerURL = authnRequest.getAssertionConsumerServiceURL();
+        if (authnRequest != null && authnRequest.getConsumerServiceURL() != null)
{
+            String consumerURL = authnRequest.getConsumerServiceURL();
             LOG.debug("Parsed SAML AuthnRequest Consumer URL: {}", consumerURL);
             return consumerURL;
         }
@@ -97,11 +135,11 @@ public class AuthnRequestParser {
     }
     
     public String retrieveRequestId(RequestContext context) {
-        AuthnRequest authnRequest = 
-            (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
+        SAMLAuthnRequest authnRequest = 
+            (SAMLAuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
 
-        if (authnRequest != null && authnRequest.getID() != null) {
-            String id = authnRequest.getID();
+        if (authnRequest != null && authnRequest.getRequestId() != null) {
+            String id = authnRequest.getRequestId();
             LOG.debug("Parsed SAML AuthnRequest Id: {}", id);
             return id;
         }
@@ -111,11 +149,11 @@ public class AuthnRequestParser {
     }
     
     public String retrieveRequestIssuer(RequestContext context) {
-        AuthnRequest authnRequest = 
-            (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
+        SAMLAuthnRequest authnRequest = 
+            (SAMLAuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
 
         if (authnRequest != null && authnRequest.getIssuer() != null) {
-            String issuer = authnRequest.getIssuer().getValue();
+            String issuer = authnRequest.getIssuer();
             LOG.debug("Parsed SAML AuthnRequest Issuer: {}", issuer);
             return issuer;
         }
@@ -125,10 +163,10 @@ public class AuthnRequestParser {
     }
     
     public boolean isForceAuthentication(RequestContext context) {
-        AuthnRequest authnRequest = 
-            (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
+        SAMLAuthnRequest authnRequest = 
+            (SAMLAuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
         if (authnRequest != null) {
-            return authnRequest.isForceAuthn().booleanValue();
+            return authnRequest.isForceAuthn();
         }
         
         LOG.debug("No AuthnRequest available to be parsed");
@@ -159,4 +197,192 @@ public class AuthnRequestParser {
     public void setSupportDeflateEncoding(boolean supportDeflateEncoding) {
         this.supportDeflateEncoding = supportDeflateEncoding;
     }
+    
+    private void validateRequest(AuthnRequest parsedRequest) throws ProcessingException {
+        if (parsedRequest.getIssuer() == null) {
+            LOG.debug("No Issuer is present in the AuthnRequest");
+            throw new ProcessingException(TYPE.BAD_REQUEST);
+        }
+        
+        String format = parsedRequest.getIssuer().getFormat();
+        if (format != null
+            && !"urn:oasis:names:tc:SAML:2.0:nameid-format:entity".equals(format))
{
+            LOG.debug("An invalid Format attribute was received: {}", format);
+            throw new ProcessingException(TYPE.BAD_REQUEST);
+        }
+        
+        // No SubjectConfirmation Elements are allowed
+        if (parsedRequest.getSubject() != null 
+            && parsedRequest.getSubject().getSubjectConfirmations() != null
+            && !parsedRequest.getSubject().getSubjectConfirmations().isEmpty()) {
+            LOG.debug("An invalid SubjectConfirmation Element was received");
+            throw new ProcessingException(TYPE.BAD_REQUEST);
+        }
+    }
+    
+    private void validateSignature(RequestContext context, AuthnRequest authnRequest, Idp
idp, 
+                                   String signature, String relayState, String samlRequest,

+                                   String realm) throws ProcessingException {
+        try {
+            if (authnRequest.isSigned()) {
+                // Check destination
+                checkDestination(context, authnRequest);
+                
+                // Check signature
+                X509Certificate validatingCert = getValidatingCertificate(idp, realm);
+                Crypto issuerCrypto = 
+                    new CertificateStore(Collections.singletonList(validatingCert).toArray(new
X509Certificate[0]));
+                validateAuthnRequestSignature(authnRequest.getSignature(), issuerCrypto);
+            } else if (signature != null) {
+                // Check destination
+                checkDestination(context, authnRequest);
+                
+                // Check signature
+                X509Certificate validatingCert = getValidatingCertificate(idp, realm);
+                
+                java.security.Signature sig = java.security.Signature.getInstance("SHA1withRSA");
+                sig.initVerify(validatingCert);
+                
+                // Recreate request to sign
+                String requestToSign = SSOConstants.SAML_REQUEST + "=" + URLEncoder.encode(samlRequest,
"UTF-8")
+                     + "&" + SSOConstants.RELAY_STATE + "=" + relayState + "&" +
SSOConstants.SIG_ALG 
+                     + "=" + URLEncoder.encode(SSOConstants.RSA_SHA1, StandardCharsets.UTF_8.name());
+                
+                sig.update(requestToSign.getBytes(StandardCharsets.UTF_8));
+                
+                if (!sig.verify(Base64.decode(signature))) {
+                    LOG.debug("Signature validation failed");
+                    throw new ProcessingException(TYPE.BAD_REQUEST);
+                }
+            } else if (requireSignature) {
+                LOG.debug("No signature is present, therefore the request is rejected");
+                throw new ProcessingException(TYPE.BAD_REQUEST);
+            } else {
+                LOG.debug("No signature is present, but this is allowed by configuration");
+            }
+        } catch (Exception ex) {
+            LOG.debug("Error validating SAML Signature", ex);
+            throw new ProcessingException(TYPE.BAD_REQUEST);
+        }
+    }
+    
+    private X509Certificate getValidatingCertificate(Idp idp, String realm) 
+        throws Exception {
+        Application serviceConfig = idp.findApplication(realm);
+        if (serviceConfig == null || serviceConfig.getValidatingCertificate() == null) {
+            LOG.debug("No validating certificate found for realm {}", realm);
+            throw new ProcessingException(TYPE.ISSUER_NOT_TRUSTED);
+        }
+        
+        return CertsUtils.parseX509Certificate(serviceConfig.getValidatingCertificate());
+    }
+    
+    private void checkDestination(RequestContext context, AuthnRequest authnRequest) throws
ProcessingException {
+        // Check destination
+        String destination = authnRequest.getDestination();
+        LOG.debug("Validating destination: {}", destination);
+        
+        String localAddr = WebUtils.getHttpServletRequest(context).getRequestURL().toString();
+        if (destination == null || !localAddr.startsWith(destination)) {
+            LOG.debug("The destination {} does not match the local address {}", destination,
localAddr);
+            throw new ProcessingException(TYPE.BAD_REQUEST);
+        }
+    }
+    
+    /**
+     * Validate the AuthnRequest signature
+     */
+    private void validateAuthnRequestSignature(
+        Signature signature,
+        Crypto sigCrypto
+    ) throws WSSecurityException {
+        RequestData requestData = new RequestData();
+        requestData.setSigVerCrypto(sigCrypto);
+        WSSConfig wssConfig = WSSConfig.getNewInstance();
+        requestData.setWssConfig(wssConfig);
+        // requestData.setCallbackHandler(callbackHandler);
+
+        SAMLKeyInfo samlKeyInfo = null;
+
+        KeyInfo keyInfo = signature.getKeyInfo();
+        if (keyInfo != null) {
+            try {
+                Document doc = signature.getDOM().getOwnerDocument();
+                samlKeyInfo =
+                    SAMLUtil.getCredentialFromKeyInfo(
+                        keyInfo.getDOM(), new WSSSAMLKeyInfoProcessor(requestData, new WSDocInfo(doc)),
sigCrypto
+                    );
+            } catch (WSSecurityException ex) {
+                LOG.debug("Error in getting KeyInfo from SAML AuthnRequest: {}", ex.getMessage(),
ex);
+                throw ex;
+            }
+        }
+        
+        if (samlKeyInfo == null) {
+            LOG.debug("No KeyInfo supplied in the AuthnRequest signature");
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
+        }
+
+        // Validate Signature against profiles
+        validateSignatureAgainstProfiles(signature, samlKeyInfo);
+
+        // Now verify trust on the signature
+        Credential trustCredential = new Credential();
+        trustCredential.setPublicKey(samlKeyInfo.getPublicKey());
+        trustCredential.setCertificates(samlKeyInfo.getCerts());
+
+        try {
+            Validator signatureValidator = new SignatureTrustValidator();
+            signatureValidator.validate(trustCredential, requestData);
+        } catch (WSSecurityException e) {
+            LOG.debug("Error in validating signature on SAML AuthnRequest: {}", e.getMessage(),
e);
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
+        }
+    }
+
+    /**
+     * Validate a signature against the profiles
+     */
+    private void validateSignatureAgainstProfiles(
+        Signature signature,
+        SAMLKeyInfo samlKeyInfo
+    ) throws WSSecurityException {
+        // Validate Signature against profiles
+        SAMLSignatureProfileValidator validator = new SAMLSignatureProfileValidator();
+        try {
+            validator.validate(signature);
+        } catch (SignatureException ex) {
+            LOG.debug("Error in validating the SAML Signature: {}", ex.getMessage(), ex);
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
+        }
+
+        BasicCredential credential = null;
+        if (samlKeyInfo.getCerts() != null) {
+            credential = new BasicX509Credential(samlKeyInfo.getCerts()[0]);
+        } else if (samlKeyInfo.getPublicKey() != null) {
+            credential = new BasicCredential(samlKeyInfo.getPublicKey());
+        } else {
+            LOG.debug("Can't get X509Certificate or PublicKey to verify signature");
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
+        }
+        try {
+            SignatureValidator.validate(signature, credential);
+        } catch (SignatureException ex) {
+            LOG.debug("Error in validating the SAML Signature: {}", ex.getMessage(), ex);
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
+        }
+    }
+
+    public boolean isRequireSignature() {
+        return requireSignature;
+    }
+
+    /**
+     * Whether to require a signature or not on the AuthnRequest
+     * @param requireSignature
+     */
+    public void setRequireSignature(boolean requireSignature) {
+        this.requireSignature = requireSignature;
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/d2830809/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java
b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java
deleted file mode 100644
index cc87432..0000000
--- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/**
- * 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.cxf.fediz.service.idp.beans.samlsso;
-
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.security.cert.X509Certificate;
-import java.util.Collections;
-
-import org.w3c.dom.Document;
-
-import org.apache.cxf.fediz.core.exception.ProcessingException;
-import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE;
-import org.apache.cxf.fediz.core.util.CertsUtils;
-import org.apache.cxf.fediz.service.idp.IdpConstants;
-import org.apache.cxf.fediz.service.idp.domain.Application;
-import org.apache.cxf.fediz.service.idp.domain.Idp;
-import org.apache.cxf.fediz.service.idp.util.WebUtils;
-import org.apache.cxf.rs.security.saml.sso.SSOConstants;
-import org.apache.wss4j.common.crypto.CertificateStore;
-import org.apache.wss4j.common.crypto.Crypto;
-import org.apache.wss4j.common.ext.WSSecurityException;
-import org.apache.wss4j.common.saml.SAMLKeyInfo;
-import org.apache.wss4j.common.saml.SAMLUtil;
-import org.apache.wss4j.dom.WSDocInfo;
-import org.apache.wss4j.dom.engine.WSSConfig;
-import org.apache.wss4j.dom.handler.RequestData;
-import org.apache.wss4j.dom.saml.WSSSAMLKeyInfoProcessor;
-import org.apache.wss4j.dom.validate.Credential;
-import org.apache.wss4j.dom.validate.SignatureTrustValidator;
-import org.apache.wss4j.dom.validate.Validator;
-import org.apache.xml.security.utils.Base64;
-import org.opensaml.saml.saml2.core.AuthnRequest;
-import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;
-import org.opensaml.security.credential.BasicCredential;
-import org.opensaml.security.x509.BasicX509Credential;
-import org.opensaml.xmlsec.signature.KeyInfo;
-import org.opensaml.xmlsec.signature.Signature;
-import org.opensaml.xmlsec.signature.support.SignatureException;
-import org.opensaml.xmlsec.signature.support.SignatureValidator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-import org.springframework.webflow.execution.RequestContext;
-
-/**
- * Validate the received AuthnRequest
- */
-@Component
-public class AuthnRequestValidator {
-
-    private static final Logger LOG = LoggerFactory.getLogger(AuthnRequestValidator.class);
-    
-    private boolean requireSignature = true;
-
-    public void validateAuthnRequest(RequestContext context, Idp idp, String signature,
-                                     String relayState, String samlRequest, String realm)

-        throws Exception {
-        AuthnRequest authnRequest = 
-            (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
-        if (authnRequest == null) {
-            throw new ProcessingException(TYPE.BAD_REQUEST);
-        }
-        
-        validateSignature(context, authnRequest, idp, signature, relayState, samlRequest,
realm);
-        
-        if (authnRequest.getIssuer() == null) {
-            LOG.debug("No Issuer is present in the AuthnRequest");
-            throw new ProcessingException(TYPE.BAD_REQUEST);
-        }
-        
-        String format = authnRequest.getIssuer().getFormat();
-        if (format != null
-            && !"urn:oasis:names:tc:SAML:2.0:nameid-format:entity".equals(format))
{
-            LOG.debug("An invalid Format attribute was received: {}", format);
-            throw new ProcessingException(TYPE.BAD_REQUEST);
-        }
-        
-        // No SubjectConfirmation Elements are allowed
-        if (authnRequest.getSubject() != null 
-            && authnRequest.getSubject().getSubjectConfirmations() != null
-            && !authnRequest.getSubject().getSubjectConfirmations().isEmpty()) {
-            LOG.debug("An invalid SubjectConfirmation Element was received");
-            throw new ProcessingException(TYPE.BAD_REQUEST);
-        }
-    }
-    
-    private void validateSignature(RequestContext context, AuthnRequest authnRequest, Idp
idp, 
-                                   String signature, String relayState, String samlRequest,

-                                   String realm) throws ProcessingException {
-        try {
-            if (authnRequest.isSigned()) {
-                // Check destination
-                checkDestination(context, authnRequest);
-                
-                // Check signature
-                X509Certificate validatingCert = getValidatingCertificate(idp, realm);
-                Crypto issuerCrypto = 
-                    new CertificateStore(Collections.singletonList(validatingCert).toArray(new
X509Certificate[0]));
-                validateAuthnRequestSignature(authnRequest.getSignature(), issuerCrypto);
-            } else if (signature != null) {
-                // Check destination
-                checkDestination(context, authnRequest);
-                
-                // Check signature
-                X509Certificate validatingCert = getValidatingCertificate(idp, realm);
-                
-                java.security.Signature sig = java.security.Signature.getInstance("SHA1withRSA");
-                sig.initVerify(validatingCert);
-                
-                // Recreate request to sign
-                String requestToSign = SSOConstants.SAML_REQUEST + "=" + URLEncoder.encode(samlRequest,
"UTF-8")
-                     + "&" + SSOConstants.RELAY_STATE + "=" + relayState + "&" +
SSOConstants.SIG_ALG 
-                     + "=" + URLEncoder.encode(SSOConstants.RSA_SHA1, StandardCharsets.UTF_8.name());
-                
-                sig.update(requestToSign.getBytes(StandardCharsets.UTF_8));
-                
-                if (!sig.verify(Base64.decode(signature))) {
-                    LOG.debug("Signature validation failed");
-                    throw new ProcessingException(TYPE.BAD_REQUEST);
-                }
-            } else if (requireSignature) {
-                LOG.debug("No signature is present, therefore the request is rejected");
-                throw new ProcessingException(TYPE.BAD_REQUEST);
-            } else {
-                LOG.debug("No signature is present, but this is allowed by configuration");
-            }
-        } catch (Exception ex) {
-            LOG.debug("Error validating SAML Signature", ex);
-            throw new ProcessingException(TYPE.BAD_REQUEST);
-        }
-    }
-    
-    private X509Certificate getValidatingCertificate(Idp idp, String realm) 
-        throws Exception {
-        Application serviceConfig = idp.findApplication(realm);
-        if (serviceConfig == null || serviceConfig.getValidatingCertificate() == null) {
-            LOG.debug("No validating certificate found for realm {}", realm);
-            throw new ProcessingException(TYPE.ISSUER_NOT_TRUSTED);
-        }
-        
-        return CertsUtils.parseX509Certificate(serviceConfig.getValidatingCertificate());
-    }
-    
-    private void checkDestination(RequestContext context, AuthnRequest authnRequest) throws
ProcessingException {
-        // Check destination
-        String destination = authnRequest.getDestination();
-        LOG.debug("Validating destination: {}", destination);
-        
-        String localAddr = WebUtils.getHttpServletRequest(context).getRequestURL().toString();
-        if (destination == null || !localAddr.startsWith(destination)) {
-            LOG.debug("The destination {} does not match the local address {}", destination,
localAddr);
-            throw new ProcessingException(TYPE.BAD_REQUEST);
-        }
-    }
-    
-    /**
-     * Validate the AuthnRequest signature
-     */
-    private void validateAuthnRequestSignature(
-        Signature signature,
-        Crypto sigCrypto
-    ) throws WSSecurityException {
-        RequestData requestData = new RequestData();
-        requestData.setSigVerCrypto(sigCrypto);
-        WSSConfig wssConfig = WSSConfig.getNewInstance();
-        requestData.setWssConfig(wssConfig);
-        // requestData.setCallbackHandler(callbackHandler);
-
-        SAMLKeyInfo samlKeyInfo = null;
-
-        KeyInfo keyInfo = signature.getKeyInfo();
-        if (keyInfo != null) {
-            try {
-                Document doc = signature.getDOM().getOwnerDocument();
-                samlKeyInfo =
-                    SAMLUtil.getCredentialFromKeyInfo(
-                        keyInfo.getDOM(), new WSSSAMLKeyInfoProcessor(requestData, new WSDocInfo(doc)),
sigCrypto
-                    );
-            } catch (WSSecurityException ex) {
-                LOG.debug("Error in getting KeyInfo from SAML AuthnRequest: {}", ex.getMessage(),
ex);
-                throw ex;
-            }
-        }
-        
-        if (samlKeyInfo == null) {
-            LOG.debug("No KeyInfo supplied in the AuthnRequest signature");
-            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
-        }
-
-        // Validate Signature against profiles
-        validateSignatureAgainstProfiles(signature, samlKeyInfo);
-
-        // Now verify trust on the signature
-        Credential trustCredential = new Credential();
-        trustCredential.setPublicKey(samlKeyInfo.getPublicKey());
-        trustCredential.setCertificates(samlKeyInfo.getCerts());
-
-        try {
-            Validator signatureValidator = new SignatureTrustValidator();
-            signatureValidator.validate(trustCredential, requestData);
-        } catch (WSSecurityException e) {
-            LOG.debug("Error in validating signature on SAML AuthnRequest: {}", e.getMessage(),
e);
-            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
-        }
-    }
-
-    /**
-     * Validate a signature against the profiles
-     */
-    private void validateSignatureAgainstProfiles(
-        Signature signature,
-        SAMLKeyInfo samlKeyInfo
-    ) throws WSSecurityException {
-        // Validate Signature against profiles
-        SAMLSignatureProfileValidator validator = new SAMLSignatureProfileValidator();
-        try {
-            validator.validate(signature);
-        } catch (SignatureException ex) {
-            LOG.debug("Error in validating the SAML Signature: {}", ex.getMessage(), ex);
-            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
-        }
-
-        BasicCredential credential = null;
-        if (samlKeyInfo.getCerts() != null) {
-            credential = new BasicX509Credential(samlKeyInfo.getCerts()[0]);
-        } else if (samlKeyInfo.getPublicKey() != null) {
-            credential = new BasicCredential(samlKeyInfo.getPublicKey());
-        } else {
-            LOG.debug("Can't get X509Certificate or PublicKey to verify signature");
-            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
-        }
-        try {
-            SignatureValidator.validate(signature, credential);
-        } catch (SignatureException ex) {
-            LOG.debug("Error in validating the SAML Signature: {}", ex.getMessage(), ex);
-            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
-        }
-    }
-
-    public boolean isRequireSignature() {
-        return requireSignature;
-    }
-
-    /**
-     * Whether to require a signature or not on the AuthnRequest
-     * @param requireSignature
-     */
-    public void setRequireSignature(boolean requireSignature) {
-        this.requireSignature = requireSignature;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/d2830809/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
index 3bc36ea..742797d 100644
--- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
@@ -33,6 +33,7 @@ import org.apache.cxf.fediz.service.idp.IdpConstants;
 import org.apache.cxf.fediz.service.idp.domain.Idp;
 import org.apache.cxf.fediz.service.idp.samlsso.SAML2CallbackHandler;
 import org.apache.cxf.fediz.service.idp.samlsso.SAML2PResponseComponentBuilder;
+import org.apache.cxf.fediz.service.idp.samlsso.SAMLAuthnRequest;
 import org.apache.cxf.fediz.service.idp.util.WebUtils;
 import org.apache.cxf.helpers.DOMUtils;
 import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
@@ -48,7 +49,6 @@ import org.apache.wss4j.common.util.DOM2Writer;
 import org.apache.wss4j.dom.WSConstants;
 import org.joda.time.DateTime;
 import org.opensaml.saml.saml2.core.Assertion;
-import org.opensaml.saml.saml2.core.AuthnRequest;
 import org.opensaml.saml.saml2.core.NameID;
 import org.opensaml.saml.saml2.core.Response;
 import org.opensaml.saml.saml2.core.Status;
@@ -103,15 +103,13 @@ public class SamlResponseCreator {
         callbackHandler.setSubject(receivedToken.getSaml2().getSubject());
         
         // Test Subject against received Subject (if applicable)
-        AuthnRequest authnRequest = 
-            (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
-        if (authnRequest.getSubject() != null && authnRequest.getSubject().getNameID()
!= null
-            && receivedToken.getSaml2().getSubject().getNameID() != null) {
-            NameID receivedNameId = authnRequest.getSubject().getNameID();
+        SAMLAuthnRequest authnRequest = 
+            (SAMLAuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
+        if (authnRequest.getSubjectNameId() != null && receivedToken.getSaml2().getSubject().getNameID()
!= null) {
             NameID issuedNameId = receivedToken.getSaml2().getSubject().getNameID();
-            if (!receivedNameId.getValue().equals(issuedNameId.getValue())) {
+            if (!authnRequest.getSubjectNameId().equals(issuedNameId.getValue())) {
                 LOG.debug("Received NameID value of {} does not match issued value {}",
-                          receivedNameId.getValue(), issuedNameId.getValue());
+                          authnRequest.getSubjectNameId(), issuedNameId.getValue());
                 throw new ProcessingException(ProcessingException.TYPE.INVALID_REQUEST);
             }
         }

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/d2830809/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAuthnRequest.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAuthnRequest.java
b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAuthnRequest.java
new file mode 100644
index 0000000..c7ded4b
--- /dev/null
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAuthnRequest.java
@@ -0,0 +1,74 @@
+/**
+ * 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.cxf.fediz.service.idp.samlsso;
+
+import java.io.Serializable;
+
+import org.opensaml.saml.saml2.core.AuthnRequest;
+
+/**
+ * This class encapsulates a (parsed) SAML AuthnRequest Object. The OpenSAML AuthnRequest
Object is not
+ * serializable.
+ */
+public class SAMLAuthnRequest implements Serializable {
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 4353024755428346545L;
+    
+    private String issuer;
+    private String consumerServiceURL;
+    private String requestId;
+    private boolean forceAuthn;
+    private String subjectNameId;
+    
+    public SAMLAuthnRequest(AuthnRequest authnRequest) {
+        if (authnRequest.getIssuer() != null) {
+            issuer = authnRequest.getIssuer().getValue();
+        }
+        
+        consumerServiceURL = authnRequest.getAssertionConsumerServiceURL();
+        requestId = authnRequest.getID();
+        forceAuthn = authnRequest.isForceAuthn().booleanValue();
+        if (authnRequest.getSubject() != null && authnRequest.getSubject().getNameID()
!= null) {
+            subjectNameId = authnRequest.getSubject().getNameID().getValue();
+        }
+    }
+    
+    public String getIssuer() {
+        return issuer;
+    }
+    
+    public String getConsumerServiceURL() {
+        return consumerServiceURL;
+    }
+    
+    public String getRequestId() {
+        return requestId;
+    }
+    
+    public boolean isForceAuthn() {
+        return forceAuthn;
+    }
+    
+    public String getSubjectNameId() {
+        return subjectNameId;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/d2830809/services/idp/src/main/webapp/WEB-INF/flows/saml-signin-request.xml
----------------------------------------------------------------------
diff --git a/services/idp/src/main/webapp/WEB-INF/flows/saml-signin-request.xml b/services/idp/src/main/webapp/WEB-INF/flows/saml-signin-request.xml
index 93ffba7..6382a48 100644
--- a/services/idp/src/main/webapp/WEB-INF/flows/saml-signin-request.xml
+++ b/services/idp/src/main/webapp/WEB-INF/flows/saml-signin-request.xml
@@ -27,6 +27,14 @@
     <input name="SAMLRequest" />
     <input name="RelayState" />
     <input name="Signature" />
+    
+    <action-state id="parseAuthnRequest">
+        <evaluate expression="authnRequestParser.parseSAMLRequest(flowRequestContext,
flowScope.idpConfig,
+                                                              flowScope.SAMLRequest, flowScope.Signature,
+                                                              flowScope.RelayState)" />
+        <transition to="processHRDSExpression"/>
+        <transition on-exception="org.apache.cxf.fediz.core.exception.ProcessingException"
to="viewBadRequest" />
+    </action-state>
 
     <decision-state id="processHRDSExpression">
         <on-entry>
@@ -114,15 +122,11 @@
     </decision-state>
 
     <action-state id="checkTokenExpiry">
-        <on-entry>
-            <evaluate expression="authnRequestParser.parseSAMLRequest(flowRequestContext,
flowScope.idpConfig,
-                                                                  flowScope.SAMLRequest)"
/>
-        </on-entry>
         <evaluate
             expression="idpTokenExpiredAction.isTokenExpired(flowScope.home_realm, flowRequestContext)
                         or authnRequestParser.isForceAuthentication(flowRequestContext)"
/>
         <transition on="yes" to="redirectToLocalIDP" />
-        <transition on="no" to="parseAndValidateSAMLRequest">
+        <transition on="no" to="validateEndpointAddress">
             <set name="flowScope.idpToken" value="externalContext.sessionMap[flowScope.home_realm]"
/>
         </transition>
         <transition on-exception="java.lang.Throwable" to="scInternalServerError" />
@@ -138,29 +142,17 @@
     <action-state id="cacheSecurityToken">
         <secured attributes="IS_AUTHENTICATED_FULLY" />
         <evaluate expression="cacheSecurityToken.submit(flowRequestContext)" />
-        <transition to="parseAndValidateSAMLRequest">
+        <transition to="validateEndpointAddress">
             <set name="flowScope.idpToken" value="externalContext.sessionMap[flowScope.home_realm]"
/>
         </transition>
     </action-state>
     
-    <action-state id="parseAndValidateSAMLRequest">
-        <on-entry>
-            <evaluate expression="authnRequestParser.parseSAMLRequest(flowRequestContext,
flowScope.idpConfig,
-                                                                  flowScope.SAMLRequest)"
/>
-            <evaluate expression="authnRequestParser.retrieveRealm(flowRequestContext)"

-                          result="flowScope.realm"/>
-        </on-entry>
-        <evaluate expression="authnRequestValidator.validateAuthnRequest(flowRequestContext,
flowScope.idpConfig,
-                                                                  flowScope.Signature, flowScope.RelayState,
-                                                                  flowScope.SAMLRequest,
flowScope.realm)" />
-        <transition to="validateEndpointAddress"/>
-        <transition on-exception="org.apache.cxf.fediz.core.exception.ProcessingException"
to="viewBadRequest" />
-    </action-state>
-    
     <action-state id="validateEndpointAddress">
         <on-entry>
             <evaluate expression="authnRequestParser.retrieveConsumerURL(flowRequestContext)"

                       result="flowScope.consumerURL"/>
+            <evaluate expression="authnRequestParser.retrieveRealm(flowRequestContext)"

+                          result="flowScope.realm"/>
         </on-entry>
         <evaluate expression="passiveRequestorValidator.isValid(flowRequestContext, flowScope.consumerURL,
flowScope.realm)"/>
         <transition on="yes" to="requestRpToken" />


Mime
View raw message