it.cnr.icar.eric.client.ui.thin.security.SecurityUtil.java Source code

Java tutorial

Introduction

Here is the source code for it.cnr.icar.eric.client.ui.thin.security.SecurityUtil.java

Source

/*
 * ====================================================================
 * This file is part of the ebXML Registry by Icar Cnr v3.2 
 * ("eRICv32" in the following disclaimer).
 *
 * "eRICv32" is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * "eRICv32" 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License Version 3
 * along with "eRICv32".  If not, see <http://www.gnu.org/licenses/>.
 *
 * eRICv32 is a forked, derivative work, based on:
 *    - freebXML Registry, a royalty-free, open source implementation of the ebXML Registry standard,
 *      which was published under the "freebxml License, Version 1.1";
 *   - ebXML OMAR v3.2 Edition, published under the GNU GPL v3 by S. Krushe & P. Arwanitis.
 * 
 * All derivative software changes and additions are made under
 *
 * Copyright (C) 2013 Ing. Antonio Messina <messina@pa.icar.cnr.it>
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the freebxml Software Foundation.  For more
 * information on the freebxml Software Foundation, please see
 * "http://www.freebxml.org/".
 *
 * This product includes software developed by the Apache Software
 * Foundation (http://www.apache.org/).
 *
 * ====================================================================
 */
package it.cnr.icar.eric.client.ui.thin.security;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.Reader;
import java.security.KeyStore;
import java.security.Principal;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.security.auth.x500.X500PrivateCredential;
import java.security.cert.X509Certificate;
import javax.servlet.ServletRequest;
import javax.faces.context.FacesContext;
import javax.xml.registry.BulkResponse;
import javax.xml.registry.Connection;
import javax.xml.registry.DeclarativeQueryManager;
import javax.xml.registry.JAXRException;
import javax.xml.registry.Query;
import javax.xml.registry.infomodel.User;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import it.cnr.icar.eric.client.xml.registry.util.KeystoreUtil;
import it.cnr.icar.eric.client.xml.registry.util.ProviderProperties;
import it.cnr.icar.eric.common.CanonicalConstants;
import it.cnr.icar.eric.client.ui.thin.WebUIResourceBundle;
import it.cnr.icar.eric.common.CommonProperties;

/**
 *
 * Some utility methods related to XML security
 *
 * $Header: /cvsroot/ebxmlrr/eric/src/java/it/cnr/eric/client/ui/thin/security/SecurityUtil.java,v 1.19 2006/02/08 18:38:46 farrukh_najmi Exp $
 *
 */
public class SecurityUtil {

    private static SecurityUtil instance = null;
    private static final Log log = LogFactory.getLog(SecurityUtil.class);

    protected SecurityUtil() throws JAXRException {
    }

    /** 
     * The purpose of this method to determine if a <code>java.util.Set</code>
     * of credentials needs to be set on the <code>javax.registry.Connection
     * </code> object. This determination considers whether or not the web
     * container or policy is providing authentication services.
     * 
     * @param principal
     *    A <code>java.security.Principal</code> object
     * @param connection
     *    A <code>java.security.Connection</code> object
     */
    public void handleCredentials(Principal principal, Connection connection) throws JAXRException {
        boolean isAuthenticated = (principal != null);
        if (isAuthenticated) {
            // This servlet is protected by the web contianer or policy
            // agent. To have gotten here, the user must have logged in
            // successfully.
            String principalName = principal.getName();
            String guestPrincipalName = ProviderProperties.getInstance()
                    .getProperty("jaxr-ebxml.security.guestPrincipalName");
            if (!principalName.equals(guestPrincipalName)) {
                Set<Object> credentials = getCredentials(principalName);
                if ((credentials == null) || credentials.isEmpty()) {
                    credentials = generateCredentials(principalName);
                }
                setCredentials(credentials, connection);
            }
        } else {
            // This servlet is not being protected by the web container
            // or policy agent.
            String principalName = CommonProperties.getInstance()
                    .getProperty("eric.security.anonymousUserPrincipalName");
            if (principalName != null) {
                Set<Object> credentials = getCredentials(principalName);
                if ((credentials != null) && !credentials.isEmpty()) {
                    setCredentials(credentials, connection);
                }
            } else {
                // obtain credentials from client certificate
                Set<Object> credentials = getX509CertFromRequest();
                if ((credentials != null) && !credentials.isEmpty()) {
                    setCredentials(credentials, connection);
                }
            }
        }
    }

