org.gluu.oxtrust.action.ManageCertificateAction.java Source code

Java tutorial

Introduction

Here is the source code for org.gluu.oxtrust.action.ManageCertificateAction.java

Source

/*
 * oxTrust is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
 *
 * Copyright (c) 2014, Gluu
 */

package org.gluu.oxtrust.action;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.security.spec.RSAPublicKeySpec;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.faces.context.FacesContext;
import javax.security.auth.x500.X500Principal;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.WordUtils;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.provider.JCERSAPrivateCrtKey;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.util.encoders.Base64;
import org.gluu.oxtrust.ldap.service.ApplianceService;
import org.gluu.oxtrust.ldap.service.OrganizationService;
import org.gluu.oxtrust.ldap.service.SSLService;
import org.gluu.oxtrust.model.GluuAppliance;
import org.gluu.oxtrust.model.GluuCustomPerson;
import org.gluu.oxtrust.model.cert.TrustStoreCertificate;
import org.gluu.oxtrust.model.cert.TrustStoreConfiguration;
import org.gluu.oxtrust.util.OxTrustConstants;
import org.gluu.site.ldap.persistence.exception.LdapMappingException;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.international.StatusMessage.Severity;
import org.jboss.seam.log.Log;
import org.richfaces.event.FileUploadEvent;
import org.richfaces.model.UploadedFile;
import org.xdi.config.oxtrust.ApplicationConfiguration;
import org.xdi.util.StringHelper;
import org.xdi.util.io.FileHelper;
import org.xdi.util.io.ResponseHelper;

/**
 * Manages SSL certificates
 * 
 * @author Oleksiy Tataryn
 * @author Yuriy Movchan Date: 03/03/2014
 * 
 */
@Name("manageCertificateAction")
@Scope(ScopeType.CONVERSATION)
@Restrict("#{identity.loggedIn}")
public class ManageCertificateAction implements Serializable {
    public static final String BEGIN_CERT_REQ = "-----BEGIN CERTIFICATE REQUEST-----";
    public static final String END_CERT_REQ = "-----END CERTIFICATE REQUEST-----";

    private static final long serialVersionUID = 4012709440384265524L;

    @Logger
    private Log log;

    @In(value = "#{facesContext}")
    FacesContext facesContext;

    @In
    private FacesMessages facesMessages;

    @In
    private SSLService sslService;

    @In(value = "#{oxTrustConfiguration.applicationConfiguration}")
    private ApplicationConfiguration applicationConfiguration;

    @In
    private ApplianceService applianceService;

    @SuppressWarnings("seam-unresolved-variable")
    @In
    protected GluuCustomPerson currentPerson;

    private TrustStoreConfiguration trustStoreConfiguration;
    private List<TrustStoreCertificate> trustStoreCertificates;

    private String orgInumFN, tomcatCertFN, idpCertFN;

    private HashMap<String, String> issuer;
    private HashMap<String, String> subject;

    private String uploadMarker;
    private TrustStoreCertificate trustStoreCertificateUploadMarker;

    private boolean certsMmanagePossible;
    private boolean initialized;
    private boolean wereAnyChanges;

    @Restrict("#{s:hasPermission('configuration', 'access')}")
    public String init() {
        if (this.initialized) {
            return OxTrustConstants.RESULT_SUCCESS;
        }

        this.wereAnyChanges = false;

        this.certsMmanagePossible = prepareTempWorkspace();

        this.orgInumFN = StringHelper.removePunctuation(OrganizationService.instance().getOrganizationInum());
        this.tomcatCertFN = orgInumFN + "-java.crt";
        this.idpCertFN = orgInumFN + "-shib.crt";

        try {
            GluuAppliance appliance = applianceService.getAppliance();

            if (appliance == null) {
                return OxTrustConstants.RESULT_FAILURE;
            }

            trustStoreConfiguration = appliance.getTrustStoreConfiguration();
            if (trustStoreConfiguration == null) {
                trustStoreConfiguration = new TrustStoreConfiguration();
            }

            trustStoreCertificates = appliance.getTrustStoreCertificates();
            if (trustStoreCertificates == null) {
                trustStoreCertificates = new ArrayList<TrustStoreCertificate>();
            }
        } catch (Exception ex) {
            log.error("Failed to load appliance configuration", ex);

            return OxTrustConstants.RESULT_FAILURE;
        }

        this.initialized = true;

        return OxTrustConstants.RESULT_SUCCESS;
    }

