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.NoSuchProviderException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Collection; import java.util.LinkedList; import mitm.application.djigzo.admin.FactoryRoles; import mitm.application.djigzo.ws.CertificateStore; import mitm.application.djigzo.ws.KeyAndCertStoreWS; import mitm.application.djigzo.ws.X509CertStoreWS; import mitm.common.security.certificate.CertificateUtils; import mitm.common.security.certificate.X509CertificateInspector; import mitm.common.util.SizeUtils; import mitm.common.ws.WebServiceCheckedException; import mitm.djigzo.web.pages.Certificates; import mitm.djigzo.web.pages.Roots; import mitm.djigzo.web.services.CertificateStoreMarker; import mitm.djigzo.web.services.RootStoreMarker; import org.apache.commons.fileupload.FileUploadException; 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.Mixins; 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.Select; 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/certificateImport.css") @Secured({ FactoryRoles.ROLE_ADMIN, FactoryRoles.ROLE_PKI_MANAGER }) public class CertificateImport { private final static Logger logger = LoggerFactory.getLogger(CertificateImport.class); /* * Maximum size of the file to upload (sanity check) */ private static final int MAX_UPLOAD_SIZE = 100 * SizeUtils.MB; /* * Maximum number of certificates that are uploaded in one call. */ @Inject @Value("${certificateImport.maxBatchSize}") private int maxBatchSize; /* * 'Handle' to the file that's uploaded */ private UploadedFile file; @Inject @Property @CertificateStoreMarker private KeyAndCertStoreWS keyAndCertStoreWS; @Inject @Property @RootStoreMarker private KeyAndCertStoreWS rootWS; /* * The store to store the certificates in */ @Property private CertificateStore store = CertificateStore.CERTIFICATES; /* * If true self signed certificates are ignored */ @Property @Persist(PersistenceConstants.FLASH) private Boolean ignoreSelfSigned; /* * If true only self signed certificates are imported */ @Property @Persist(PersistenceConstants.FLASH) private Boolean checkSelfSigned; /* * If true expired certificates are ignored */ @Property @Persist(PersistenceConstants.FLASH) private Boolean ignoreExpired; /* * The number of certificates actually imported */ @Property @Persist(PersistenceConstants.FLASH) private Integer importCount; /* * True when uploading a file resulted in an error (mostly when file size exceeds maximum) */ @Persist(PersistenceConstants.FLASH) private boolean importError; /* * The error message accompanying the uploadError property. */ @Persist(PersistenceConstants.FLASH) private String importErrorMessage; /* * The total number of certificates found in the uploaded file */ @SuppressWarnings("unused") @Property @Persist(PersistenceConstants.FLASH) private int totalCount; @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 */ @Component(id = "importForm", parameters = { "clientValidation=false" }) private Form form; @SuppressWarnings("unused") @Component(id = "store", parameters = { "blankOption=NEVER", "value=store", "script=literal:syncCheckboxes();" }) @Mixins({ "AddScript" }) private Select storeSelect; @SuppressWarnings("unused") @Component(id = "ignoreSelfSigned", parameters = { "value=ignoreSelfSigned" }) private Checkbox ignoreSelfSignedCheckbox; @SuppressWarnings("unused") @Component(id = "checkSelfSigned", parameters = { "value=checkSelfSigned" }) private Checkbox checkSelfSignedCheckbox; @SuppressWarnings("unused") @Component(id = "ignoreExpired", parameters = { "value=ignoreExpired" }) private Checkbox ignoreExpiredCheckbox; @Inject private Block infoBlockPlural; @Inject private Block infoBlockSingular; @SetupRender @Secured({ FactoryRoles.ROLE_ADMIN, FactoryRoles.ROLE_PKI_MANAGER }) protected void setupRender() { if (ignoreSelfSigned == null) { ignoreSelfSigned = true; } if (checkSelfSigned == null) { checkSelfSigned = true; } if (ignoreExpired == null) { ignoreExpired = true; } } 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 onActivate(CertificateStore store) { if (store != null) { this.store = store; } } public CertificateStore onPassivate() { return store; } public void onValidateFromUpload(UploadedFile file) throws NoSuchProviderException { /* * Sanity check */ if (file.getSize() > MAX_UPLOAD_SIZE) { form.recordError("The uploaded file exceeds the maximum size of " + MAX_UPLOAD_SIZE); return; } /* * We need to check if the uploaded file is really a certificate file. */ try { Collection<X509Certificate> certificates = CertificateUtils.readX509Certificates(file.getStream()); if (certificates.size() == 0) { form.recordError("The uploaded file does not contain valid certificates."); } } catch (CertificateException e) { logger.error("Error validating uploaded file.", e); form.recordError("The uploaded file is not a valid certificate file."); } } /* * 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 CertificateImport.class; } private X509CertStoreWS getCertStoreWS() { X509CertStoreWS certStore; switch (store) { case CERTIFICATES: certStore = keyAndCertStoreWS; break; case ROOTS: certStore = rootWS; break; default: throw new IllegalArgumentException("Unknown store."); } return certStore; } private void addCertificates(Collection<X509Certificate> certificates) throws CertificateEncodingException, IOException, WebServiceCheckedException { /* * Create some initial buffer space. Assume certificate is 2K on average. */ ByteArrayOutputStream bos = new ByteArrayOutputStream(maxBatchSize * 2048); CertificateUtils.writeCertificates(certificates, bos); int added = getCertStoreWS().addCertificates(bos.toByteArray()); importCount = importCount + added; } /* * Returns true if the certificate is to be accepted for import. */ private boolean accept(X509Certificate certificate) { if (ignoreExpired && X509CertificateInspector.isExpired(certificate)) { return false; } boolean isSelfSigned = X509CertificateInspector.isSelfSigned(certificate); if (checkSelfSigned && !isSelfSigned) { return false; } if (ignoreSelfSigned && isSelfSigned) { return false; } return true; } /* * Called when the form is submitted. */ public void onSuccess() throws NoSuchProviderException, IOException, WebServiceCheckedException, CertificateException { importCount = new Integer(0); /* * It should already be checked that the file contains certificates. */ Collection<X509Certificate> certificates = CertificateUtils.readX509Certificates(file.getStream()); totalCount = certificates.size(); Collection<X509Certificate> batch = new LinkedList<X509Certificate>(); for (X509Certificate certificate : certificates) { if (accept(certificate)) { batch.add(certificate); if (batch.size() >= maxBatchSize) { addCertificates(batch); batch.clear(); } } } /* * Check if there are still some certificates left to add (happens when the number * of certificates is not a multiple of maxBatchSize) */ if (batch.size() > 0) { addCertificates(batch); } } /* * Event handler called when the cancel button is pressed. */ public Object onCancel() { return store == CertificateStore.CERTIFICATES ? Certificates.class : Roots.class; } public boolean isImportError() { return importError; } public String getImportErrorMessage() { return importErrorMessage; } }