    public void handleCredentials(X509Certificate x509Cert, Connection connection) throws JAXRException {
        HashSet<Object> x509CertSet = new HashSet<Object>();
        x509CertSet.add(x509Cert);
        setCredentials(x509CertSet, connection);
    }

    public Set<Object> getX509CertFromRequest() {
        Set<Object> x509CertSet = null;
        ServletRequest request = (ServletRequest) FacesContext.getCurrentInstance().getExternalContext()
                .getRequest();
        Object certObj = request.getAttribute("javax.servlet.request.X509Certificate");

        if (certObj != null) {
            try {
                java.security.cert.Certificate[] certs = (java.security.cert.Certificate[]) certObj;
                X509Certificate x509Cert = (X509Certificate) certs[0];
                x509CertSet = new HashSet<Object>();
                x509CertSet.add(x509Cert);
            } catch (ClassCastException t) {
                log.warn(WebUIResourceBundle.getInstance().getString(
                        "message.TheFollowingCertificateTypeIsNotSupported",
                        new Object[] { certObj.getClass().getName() }));
            }
        }
        return x509CertSet;
    }

    /** 
     * The purpose of this method is to determine the return status based on 
     * the principal.  The return status is a <code>java.lang.String</code>.
     * Web app frameworks, such as JSF, use the status to determine page
     * navigation. 
     *
     * @param principal
     *     A <code>java.security.Principal</code> object
     * @return
     *     A <code>java.lang.String</code> representing the status.  Typically,
     *     the status is 'success' or 'failure', but it can have other values.
     * @deprecated
     */
    public String getStatus(Principal principal) throws JAXRException {
        String status = null;
        return status;
    }

    /** Determine if the user needs to self register.
     */
    public boolean isRegistrationNeeded(Connection connection, Principal principal) throws JAXRException {
        boolean isRegistrationNeeded = true;
        boolean isAuthenticated = (principal != null);
        if (isAuthenticated) {
            // This servlet is protected by the web contianer or policy
            // agent. To have gotten here, the user must have logged in
            // successfully.
            String principalName = principal.getName();
            String guestPrincipalName = ProviderProperties.getInstance()
                    .getProperty("jaxr-ebxml.security.guestPrincipalName");
            if (principalName.equals(guestPrincipalName)) {
                isRegistrationNeeded = false;
            } else {
                try {
                    isRegistrationNeeded = (findUserByPrincipalName(connection, principalName) == null);
                } catch (JAXRException e) {
                    // TODO: there is an issue with thin client self-registration
                    // The AuthenticationServiceImpl throws an exception when
                    // a self-registered user cert is sent, but is not included
                    // in the server keystore (because the registration has
                    // not been completed yet). This issue is under 
                    // investigation. This is a workaround for now
                    isRegistrationNeeded = true;
                }
            }
        } else {
            // The application does not have security enabled, so self-registration
            // is not possible. 
            isRegistrationNeeded = false;
        }

        return isRegistrationNeeded;
    }

    /**
     *
     * @param principalName
     * @throws JAXRException
     * @return
     */
    public User findUserByPrincipalName(Connection connection, String principalName) throws JAXRException {
        User user = null;
        DeclarativeQueryManager dqm = connection.getRegistryService().getDeclarativeQueryManager();
        String queryString = "SELECT * " + "FROM user_ u, slot s " + "WHERE u.id = s.parent AND s.name_='"
                + CanonicalConstants.CANONICAL_PRINCIPAL_NAME_URI + "' AND value='" + principalName + "'";
        Query query = dqm.createQuery(Query.QUERY_TYPE_SQL, queryString);
        BulkResponse br = dqm.executeQuery(query);
        Iterator<?> results = br.getCollection().iterator();
        while (results.hasNext()) {
            user = (User) results.next();
            break;
        }
        return user;
    }

