mitm.djigzo.web.pages.admin.SSLCertificateManager.java Source code

Java tutorial

Introduction

Here is the source code for mitm.djigzo.web.pages.admin.SSLCertificateManager.java

Source

/*
 * Copyright (c) 2008-2011, Martijn Brinkers, Djigzo.
 * 
 * This file is part of Djigzo email encryption.
 *
 * Djigzo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License 
 * version 3, 19 November 2007 as published by the Free Software 
 * Foundation.
 *
 * Djigzo 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public 
 * License along with Djigzo. If not, see <http://www.gnu.org/licenses/>
 *
 * Additional permission under GNU AGPL version 3 section 7
 * 
 * If you modify this Program, or any covered work, by linking or 
 * combining it with saaj-api-1.3.jar, saaj-impl-1.3.jar, 
 * wsdl4j-1.6.1.jar (or modified versions of these libraries), 
 * containing parts covered by the terms of Common Development and 
 * Distribution License (CDDL), Common Public License (CPL) the 
 * licensors of this Program grant you additional permission to 
 * convey the resulting work.
 */
package mitm.djigzo.web.pages.admin;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.Set;

import mitm.application.djigzo.admin.FactoryRoles;
import mitm.common.security.SecurityFactory;
import mitm.common.security.SecurityFactoryFactory;
import mitm.common.security.certificate.ExtendedKeyUsageType;
import mitm.common.security.certificate.KeyUsageType;
import mitm.common.security.certificate.X509CertificateInspector;
import mitm.common.util.Check;
import mitm.common.util.SizeUtils;
import mitm.djigzo.web.pages.Admins;

import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.io.IOUtils;
import org.apache.tapestry5.PersistenceConstants;
import org.apache.tapestry5.annotations.Component;
import org.apache.tapestry5.annotations.IncludeStylesheet;
import org.apache.tapestry5.annotations.OnEvent;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.annotations.SetupRender;
import org.apache.tapestry5.corelib.components.Form;
import org.apache.tapestry5.corelib.components.PasswordField;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.Value;
import org.apache.tapestry5.upload.components.Upload;
import org.apache.tapestry5.upload.services.UploadEvents;
import org.apache.tapestry5.upload.services.UploadedFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.annotation.Secured;

@IncludeStylesheet("context:styles/pages/admin/sslCertificateManager.css")
@Secured({ FactoryRoles.ROLE_ADMIN })
public class SSLCertificateManager {
    private final static Logger logger = LoggerFactory.getLogger(SSLCertificateManager.class);

    /*
     * Maximum size of the file to upload (sanity check)
     */
    private static final int MAX_UPLOAD_SIZE = 1 * SizeUtils.MB;

    /*
     * True when uploading a file resulted in an error (mostly when file size exceeds maximum)
     */
    @Persist(PersistenceConstants.FLASH)
    private boolean uploadError;

    /*
     * The error message accompanying the uploadError property.
     */
    @Persist(PersistenceConstants.FLASH)
    private String uploadErrorMessage;

    /*
     * True if the SSL certificate is successfully installed
     */
    @Persist(PersistenceConstants.FLASH)
    private boolean successfullyInstalled;

    /*
     * True if a restart is required because a new SSL certificate was installed
     */
    @Persist
    private boolean restartRequired;

    /*
     * 'Handle' to the file that's uploaded
     */
    private UploadedFile file;

    @Component(id = "upload", parameters = { "value=file", "validate=required" })
    private Upload upload;

    @SuppressWarnings("unused")
    @Component(id = "password", parameters = { "value=password", "validate=required" })
    private PasswordField passwordField;

    /*
     * I disabled clientValidation because it kept on popping up a warning when I opened the file
     * browser
     */
    @Component(id = "form", parameters = { "clientValidation=false" })
    private Form form;

    /*
     * The password for the private key file
     */
    @Property
    private String password;

    @Inject
    @Value("${ssl.password}")
    private String sslPassword;

    @Inject
    @Value("${ssl.file}")
    private File sslFile;

    @Inject
    @Value("${djigzo-web.home}")
    private File djigzoHome;

    @SetupRender
    @Secured({ FactoryRoles.ROLE_ADMIN })
    protected void setupRender() {
        /*
         * Empty on purpose
         */
    }

    public boolean isrestartRequired() {
        return restartRequired;
    }

    /*
     * Event handler that gets called when the uploaded file exceeds the maximum size
     */
    @OnEvent(UploadEvents.UPLOAD_EXCEPTION)
    protected Object onUploadException(FileUploadException uploadException) {
        logger.error("Error uploading file", uploadException);

        uploadError = true;
        uploadErrorMessage = uploadException.getMessage();

        return SSLCertificateManager.class;
    }

