Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * JBoss, Home of Professional Open Source.
   * Copyright 2012, Red Hat, Inc., and individual contributors
   * as indicated by the @author tags. See the copyright.txt file in the
   * distribution for a full listing of individual contributors.
   *
   * This is free software; you can redistribute it and/or modify it
   * under the terms of the GNU Lesser General Public License as
   * published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this software; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */
 
 package org.picketlink.identity.federation.bindings.jboss.auth;
 
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
 
 import  org.jboss.security.SecurityConstants;
 import  org.jboss.security.SimplePrincipal;
 import  org.jboss.security.auth.callback.ObjectCallback;
This LoginModule authenticates clients by validating their SAML assertions locally. If the supplied assertion contains roles, these roles are extracted and included in the Group returned by the getRoleSets method. The LoginModule is designed to validate SAML token using X509 certificate stored in XML signature within SAML assertion token. It validates:
  1. CertPath against specified truststore. It has to have common valid public certificate in the trusted entries.
  2. X509 certificate stored in SAML token didn't expire
  3. if signature itself is valid
  4. SAML token expiration
This module defines the following module options: roleKey: key of the attribute name that we need to use for Roles from the SAML assertion. This can be a comma-separated string values such as (Role,Membership) localValidationSecurityDomain: the security domain for the trust store information (via the JaasSecurityDomain) cache.invalidation - set it to true if you require invalidation of JBoss Auth Cache at SAML Principal expiration. jboss.security.security_domain -security domain at which Principal will expire if cache.invalidation is used. tokenEncodingType: encoding type of SAML token delivered via http request's header. Possible values are: base64 - content encoded as base64. In case of encoding will vary between base64 and gzip use base64 and LoginModule will detect gzipped data. gzip - gzipped content encoded as base64 none - content not encoded in any way samlTokenHttpHeader - name of http request header to fetch SAML token from. For example: "Authorize" samlTokenHttpHeaderRegEx - Java regular expression to be used to get SAML token from "samlTokenHttpHeader". Example: use: ."(.)".* to parse SAML token from header content like this: SAML_assertion="HHDHS=", at the same time set samlTokenHttpHeaderRegExGroup to 1. samlTokenHttpHeaderRegExGroup - Group value to be used when parsing out value of http request header specified by "samlTokenHttpHeader" using "samlTokenHttpHeaderRegEx".

Author(s):
Peter Skopek: pskopek at redhat dot com
@SuppressWarnings("unchecked")
public abstract class SAMLTokenCertValidatingCommonLoginModule extends
    protected Principal principal;
    protected SamlCredential credential;
    protected AssertionType assertion;
    protected boolean enableCacheInvalidation = false;
    protected String securityDomain = null;
Options that are computed by this login module. Few options are removed and the rest are set in the dispatch sts call
    protected Map<StringObjectoptions = new HashMap<StringObject>();

    
Original Options that are sent by the JDK JAAS Framework
    protected Map<StringObjectrawOptions = new HashMap<StringObject>();

    
This is an option that should identify the configuration file for WSTrustClient.
    public static final String STS_CONFIG_FILE = "configFile";

    
Key to specify the end point address
    public static final String ENDPOINT_ADDRESS = "endpointAddress";

    
Key to specify the port name
    public static final String PORT_NAME = "portName";

    
Key to specify the service name
    public static final String SERVICE_NAME = "serviceName";

    
Key to specify the username
    public static final String USERNAME_KEY = "username";

    
