Java tutorial
/* * 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; } }