    public void onValidateFromUpload(UploadedFile file) {
        /*
         * Sanity check
         */
        if (file.getSize() > MAX_UPLOAD_SIZE) {
            form.recordError("The uploaded file exceeds the maximum size of " + MAX_UPLOAD_SIZE);

            return;
        }
    }

    private KeyStore getKeyStore() throws KeyStoreException {
        SecurityFactory securityFactory = SecurityFactoryFactory.getSecurityFactory();

        try {
            KeyStore keyStore = securityFactory.createKeyStore("PKCS12");

            keyStore.load(file.getStream(), password.toCharArray());

            return keyStore;
        } catch (NoSuchProviderException e) {
            throw new KeyStoreException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new KeyStoreException(e);
        } catch (CertificateException e) {
            throw new KeyStoreException(e);
        } catch (IOException e) {
            throw new KeyStoreException(e);
        }
    }

    public void onValidateForm() {
        if (password != null && file != null) {
            try {
                KeyStore keyStore = getKeyStore();

                boolean keyFound = false;
                boolean validForSSL = true;

                Enumeration<String> aliases = keyStore.aliases();

                while (aliases.hasMoreElements()) {
                    String alias = aliases.nextElement();

                    if (keyStore.getKey(alias, null) != null) {
                        Certificate certificate = keyStore.getCertificate(alias);

                        if (!(certificate instanceof X509Certificate)) {
                            continue;
                        }

                        /*
                         * Check if the certificate is valid for SSL
                         */
                        X509CertificateInspector inspector = new X509CertificateInspector(
                                (X509Certificate) certificate);

                        Set<ExtendedKeyUsageType> extendedKeyUsage = inspector.getExtendedKeyUsage();

                        if (extendedKeyUsage != null
                                && !(extendedKeyUsage.contains(ExtendedKeyUsageType.ANYKEYUSAGE)
                                        || extendedKeyUsage.contains(ExtendedKeyUsageType.SERVERAUTH))) {
                            validForSSL = false;
                        }

                        Set<KeyUsageType> keyUsage = inspector.getKeyUsage();

                        if (keyUsage != null && !(keyUsage.contains(KeyUsageType.KEYAGREEMENT)
                                || keyUsage.contains(KeyUsageType.KEYENCIPHERMENT))) {
                            validForSSL = false;
                        }

                        keyFound = true;

                        break;
                    }
                }

                if (!keyFound) {
                    form.recordError(upload, "The PFX file does not contain a key.");
                } else {
                    if (!validForSSL) {
                        form.recordError(upload, "The certificate contained in the PFX file is not valid for SSL");
                    }
                }
            } catch (Exception e) {
                /*
                 * loading a PFX can sometime result in a ClassCastException so we catch Exception.
                 */
                form.recordError(upload, "The PFX file could not be read. Message: " + e.getMessage());

                logger.error("File is not a valid PFX file.", e);
            }
        }
    }

    public void onSuccess() {
        Check.notNull(password, "password");

        try {
            KeyStore keyStore = getKeyStore();

            File absoluteSslFile = sslFile.isAbsolute() ? sslFile : new File(djigzoHome, sslFile.getPath());

            /*
             * Write the KeyStore to a PFX file with the password used by the Servlet engine
             */
            FileOutputStream fos = new FileOutputStream(absoluteSslFile);

            try {
                keyStore.store(fos, sslPassword.toCharArray());

                fos.flush();

                successfullyInstalled = true;
                restartRequired = true;
            } finally {
                IOUtils.closeQuietly(fos);
            }
        } catch (Exception e) {
            logger.error("Error uploading SSL file.", e);

            uploadError = true;

            uploadErrorMessage = e.getMessage();
        }
    }

    /*
     * Event handler called when the cancel button is pressed.
     */
    public Object onCancel() {
        return Admins.class;
    }

    public UploadedFile getFile() {
        return file;
    }

    public void setFile(UploadedFile file) {
        this.file = file;
    }

    public boolean isUploadError() {
        return uploadError;
    }

    public void setUploadError(boolean uploadError) {
        this.uploadError = uploadError;
    }

    public String getUploadErrorMessage() {
        return uploadErrorMessage;
    }

    public void setUploadErrorMessage(String uploadErrorMessage) {
        this.uploadErrorMessage = uploadErrorMessage;
    }

    public boolean isSuccessfullyInstalled() {
        return successfullyInstalled;
    }

    public void setSuccessfullyInstalled(boolean successfullyInstalled) {
        this.successfullyInstalled = successfullyInstalled;
    }
}