    /**
     * Fills issuer and subject maps with data about currently selected
     * certificate
     */
    @Restrict("#{s:hasPermission('configuration', 'access')}")
    public void getCert(String fileName) {
        X509Certificate cert = sslService.getCertificate(getTempCertDir() + fileName);
        loadCert(cert);
    }

    /**
     * Fills issuer and subject maps with data about currently selected
     * certificate
     */
    @Restrict("#{s:hasPermission('configuration', 'access')}")
    public void getCert(TrustStoreCertificate trustStoreCertificate) {
        this.issuer = new HashMap<String, String>();
        this.subject = new HashMap<String, String>();

        if (trustStoreCertificate != null) {
            X509Certificate cert = sslService
                    .getCertificate(new ByteArrayInputStream(trustStoreCertificate.getCertificate().getBytes()));
            loadCert(cert);
        }
    }

    private void loadCert(X509Certificate cert) {
        if (cert != null) {
            String issuerDN = cert.getIssuerX500Principal().getName();
            String[] values = issuerDN.split("(?<!\\\\),");
            for (String value : values) {
                String[] keyValue = value.split("=");
                issuer.put(keyValue[0], keyValue[1]);
            }
            String subjectDN = cert.getSubjectX500Principal().getName();
            values = subjectDN.split("(?<!\\\\),");
            for (String value : values) {
                String[] keyValue = value.split("=");
                subject.put(keyValue[0], keyValue[1]);
            }
            subject.put("validUntil", StringHelper.toString(cert.getNotAfter()));
            subject.put("validAfter", StringHelper.toString(cert.getNotBefore()));
        }
    }

    @Restrict("#{s:hasPermission('configuration', 'access')}")
    public String generateCSR(String fileName) {
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }

        KeyPair pair = getKeyPair(fileName);
        boolean result = false;
        if (pair != null) {
            String url = applicationConfiguration.getIdpUrl().replaceFirst(".*//", "");
            String csrPrincipal = String.format("CN=%s", url);
            X500Principal principal = new X500Principal(csrPrincipal);

            PKCS10CertificationRequest csr = null;
            try {
                csr = new PKCS10CertificationRequest("SHA1withRSA", principal, pair.getPublic(), null,
                        pair.getPrivate());
            } catch (GeneralSecurityException e) {
                log.error(e.getMessage(), e);
                return OxTrustConstants.RESULT_FAILURE;
            }

            // Form download responce
            StringBuilder response = new StringBuilder();

            response.append(BEGIN_CERT_REQ + "\n");
            response.append(WordUtils.wrap(new String(Base64.encode(csr.getDEREncoded())), 64, "\n", true) + "\n");
            response.append(END_CERT_REQ + "\n");

            result = ResponseHelper.downloadFile("csr.pem", OxTrustConstants.CONTENT_TYPE_TEXT_PLAIN,
                    response.toString().getBytes(), facesContext);
        }