Key to specify the password
    public static final String PASSWORD_KEY = "password";
    // A variable used by the unit test to pass local validation
    protected boolean localTestingOnly = false;
    /*
     * (non-Javadoc)
     *
     * @see org.jboss.security.auth.spi.AbstractServerLoginModule#initialize(javax.security.auth.Subject,
     * javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map)
     */
    @Override
    public void initialize(Subject subjectCallbackHandler callbackHandlerMap<String, ?> sharedStateMap<String, ?> options) {
        super.initialize(subjectcallbackHandlersharedStateoptions);
        this..putAll(options);
        this..putAll(options);
        if (.isTraceEnabled()) {
            .trace(options.toString());
        }
        String cacheInvalidation = (Stringthis..remove("cache.invalidation");
        if (cacheInvalidation != null && !cacheInvalidation.isEmpty()) {
            this. = Boolean.parseBoolean(cacheInvalidation);
            this. = (Stringthis..remove(SecurityConstants.SECURITY_DOMAIN_OPTION);
            if (this. == null || this..isEmpty())
                throw .optionNotSet(SecurityConstants.SECURITY_DOMAIN_OPTION);
        }
        String roleKeyStr = (Stringoptions.get("roleKey");
        if (StringUtil.isNotNull(roleKeyStr)) {
             = roleKeyStr.trim();
        }
         = (Stringoptions.get("localValidationSecurityDomain");
        if ( == null) {
           throw .optionNotSet("localValidationSecurityDomain");
        }
        if (.startsWith("java:") == false)
             = SecurityConstants.JAAS_CONTEXT_ROOT + "/" + ;
        // initialize xmlsec
        org.apache.xml.security.Init.init();
    }
    /*
     * (non-Javadoc)
     *
     * @see org.jboss.security.auth.spi.AbstractServerLoginModule#login()
     */
    @Override
    public boolean login() throws LoginException {
        // if shared data exists, set our principal and assertion variables.
        if (super.login()) {
            Object sharedPrincipal = super..get("javax.security.auth.login.name");
            if (sharedPrincipal instanceof Principal)
                this. = (PrincipalsharedPrincipal;
            else {
                try {
                    this. = createIdentity(sharedPrincipal.toString());
                } catch (Exception e) {
                    throw .authFailedToCreatePrincipal(e);
                }
            }
            Object credential = super..get("javax.security.auth.login.password");
            if (credential instanceof SamlCredential)
                this. = (SamlCredentialcredential;
            else
                throw .authSharedCredentialIsNotSAMLCredential(credential.getClass().getName());
            return true;
        }
        // obtain the assertion from the callback handler.
        ObjectCallback callback = new ObjectCallback(null);
        Element assertionElement = null;
        try {
            if (getSamlTokenHttpHeader() != null) {
                this. = getCredentialFromHttpRequest();
            }
            else {
                super..handle(new Callback[] { callback });
                if (callback.getCredential() instanceof SamlCredential == false)
                    throw .authSharedCredentialIsNotSAMLCredential(callback.getCredential().getClass().getName());
                this. = (SamlCredentialcallback.getCredential();
            }
            assertionElement = this..getAssertionAsElement();
        } catch (Exception e) {
            throw .authErrorHandlingCallback(e);
        }
        try {
            this. = SAMLUtil.fromElement(assertionElement);
        } catch (Exception e) {
            throw .authFailedToParseSAMLAssertion(e);
        }
        try {
            // cert path validation
            validateSAMLCredential();
            // if the assertion is valid, create a principal containing the assertion subject.
            SubjectType subject = .getSubject();
            if (subject != null) {
                BaseIDAbstractType baseID = subject.getSubType().getBaseID();
                if (baseID instanceof NameIDType) {
                    NameIDType nameID = (NameIDTypebaseID;
                    this. = new PicketLinkPrincipal(nameID.getValue());
                    // If the user has configured cache invalidation of subject based on saml token expiry
                    if () {
                        TimeCacheExpiry cacheExpiry = this.getCacheExpiry();
                        XMLGregorianCalendar expiry = AssertionUtil.getExpiration();
                        if (expiry != null) {
                            Date expiryDate = expiry.toGregorianCalendar().getTime();
                            .trace("Creating Cache Entry for JBoss at [" + new Date() + "] , with expiration set to SAML expiry = " + expiryDate);
                            cacheExpiry.register(expiryDate);
                        } else {
                            .samlAssertionWithoutExpiration(.getID());
                        }
                    }
                }
            }
        } catch (Throwable e) {
            .error(e);
            LoginException le = new LoginException(e.getMessage());
            throw le;
        }
        // if password-stacking has been configured, set the principal and the assertion in the shared map.
        if (getUseFirstPass()) {
            super..put("javax.security.auth.login.name"this.);
            super..put("javax.security.auth.login.password"this.);
        }
        return (super. = true);
    }
    /* (non-Javadoc)
     * @see org.jboss.security.auth.spi.AbstractServerLoginModule#commit()
     */
    @Override
    public boolean commit() throws LoginException {
        if (super.commit()) {
            final boolean added = subject.getPublicCredentials().add(this.);
            if (added && .isTraceEnabled())
                .trace("Added Credential " + this.);
            return true;
        } else {
            return false;
        }
    }

    
Called if the overall authentication failed (phase 2).
    @Override
    public boolean abort() throws LoginException {
        clearState();
        super.abort();
        return true;
    }
    @Override
    public boolean logout() throws LoginException {
        clearState();
        super.logout();
        return true;
    }
    private void clearState() {
        AbstractSTSLoginModule.removeAllSamlCredentials(subject);
         = null;
    }
    /*
     * (non-Javadoc)
     *
     * @see org.jboss.security.auth.spi.AbstractServerLoginModule#getIdentity()
     */
    @Override
    protected Principal getIdentity() {
        return this.;
    }
    /*
     * (non-Javadoc)
     *
     * @see org.jboss.security.auth.spi.AbstractServerLoginModule#getRoleSets()
     */
    @Override
    protected Group[] getRoleSets() throws LoginException {
        if (this. == null) {
            try {
                this. = SAMLUtil.fromElement(this..getAssertionAsElement());
            } catch (Exception e) {
                throw .authFailedToParseSAMLAssertion(e);
            }
        }
        if (.isTraceEnabled()) {
            try {
                .trace("Assertion from where roles will be sought = " + AssertionUtil.asString());
            } catch (ProcessingException ignore) {
            }
        }
        List<StringroleKeys = new ArrayList<String>();
        if (StringUtil.isNotNull()) {
            roleKeys.addAll(StringUtil.tokenize());
        }
        String groupName = SecurityConstants.ROLES_IDENTIFIER;
        Group rolesGroup = new PicketLinkGroup(groupName);
        List<Stringroles = AssertionUtil.getRoles(roleKeys);
        for (String role : roles) {
            rolesGroup.addMember(new SimplePrincipal(role));
        }
        return new Group[] { rolesGroup };
    }
        return JBossAuthCacheInvalidationFactory.getCacheExpiry();
    }


    
This method validates SAML Credential in following steps:
  1. Validate the signing key embedded in SAML token is still valid, not expired
  2. Validate the signing key embedded in SAML token is trusted against a local truststore, such as certpath validation
  3. Validate SAML token is still valid, not expired
  4. Validate the SAML signature using the embedded signing key in SAML token itself as you indicated below
If something goes wrong throws LoginException.

Throws:
LoginException
        X509Certificate cert = getX509Certificate();
        // public certificate validation
        validateCertPath(cert);
        // check time validity of the certificate
        cert.checkValidity();
        boolean sigValid = false;
        try {
            sigValid = AssertionUtil.isSignatureValid(.getAssertionAsElement(), cert.getPublicKey());
        } catch (ProcessingException e) {
            .processingError(e);
        }
        if (!sigValid) {
            throw .authSAMLInvalidSignatureError();
        }
        if (AssertionUtil.hasExpired()) {
            throw .authSAMLAssertionExpiredError();
        }
    }

    
Extract x509 certificate from SAML Assertion's signature.

Returns:
Throws:
LoginException
    private X509Certificate getX509Certificate() throws LoginException {
        try {
            Element assertion = .getAssertionAsElement();
            String xmlSignatureNSPrefix = findNameSpacePrefix(assertion..get());
            String expression = "//" + xmlSignatureNSPrefix + ":Signature[1]";
            XPathFactory xpf = XPathFactory.newInstance();
            XPath xpath = xpf.newXPath();
            xpath.setNamespaceContext(
                    NamespaceContext.create()
                        .addNsUriPair(xmlSignatureNSPrefix..get())
                    );
            Element sigElement =
                (Elementxpath.evaluate(expression.getAssertionAsElement(), .);
            XMLSignature signature =
                new XMLSignature(sigElement"");
            if (.isTraceEnabled()) {
                .trace("sigElement="+sigElement.getTextContent());
            }
            KeyInfo keyInfo = signature.getKeyInfo();
            if (!keyInfo.containsX509Data()) {
                log.error("Cannot find X509Data element");
                throw new LoginException("Cannot find X509Data element");
            }
            X509Certificate certificate = signature.getKeyInfo().getX509Certificate();
            if (certificate == null) {
                .error("Not able to extract x509 certificate");
                throw new LoginException("Not able to extract x509 certificate");
            }
            if (.isTraceEnabled()) {
                .trace("Got certificate="+certificate.toString());
            }
            return certificate;
        }
        catch (Exception e) {
            .error(e);
            throw new LoginException(e.getLocalizedMessage());
        }
    }
    private String findNameSpacePrefix(Element elementString xmlns) {
        NodeList nl = element.getElementsByTagNameNS(xmlns"Signature");
        if (nl.getLength() > 0) {
            return nl.item(0).getPrefix();
        }
        else {
            return null;
        }
    }

    
Validate certificate path against keystore specified as SecurityDomain in module-option.

Parameters:
cert
    protected void validateCertPath(X509Certificate certificate)
            throws LoginException {
        // get cert path
        CertPath certPath = null;
        try {
            CertificateFactory certFact = CertificateFactory
                    .getInstance("X.509");
            certPath = certFact.generateCertPath(Arrays.asList(certificate));
        } catch (CertificateEncodingException e) {
            .error(e.getMessage());
            throw new LoginException(e.getLocalizedMessage());
        } catch (CertificateException e) {
            .error(e.getMessage());
            throw new LoginException(e.getLocalizedMessage());
        }
        if (.isTraceEnabled()) {
            .trace("Certificates from SAML token:");
            for (Certificate c : certPath.getCertificates()) {
                .trace("Type of certificate=" + c.getType());
                .trace(c.toString());
            }
        }
        // certpath validation
        try {
            // get keystore
            KeyStore trustStore = getKeyStore();
            if (trustStore == null) {
            }
            if (.isTraceEnabled()) {
                .trace("Certificates from truststore:");
                Enumeration<StringtsAliases = trustStore.aliases();
                while (tsAliases.hasMoreElements()) {
                    String alias = tsAliases.nextElement();
                    .trace("Alias=" + alias);
                    Certificate[] chain = trustStore.getCertificateChain(alias);
                    if (chain != null) {
                        .trace(alias + " is a chain:");
                        for (Certificate c : chain) {
                            .trace(c.toString());
                        }
                    }
                    Certificate crt = trustStore.getCertificate(alias);
                    if (crt != null) {
                        .trace(alias + " is a certificate of type "
                                + crt.getType());
                        .trace(crt.toString());
                    }
                }
            }
            // Create the parameters for the validator
            PKIXParameters params = new PKIXParameters(trustStore);
            // Disable CRL checking since we are not supplying any CRLs
            params.setRevocationEnabled(false);
            // Create the validator and validate the path
            // To create a path, see Creating a Certification Path
            CertPathValidator certPathValidator = CertPathValidator
                    .getInstance(CertPathValidator.getDefaultType());
            if (.isTraceEnabled()) {
                .trace("certPathValidator is ready");
            }
            CertPathValidatorResult result = certPathValidator.validate(
                    certPathparams);
            if (.isTraceEnabled()) {
                .trace("CertPathValidatorResult=" + result);
            }
        } catch (Exception e) {
            .error(e);
            throw new LoginException(e.getLocalizedMessage());
        }
    }

    
Binding dependent version of getting configured keyStore. uses module-option: localValidationSecurityDomain.

Returns:
Throws:
Exception
    protected abstract KeyStore getKeyStore() throws Exception;
New to GrepCode? Check out our FAQ X