    /** Get the credentials for the specified principal.
     *
     * @param alias
     *     The principal of the user making the request on the registry.
     *     This value will be obtained from the web container hosting the
     *     registry client by calling HttpServletRequest.getUserPrincipal().
     *     If this value is <code>null</code>, or if <code>principal.getName()</code>
     *     is <code>null</code>, then the default principal name, as specified
     *     by the <i>jaxr-ebxml.security.defaultPrincipalName</i> property
     *     will be used. If this property is not set, then an empty Set will
     *     be returned.
     * @return
     *     A Set of X500PrivateCredential objects representing the user's
     *     credentials. If this set is empty or null, no credentials will
     *     be passed to the registry with the request. The registry treats
     *     such requests as coming from the Registry Guest user.
     * @throws JAXRException
     *     Thrown if an error occurs while trying to map the principal
     *     to its credentials. An exception should not be thrown if there are
     *     no credentials associated with the principal. In this case, an
     *     empty Set should be returned.
     */
    public Set<Object> getCredentials(String alias) throws JAXRException {

        HashSet<Object> credentials = new HashSet<Object>();

        if (alias == null) {
            return credentials;
        }

        log.debug("Getting credentials for '" + alias + "'");

        try {
            credentials.add(it.cnr.icar.eric.client.xml.registry.util.SecurityUtil.getInstance()
                    .aliasToX500PrivateCredential(alias));
        } catch (JAXRException je) {
            // aliasToX500PrivateCredential() throws an exception if no certificate
            // can be found for the specified alias. For our purposes, this
            // is not an exception, so we just ignore such exceptions and
            // propogate all others.
            if (je.getMessage().equals("Alias unknown in keystore")
                    || je.getMessage().startsWith("KeyStore file not found")
                    || je.getMessage().startsWith("Failed to find an entry with the alias")) {
                log.warn(WebUIResourceBundle.getInstance().getString("message.FailedToGetCredentialsForException",
                        new Object[] { alias }), je);
                throw je;
            } else {
                throw je;
            }
        }

        return credentials;
    }

    /** Wrapper for ConnectionImpl.setCredentials() that ignores null or
     * empty credential sets.
     */
    public void setCredentials(Set<Object> credentials, Connection connection) throws JAXRException {
        if ((credentials != null) && !credentials.isEmpty()) {
            connection.setCredentials(credentials);
        }
    }

