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.certificate; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.UnrecoverableEntryException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.Enumeration; import mitm.application.djigzo.admin.FactoryRoles; import mitm.application.djigzo.workflow.KeyAndCertificateWorkflow; import mitm.application.djigzo.ws.KeyAndCertificateWorkflowWS; import mitm.common.security.SecurityFactoryFactory; import mitm.common.security.SecurityFactoryFactoryException; import mitm.common.util.SizeUtils; import mitm.common.ws.WebServiceCheckedException; import mitm.djigzo.web.pages.Certificates; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.tapestry5.Block; 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.Checkbox; 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/certificate/certificateImportKey.css") @Secured({ FactoryRoles.ROLE_ADMIN, FactoryRoles.ROLE_PKI_MANAGER }) public class CertificateImportKey { private final static Logger logger = LoggerFactory.getLogger(CertificateImportKey.class); /* * Maximum size of the file to upload */ private static final int MAX_UPLOAD_SIZE = 100 * SizeUtils.MB; /* * 'Handle' to the file that's uploaded */ private UploadedFile file; /* * Maximum number of certificates that are uploaded in one call. */ @Inject @Value("${certificateImportKey.maxBatchSize}") private int maxBatchSize; @Inject @Property private KeyAndCertificateWorkflowWS keyAndCertificateWorkflowWS; /* * The number of entries imported */ @Property @Persist(PersistenceConstants.FLASH) private Integer importCount; /* * True if an error occurred during private key file upload */ @Persist(PersistenceConstants.FLASH) private boolean importError; /* * The error message (only valid when error is true) */ @Persist(PersistenceConstants.FLASH) private String importErrorMessage; /* * If true self entries without a key are ignored */ @Property @Persist private Boolean ignoreMissingKey; /* * The password for the private key file */ @Property private String password; @SuppressWarnings("unused") @Component(id = "upload", parameters = { "value=file", "validate=required" }) private Upload upload; /* * I disabled clientValidation because it kept on popping up a warning when I opened the file * browser. Could be a bug. */ @Component(id = "importForm", parameters = { "clientValidation=false" }) private Form form; @SuppressWarnings("unused") @Component(id = "ignoreMissingKey", parameters = { "value=ignoreMissingKey" }) private Checkbox ignoreMissingKeyCheckbox; @SuppressWarnings("unused") @Component(id = "password", parameters = { "value=password" }) private PasswordField passwordField; @Inject private Block infoBlockPlural; @Inject private Block infoBlockSingular; @SetupRender @Secured({ FactoryRoles.ROLE_ADMIN, FactoryRoles.ROLE_PKI_MANAGER }) protected void setupRender() { if (ignoreMissingKey == null) { ignoreMissingKey = false; } } public UploadedFile getFile() { return file; } public void setFile(UploadedFile file) { this.file = file; } public Block getInfoBlock() { if (importCount == null) { return null; } return importCount == 1 ? infoBlockSingular : infoBlockPlural; } 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); } } /* * 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); importError = true; importErrorMessage = uploadException.getMessage(); return CertificateImportKey.class; } private int uploadKeyStore(KeyStore keyStore, KeyAndCertificateWorkflow.MissingKey missingKey, String password) throws WebServiceCheckedException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { ByteArrayOutputStream pfx = new ByteArrayOutputStream(); keyStore.store(pfx, password.toCharArray()); return keyAndCertificateWorkflowWS.addPFX(pfx.toByteArray(), password, missingKey); } private void importPfx() throws KeyStoreException, NoSuchProviderException, SecurityFactoryFactoryException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableEntryException, WebServiceCheckedException { /* * To prevent timeouts on the SOAP connection we should upload the PFX file in batches if the PFX file * contains a large number of entries. The PFX file should therefore be opened. */ KeyStore allKeys = SecurityFactoryFactory.getSecurityFactory().createKeyStore("PKCS12"); if (password == null) { password = ""; } allKeys.load(file.getStream(), password.toCharArray()); KeyAndCertificateWorkflow.MissingKey missingKey = ignoreMissingKey ? KeyAndCertificateWorkflow.MissingKey.SKIP_CERTIFICATE : KeyAndCertificateWorkflow.MissingKey.ADD_CERTIFICATE; int imported = 0; KeyStore batchKeys = SecurityFactoryFactory.getSecurityFactory().createKeyStore("PKCS12"); batchKeys.load(null, password.toCharArray()); Enumeration<String> aliases = allKeys.aliases(); KeyStore.PasswordProtection passwordProtection = new KeyStore.PasswordProtection(password.toCharArray()); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); if (allKeys.isKeyEntry(alias)) { KeyStore.Entry entry = allKeys.getEntry(alias, passwordProtection); batchKeys.setEntry(alias, entry, passwordProtection); } else { Certificate certificate = allKeys.getCertificate(alias); batchKeys.setCertificateEntry(alias, certificate); } if (batchKeys.size() >= maxBatchSize) { imported += uploadKeyStore(batchKeys, missingKey, password); batchKeys = SecurityFactoryFactory.getSecurityFactory().createKeyStore("PKCS12"); batchKeys.load(null, password.toCharArray()); } } /* * Check if there are still some entries left to add (happens when the number * of entries is not a multiple of maxBatchSize) */ if (batchKeys.size() > 0) { imported += uploadKeyStore(batchKeys, missingKey, password); } this.importCount = imported; } /* * Called when the form is submitted. */ public void onSuccess() { try { importPfx(); } catch (Exception e) { logger.error("Error importing the private key file. Message: ", e); importError = true; importErrorMessage = ExceptionUtils.getRootCauseMessage(e); } } public boolean isSingular() { return importCount == 1; } public boolean isImportError() { return importError; } public String getImportErrorMessage() { return importErrorMessage; } /* * Event handler called when the cancel button is pressed. */ public Object onCancel() { return Certificates.class; } }