        return result ? OxTrustConstants.RESULT_SUCCESS : OxTrustConstants.RESULT_FAILURE;
    }

    @Restrict("#{s:hasPermission('configuration', 'access')}")
    public boolean compare(String fileName) {
        KeyPair pair = getKeyPair(fileName);
        X509Certificate cert = sslService.getCertificate(getTempCertDir() + fileName);

        boolean noFilesPresent = (pair == null) && (cert == null);

        boolean filesPresent = (pair != null) && (cert != null);
        boolean filesValid = false;
        if (filesPresent) {
            filesValid = (pair.getPublic() != null) && (pair.getPublic().equals(cert.getPublicKey()));
        }

        boolean compareResult = (noFilesPresent || (filesPresent && filesValid));
        log.debug(fileName + " compare result: " + compareResult);
        return compareResult;
    }

    private KeyPair getKeyPair(String fileName) {
        KeyPair pair = null;
        JCERSAPrivateCrtKey privateKey = null;
        PEMReader r = null;
        FileReader fileReader = null;

        File keyFile = new File(getTempCertDir() + fileName.replace("crt", "key"));
        if (keyFile.isFile()) {
            try {
                fileReader = new FileReader(keyFile);
                r = new PEMReader(fileReader, new PasswordFinder() {
                    public char[] getPassword() {
                        // Since keys are stored without a password this
                        // function should not be called.
                        return null;
                    }
                });

                Object keys = r.readObject();
                if (keys == null) {
                    log.error(" Unable to read keys from: " + keyFile.getAbsolutePath());
                    return null;
                }

                if (keys instanceof KeyPair) {
                    pair = (KeyPair) keys;
                    log.debug(keyFile.getAbsolutePath() + "contains KeyPair");
                } else if (keys instanceof JCERSAPrivateCrtKey) {

                    privateKey = (JCERSAPrivateCrtKey) keys;
                    log.debug(keyFile.getAbsolutePath() + "contains JCERSAPrivateCrtKey");
                    BigInteger exponent = privateKey.getPublicExponent();
                    BigInteger modulus = privateKey.getModulus();

                    RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(modulus, exponent);
                    PublicKey publicKey = null;
                    try {
                        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

                        publicKey = keyFactory.generatePublic(publicKeySpec);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    pair = new KeyPair(publicKey, privateKey);
                } else {
                    log.error(keyFile.getAbsolutePath() + " Contains unsupported key type: "
                            + keys.getClass().getName());
                    return null;
                }

            } catch (IOException e) {
                log.error(e.getMessage(), e);
                return null;
            } finally {
                try {
                    r.close();
                    fileReader.close();
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                    return null;
                }
            }
        } else {
            log.error("Key file does not exist : " + keyFile.getAbsolutePath());
        }
        log.debug("KeyPair successfully extracted from: " + keyFile.getAbsolutePath());
        return pair;
    }

    @Restrict("#{s:hasPermission('configuration', 'access')}")
    public boolean certPresent(String filename) {
        KeyPair pair = getKeyPair(filename);
        X509Certificate cert = sslService.getCertificate(getTempCertDir() + filename);

        boolean filesPresent = (pair != null) && (cert != null);

        return filesPresent;
    }

    public String getIdpCertFN() {
        return idpCertFN;
    }

    public String getTomcatCertFN() {
        return tomcatCertFN;
    }

    public String getTempCertDir() {
        return applicationConfiguration.getTempCertDir() + File.separator;
    }

    public HashMap<String, String> getIssuer() {
        return issuer;
    }

    public HashMap<String, String> getSubject() {
        return subject;
    }

    public void setUploadMarker(String uploadMarker) {
        this.uploadMarker = uploadMarker;
        this.trustStoreCertificateUploadMarker = null;
    }

    public void setUploadMarker(TrustStoreCertificate trustStoreCertificate) {
        this.uploadMarker = null;
        this.trustStoreCertificateUploadMarker = trustStoreCertificate;
    }

    private boolean prepareTempWorkspace() {
        String tempDirFN = applicationConfiguration.getTempCertDir();
        String dirFN = applicationConfiguration.getCertDir();
        File certDir = new File(dirFN);
        if (tempDirFN == null || dirFN == null || !certDir.isDirectory() || StringHelper.isEmpty(tempDirFN)) {

            return false;
        } else {
            File tempDir = new File(tempDirFN);
            // If tempDir exists - empty it, if not - create. If exists, but
            // isFile - write an error and return false.
            if (tempDir.isDirectory()) {
                File[] files = tempDir.listFiles();
                for (File file : files) {
                    if (file.isFile()) {
                        file.delete();
                    }
                }
            } else {
                if (tempDir.exists()) {
                    log.error("Temporary certifcates path exists but is not a directory");
                    return false;
                } else {
                    tempDir.mkdirs();
                }
            }

            File[] files = certDir.listFiles();
            for (File file : files) {
                if (file.isFile()) {
                    try {
                        FileHelper.copy(file, new File(tempDirFN + File.separator + file.getName()));
                    } catch (IOException e) {
                        log.error("Unable to populate temp certs directory: ", e);
                        return false;
                    }
                }
            }
        }

        return true;
    }

    public static ManageCertificateAction instance() {
        return (ManageCertificateAction) Component.getInstance(ManageCertificateAction.class);
    }

    @Restrict("#{s:hasPermission('configuration', 'access')}")
    public String update() {
        if (!isCertsManagePossible()) {
            return OxTrustConstants.RESULT_FAILURE;
        }

        boolean isUpdateTrustCertificates = updateTrustCertificates();
        boolean isUpdatedCertificates = updateCertificates();
        boolean result = isUpdateTrustCertificates && isUpdatedCertificates;

        if (result) {
            tirggerTrustStoreUpdate();
        }

        return result ? OxTrustConstants.RESULT_SUCCESS : OxTrustConstants.RESULT_FAILURE;
    }

    private boolean updateTrustCertificates() {
        try {
            // Reload entry to include latest changes
            GluuAppliance tmpAppliance = applianceService.getAppliance();

            TrustStoreConfiguration currTrustStoreConfiguration = tmpAppliance.getTrustStoreConfiguration();
            List<TrustStoreCertificate> currTrustStoreCertificates = tmpAppliance.getTrustStoreCertificates();
            if (currTrustStoreCertificates == null) {
                currTrustStoreCertificates = new ArrayList<TrustStoreCertificate>(0);
            }

            if (!trustStoreConfiguration.equals(currTrustStoreConfiguration)
                    || !trustStoreCertificates.equals(currTrustStoreCertificates)) {
                this.wereAnyChanges = true;
            }

            tmpAppliance.setTrustStoreConfiguration(trustStoreConfiguration);

            if (trustStoreCertificates.size() == 0) {
                tmpAppliance.setTrustStoreCertificates(null);
            } else {
                tmpAppliance.setTrustStoreCertificates(trustStoreCertificates);
            }

            applianceService.updateAppliance(tmpAppliance);
        } catch (LdapMappingException ex) {
            log.error("Failed to update appliance configuration", ex);
            facesMessages.add(Severity.ERROR, "Failed to update appliance");
            return false;
        }

        return true;
    }

    /**
     * Updates certificates from temporary working directory to production and
     * restarts services.
     * 
     * @return true if update was successful. false if update was aborted due to
     *         some error (perhaps permissions issue.)
     */
    private boolean updateCertificates() {
        if (!compare(tomcatCertFN) || !compare(idpCertFN)) {
            facesMessages.add(Severity.ERROR,
                    "Certificates and private keys should match. Certificate update aborted.");
            return false;
        }

        String tempDirFN = applicationConfiguration.getTempCertDir();
        String dirFN = applicationConfiguration.getCertDir();
        File certDir = new File(dirFN);
        File tempDir = new File(tempDirFN);
        if (tempDirFN == null || dirFN == null || !certDir.isDirectory() || !tempDir.isDirectory()) {
            facesMessages.add(Severity.ERROR, "Certificate update aborted due to filesystem error");
            return false;
        } else {
            File[] files = tempDir.listFiles();
            for (File file : files) {
                try {
                    if (file.isFile()
                            && !FileUtils.contentEquals(file, new File(dirFN + File.separator + file.getName()))) {
                        FileHelper.copy(file, new File(dirFN + File.separator + file.getName()));
                        this.wereAnyChanges = true;
                    }
                } catch (IOException e) {
                    facesMessages.add(Severity.FATAL,
                            "Certificate update failed. Certificates may have been corrupted. Please contact a Gluu administrator for help.");
                    log.fatal("Error occured on certificates update:", e);
                }
            }
        }

        return true;
    }

    private void tirggerTrustStoreUpdate() {
        String certDirFileName = applicationConfiguration.getCertDir();
        File certDir = new File(certDirFileName);

        if (this.wereAnyChanges) {
            File pkcs12 = new File(certDir, orgInumFN + "-java.pkcs12");
            File pem = new File(certDir, orgInumFN + "-java.pem");
            File jks = new File(certDir, orgInumFN + "-java.jks");

            log.info("Deleting %s : %s", orgInumFN + "-java.pkcs12", pkcs12.delete());
            log.info("Deleting %s : %s", orgInumFN + "-java.pem", pem.delete());
            log.info("Deleting %s : %s", orgInumFN + "-java.jks", jks.delete());

            ApplianceService.instance().restartServices();

            facesMessages.add(Severity.WARN,
                    "Certificates were updated and appliance service will be restarted. Please log in again in 5 minutes.");

            this.wereAnyChanges = false;
        }
    }

    @Restrict("#{s:hasPermission('configuration', 'access')}")
    public void cancel() {
    }

    @Restrict("#{s:hasPermission('configuration', 'access')}")
    public void certUpload(FileUploadEvent event) {
        if (this.trustStoreCertificateUploadMarker == null) {
            updateCert(event.getUploadedFile());
        } else {
            updateTrsutStoreCert(event.getUploadedFile());
        }
    }

    private void updateCert(UploadedFile item) {
        InputStream is = null;
        OutputStream os = null;
        try {
            is = item.getInputStream();
            os = new FileOutputStream(getTempCertDir() + this.uploadMarker);
            BufferedOutputStream bos = new BufferedOutputStream(os);

            IOUtils.copy(is, bos);
            bos.flush();
        } catch (IOException ex) {
            log.error("Failed to upload certicicate", ex);
        } finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(os);
        }
    }

    private void updateTrsutStoreCert(UploadedFile item) {
        InputStream is = null;
        try {
            is = item.getInputStream();
            String certificate = IOUtils.toString(is);
            this.trustStoreCertificateUploadMarker.setCertificate(certificate);

            this.trustStoreCertificateUploadMarker.setAddedAt(new Date());
            this.trustStoreCertificateUploadMarker.setAddedBy(currentPerson.getDn());

        } catch (IOException ex) {
            log.error("Failed to upload key", ex);
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    @Restrict("#{s:hasPermission('configuration', 'access')}")
    public void keyUpload(FileUploadEvent event) {
        updateKey(event.getUploadedFile());
    }

    private void updateKey(UploadedFile item) {
        InputStream is = null;
        OutputStream os = null;
        try {
            is = item.getInputStream();
            os = new FileOutputStream(getTempCertDir() + this.uploadMarker.replace("crt", "key"));
            BufferedOutputStream bos = new BufferedOutputStream(os);

            IOUtils.copy(is, bos);
            bos.flush();
        } catch (IOException ex) {
            log.error("Failed to upload key", ex);
        } finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(os);
        }
    }

    public void addPublicCertificate() {
        TrustStoreCertificate trustStoreCertificate = new TrustStoreCertificate();
        trustStoreCertificate.setAddedAt(new Date());
        trustStoreCertificate.setAddedBy(currentPerson.getDn());

        this.trustStoreCertificates.add(trustStoreCertificate);
    }

    public void removePublicCertificate(TrustStoreCertificate removeTrustStoreCertificate) {
        for (Iterator<TrustStoreCertificate> iterator = this.trustStoreCertificates.iterator(); iterator
                .hasNext();) {
            TrustStoreCertificate trustStoreCertificate = iterator.next();
            if (System.identityHashCode(removeTrustStoreCertificate) == System
                    .identityHashCode(trustStoreCertificate)) {
                iterator.remove();
                return;
            }
        }
    }

    public boolean isInitialized() {
        return initialized;
    }

    public boolean isCertsManagePossible() {
        return certsMmanagePossible;
    }

    public TrustStoreConfiguration getTrustStoreConfiguration() {
        return trustStoreConfiguration;
    }

    public List<TrustStoreCertificate> getTrustStoreCertificates() {
        return trustStoreCertificates;
    }

}