    /** Generate a key pair and add it to the keystore.
      *
      * @param alias
      * @return
      *     A HashSet of X500PrivateCredential objects.
      * @throws Exception
      */
    private Set<Object> generateCredentials(String alias) throws JAXRException {

        try {
            HashSet<Object> credentials = new HashSet<Object>();

            // The keystore file is at ${jaxr-ebxml.home}/security/keystore.jks. If
            // the 'jaxr-ebxml.home' property is not set, ${user.home}/jaxr-ebxml/ is
            // used.
            File keyStoreFile = KeystoreUtil.getKeystoreFile();
            String storepass = ProviderProperties.getInstance().getProperty("jaxr-ebxml.security.storepass",
                    "ebxmlrr");
            String keypass = ProviderProperties.getInstance().getProperty("jaxr-ebxml.security.keypass");
            if (keypass == null) {
                // keytool utility requires a six character minimum password.
                // pad passwords with < six chars
                if (alias.length() >= 6) {
                    keypass = alias;
                } else if (alias.length() == 5) {
                    keypass = alias + "1";
                } else if (alias.length() == 4) {
                    keypass = alias + "12";
                } else if (alias.length() == 3) {
                    keypass = alias + "123";
                }
                // alias should have at least 3 chars
            }
            log.debug("Generating key pair for '" + alias + "' in '" + keyStoreFile.getAbsolutePath() + "'");

            // When run in S1WS 6.0, this caused some native library errors. It appears that S1WS
            // uses different encryption spis than those in the jdk. 
            //            String[] args = {
            //                "-genkey", "-alias", uid, "-keypass", "keypass",
            //                "-keystore", keyStoreFile.getAbsolutePath(), "-storepass",
            //                new String(storepass), "-dname", "uid=" + uid + ",ou=People,dc=sun,dc=com"
            //            };
            //            KeyTool keytool = new KeyTool();
            //            ByteArrayOutputStream keytoolOutput = new ByteArrayOutputStream();
            //            try {
            //                keytool.run(args, new PrintStream(keytoolOutput));
            //            }
            //            finally {
            //                log.info(keytoolOutput.toString());
            //            }
            // To work around this problem, generate the key pair using keytool (which executes
            // in its own vm. Note that all the parameters must be specified, or keytool prompts
            // for their values and this 'hangs'
            String[] cmdarray = { "keytool", "-genkey", "-alias", alias, "-keypass", keypass, "-keystore",
                    keyStoreFile.getAbsolutePath(), "-storepass", storepass, "-dname", "cn=" + alias };
            Process keytool = Runtime.getRuntime().exec(cmdarray);
            try {
                keytool.waitFor();
            } catch (InterruptedException ie) {
            }
            if (keytool.exitValue() != 0) {
                log.error(WebUIResourceBundle.getInstance().getString("message.keytoolCommandFailedDetails"));
                Reader reader = new InputStreamReader(keytool.getErrorStream());
                BufferedReader bufferedReader = new BufferedReader(reader);
                while (bufferedReader.ready()) {
                    log.error(bufferedReader.readLine());
                }
                throw new JAXRException(
                        WebUIResourceBundle.getInstance().getString("excKeyToolCommandFail") + keytool.exitValue());
            }
            log.debug("Key pair generated successfully.");

            // After generating the keypair in the keystore file, we have to reload
            // SecurityUtil's KeyStore object.
            KeyStore keyStore = it.cnr.icar.eric.client.xml.registry.util.SecurityUtil.getInstance().getKeyStore();
            keyStore.load(new FileInputStream(keyStoreFile), storepass.toCharArray());

            credentials.add(it.cnr.icar.eric.client.xml.registry.util.SecurityUtil.getInstance()
                    .aliasToX500PrivateCredential(alias));

            return credentials;
        } catch (Exception e) {
            if (e instanceof JAXRException) {
                throw (JAXRException) e;
            } else {
                throw new JAXRException(e);
            }
        }
    }

    public String getAliasFromCredentials(Set<?> credentials) {
        String alias = null;
        Iterator<?> itr = credentials.iterator();
        while (itr.hasNext()) {
            try {
                Object obj = itr.next();
                if (obj instanceof X500PrivateCredential) {
                    X500PrivateCredential credential = (X500PrivateCredential) obj;
                    alias = it.cnr.icar.eric.client.xml.registry.util.SecurityUtil.getInstance()
                            .x500PrivateCredentialToAlias(credential);
                    break;
                } else if (obj instanceof X509Certificate) {
                    X509Certificate credential = (X509Certificate) obj;
                    alias = credential.getSubjectDN().getName();
                } else {
                    log.warn(WebUIResourceBundle.getInstance().getString("message.CouldGetAliasFromCredentials",
                            new Object[] { obj }));
                }
            } catch (Throwable t) {
                log.warn(WebUIResourceBundle.getInstance().getString("message.CouldGetAliasFromCredentials1"), t);
            }
        }
        return alias;
    }

    /**
     * Method main
     *
     * @param unused
     * @throws Exception
     */
    public static void main(String[] unused) throws Exception {
    }

    public synchronized static SecurityUtil getInstance() throws JAXRException {
        if (instance == null) {
            instance = new SecurityUtil();
        }

        return instance;
    }
}