Java tutorial
/* * * * Copyright (C) 2008 Pingtel Corp., certain elements licensed under a Contributor Agreement. * Contributors retain copyright to elements licensed under a Contributor Agreement. * Licensed to the User under the LGPL license. * * $ */ package org.sipfoundry.sipxconfig.admin; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.Properties; import java.util.Set; import java.util.TreeSet; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.methods.DeleteMethod; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sipfoundry.sipxconfig.admin.commserver.Location; import org.sipfoundry.sipxconfig.admin.commserver.LocationsManager; import org.sipfoundry.sipxconfig.admin.commserver.SipxProcessContext; import org.sipfoundry.sipxconfig.admin.commserver.SipxReplicationContext; import org.sipfoundry.sipxconfig.admin.commserver.SoftwareAdminApi; import org.sipfoundry.sipxconfig.common.UserException; import org.sipfoundry.sipxconfig.service.SipxBridgeService; import org.sipfoundry.sipxconfig.service.SipxConfigService; import org.sipfoundry.sipxconfig.service.SipxImbotService; import org.sipfoundry.sipxconfig.service.SipxIvrService; import org.sipfoundry.sipxconfig.service.SipxProvisionService; import org.sipfoundry.sipxconfig.service.SipxRecordingService; import org.sipfoundry.sipxconfig.service.SipxRestService; import org.sipfoundry.sipxconfig.service.SipxService; import org.sipfoundry.sipxconfig.service.SipxServiceManager; import org.sipfoundry.sipxconfig.xmlrpc.ApiProvider; import org.sipfoundry.sipxconfig.xmlrpc.XmlRpcRemoteException; import org.springframework.beans.factory.annotation.Required; import static org.apache.commons.lang.StringUtils.deleteWhitespace; import static org.apache.commons.lang.StringUtils.join; /** * Backup provides Java interface to backup scripts */ public class CertificateManagerImpl implements CertificateManager { private static final Log LOG = LogFactory.getLog(CertificateManager.class); private static final String PROPERTIES_FILE = "webCert.properties"; private static final String DOUBLE_QUOTES = "\""; private static final String READ_ERROR = "&msg.readError"; private static final String WRITE_ERROR = "&msg.writeError"; private static final String SCRIPT_ERROR = "&msg.scriptGenError"; private static final String TMP = "tmp"; private static final String WORKDIR_FLAG = "--workdir"; private static final String BLANK = " "; private static final String SCRIPT_EXCEPTION_MESSAGE = "Script finished with exit code "; private static final String RUNNING = "Running"; private static final String DEFAULTS_FLAG = "--defaults"; private static final String PARAMETERS_FLAG = "--parameters"; private static final String GEN_SSL_KEYS_SH = "/gen-ssl-keys.sh"; private static final String CA_REHASH = "/ca_rehash"; private static final String KEYSTOREGEN_SH = "/sipxkeystoregen"; private static final String CHECK_CERT = "/check-cert.sh"; private static final String WEB_ONLY = "--web-only"; private static final String WEB_KEY = "-web.key"; private static final String SSL_CERT = "ssl.crt"; private static final String ERROR_CERT_VALIDATE = "&error.validate"; private static final String ERROR_MSG_COPY = "&msg.copyError"; private static final String ERROR_VALID = "&error.valid"; private static final String EXTERNAL_KEY_BASED = "external-key-based"; private static final String RESTART_SIPXECS = "restart"; private static final String[] SSL_CA_EXTENSIONS = { "crt" }; private String m_binCertDirectory; private String m_certDirectory; private String m_sslDirectory; private String m_sslAuthDirectory; private String m_certdbDirectory; private String m_libExecDirectory; private SipxProcessContext m_processContext; private SipxServiceManager m_sipxServiceManager; private SipxReplicationContext m_sipxReplicationContext; private ApiProvider<SoftwareAdminApi> m_softwareAdminApiProvider; private LocationsManager m_locationsManager; public void setBinCertDirectory(String binCertDirectory) { m_binCertDirectory = binCertDirectory; } public void setCertDirectory(String certDirectory) { m_certDirectory = certDirectory; } public void setSslDirectory(String sslDirectory) { m_sslDirectory = sslDirectory; } public void setLibExecDirectory(String libExecDirectory) { m_libExecDirectory = libExecDirectory; } public Properties loadCertPropertiesFile() { File propertiesFile = new File(m_certDirectory, PROPERTIES_FILE); try { FileInputStream propertiesStream = new FileInputStream(propertiesFile); Properties properties = new Properties(); properties.load(propertiesStream); Enumeration<Object> propertiesEnum = properties.keys(); while (propertiesEnum.hasMoreElements()) { String key = (String) propertiesEnum.nextElement(); String value = properties.getProperty(key); value = StringUtils.strip(value, DOUBLE_QUOTES); properties.setProperty(key, value); } return properties; } catch (FileNotFoundException e) { return null; } catch (IOException e) { throw new UserException(READ_ERROR, propertiesFile.getPath()); } } public void writeCertPropertiesFile(Properties properties) { File propertiesFile = new File(m_certDirectory, PROPERTIES_FILE); try { Enumeration<Object> propertiesEnum = properties.keys(); while (propertiesEnum.hasMoreElements()) { String key = (String) propertiesEnum.nextElement(); String value = properties.getProperty(key); value = DOUBLE_QUOTES + value + DOUBLE_QUOTES; properties.setProperty(key, value); } FileOutputStream propertiesStream = new FileOutputStream(propertiesFile); properties.store(propertiesStream, null); } catch (IOException e) { throw new UserException(WRITE_ERROR, propertiesFile.getPath()); } } public String readCSRFile(String serverName) { File csrFile = new File(m_certDirectory, serverName + "-web.csr"); try { return FileUtils.readFileToString(csrFile, "US-ASCII"); } catch (FileNotFoundException e) { return null; } catch (IOException e) { throw new UserException(READ_ERROR, csrFile.getPath()); } } private String[] getGenerateCSRFileCommand() { String[] cmdLine = new String[] { m_binCertDirectory + GEN_SSL_KEYS_SH, "--csr", WEB_ONLY, DEFAULTS_FLAG, PARAMETERS_FLAG, PROPERTIES_FILE, WORKDIR_FLAG, m_certDirectory }; return cmdLine; } private String[] getValidateCertFileCommand(File file, boolean isCA) { if (isCA) { return new String[] { m_binCertDirectory + CHECK_CERT, "--certificate-authority ", file.getPath() }; } else { return new String[] { m_binCertDirectory + CHECK_CERT, file.getPath() }; } } private String[] getShowCertFileCommand(File file) { String[] cmdLine = new String[] { m_binCertDirectory + GEN_SSL_KEYS_SH, "--show-cert", file.getPath() }; return cmdLine; } private String[] getHashCertsCommand() { String[] cmdLine = new String[] { m_binCertDirectory + CA_REHASH }; return cmdLine; } private String[] getKeyStoreGenCommand() { String[] cmdLine = new String[] { m_libExecDirectory + KEYSTOREGEN_SH }; return cmdLine; } public File getCAFile(String fileName) { return new File(m_sslAuthDirectory + File.separator + fileName); } public File getCATmpFile(String fileName) { return new File(getCATmpDir() + File.separator + fileName); } private String getCATmpDir() { try { FileUtils.forceMkdir(new File(m_sslAuthDirectory + File.separator + TMP)); return m_sslAuthDirectory + File.separator + TMP; } catch (IOException ex) { throw new UserException("&error.temp"); } } public void setSslAuthDirectory(String sslAuthDirectory) { m_sslAuthDirectory = sslAuthDirectory; } private InputStream runCommand(String[] command) { try { Runtime runtime = Runtime.getRuntime(); Process proc = runtime.exec(command); LOG.debug("Executing: " + StringUtils.join(command, BLANK)); proc.waitFor(); if (proc.exitValue() != 0) { throw new ScriptExitException(proc.exitValue()); } return proc.getInputStream(); } catch (IOException e) { throw new RuntimeException(e); } catch (InterruptedException e) { throw new RuntimeException(e); } } public void generateCSRFile() { try { runCommand(getGenerateCSRFileCommand()); } catch (RuntimeException e) { throw new UserException(SCRIPT_ERROR, e.getMessage()); } } public boolean validateCertificateAuthority(File file) { try { runCommand(getValidateCertFileCommand(file, true)); return true; } catch (ScriptExitException ex) { deleteCRTAuthorityTmpDirectory(); return false; } catch (RuntimeException ex) { deleteCRTAuthorityTmpDirectory(); throw new UserException(ERROR_CERT_VALIDATE, file.getName()); } } public boolean validateCertificate(File file) { try { runCommand(getValidateCertFileCommand(file, false)); return true; } catch (ScriptExitException ex) { return false; } catch (RuntimeException ex) { throw new UserException(ERROR_CERT_VALIDATE, file.getName()); } } public String showCertificate(File file) { try { InputStream inputStream = runCommand(getShowCertFileCommand(file)); return IOUtils.toString(inputStream); } catch (Exception ex) { throw new UserException("&error.show", file.getName()); } } public void rehashCertificates() { try { runCommand(getHashCertsCommand()); } catch (RuntimeException ex) { throw new UserException("&error.rehash"); } } public void restartRemote() { // restart distributed systems in order to regenerate their keystore/truststore. try { Location[] locations = m_locationsManager.getLocations(); for (Location location : locations) { if (!location.isPrimary()) { SoftwareAdminApi api = m_softwareAdminApiProvider.getApi(location.getSoftwareAdminUrl()); api.exec(m_locationsManager.getPrimaryLocation().getFqdn(), RESTART_SIPXECS); } } } catch (XmlRpcRemoteException e) { LOG.error("Cannot restart remote locations", e); } } public void generateKeyStores() { try { runCommand(getKeyStoreGenCommand()); // Mark required services for restart markServicesForRestart(); } catch (RuntimeException ex) { throw new UserException("&error.regenstore"); } } /** * Mark for restart only services from primary location. * Anyway, sipXecs from secondary locations * is restarted, so there is no need to mark services for restart for these locations */ private void markServicesForRestart() { SipxService configService = m_sipxServiceManager.getServiceByBeanId(SipxConfigService.BEAN_ID); SipxService bridgeService = m_sipxServiceManager.getServiceByBeanId(SipxBridgeService.BEAN_ID); SipxService ivrService = m_sipxServiceManager.getServiceByBeanId(SipxIvrService.BEAN_ID); SipxService recordingService = m_sipxServiceManager.getServiceByBeanId(SipxRecordingService.BEAN_ID); SipxService imbotService = m_sipxServiceManager.getServiceByBeanId(SipxImbotService.BEAN_ID); SipxService openfireService = m_sipxServiceManager.getServiceByBeanId("sipxOpenfireService"); SipxService provisionService = m_sipxServiceManager.getServiceByBeanId(SipxProvisionService.BEAN_ID); SipxService restService = m_sipxServiceManager.getServiceByBeanId(SipxRestService.BEAN_ID); Location primaryLocation = m_locationsManager.getPrimaryLocation(); m_processContext.markServicesForRestart(primaryLocation, Arrays.asList(configService, bridgeService, ivrService, recordingService, openfireService, imbotService, provisionService, restService)); } public File getCRTFile(String serverName) { return new File(m_certDirectory, serverName + "-web.crt"); } public File getExternalCRTFile() { return getCRTFile(EXTERNAL_KEY_BASED); } public File getKeyFile(String serverName) { return new File(m_certDirectory, serverName + WEB_KEY); } public File getExternalKeyFile() { return getKeyFile(EXTERNAL_KEY_BASED); } public void deleteCA(CertificateDecorator cert) { if (isSystemGenerated(cert.getFileName())) { throw new UserException("&error.delete.default.ca", cert.getFileName()); } Collection<File> files = FileUtils.listFiles(new File(m_sslAuthDirectory), SSL_CA_EXTENSIONS, false); Collection<File> filesToDelete = new ArrayList<File>(); try { for (File file : files) { if (StringUtils.equals(file.getCanonicalFile().getName(), cert.getFileName())) { filesToDelete.add(getCAFile(file.getName())); } } HttpClient httpClient = getNewHttpClient(); Location[] locations = m_locationsManager.getLocations(); for (Location curLocation : locations) { for (File file : filesToDelete) { String remoteFileName = join( new Object[] { curLocation.getHttpsServerUrl(), file.getAbsolutePath() }); DeleteMethod httpDelete = new DeleteMethod(remoteFileName); try { int statusCode = httpClient.executeMethod(httpDelete); if (statusCode != 200) { throw new UserException("&error.https.server.status.code", curLocation.getFqdn(), String.valueOf(statusCode)); } } catch (HttpException ex) { throw new UserException("&error.https.server", curLocation.getFqdn(), ex.getMessage()); } finally { httpDelete.releaseConnection(); } } } } catch (IOException ex) { throw new UserException("&error.delete.cert"); } } // check the issuer DN name against ssl.crt (always signed by sipx) private boolean isSystemGenerated(String fileName) { X509Certificate sslCrt = X509CertificateUtils .getX509Certificate(m_sslDirectory + File.separatorChar + SSL_CERT); X509Certificate currentCertificate = X509CertificateUtils .getX509Certificate(m_certdbDirectory + File.separatorChar + fileName); if (sslCrt != null && currentCertificate != null) { return StringUtils.equals(sslCrt.getIssuerDN().getName(), currentCertificate.getIssuerDN().getName()); } return false; } public void deleteCAs(Collection<CertificateDecorator> listCert) { UserException userEx = null; for (CertificateDecorator certDecorator : listCert) { try { deleteCA(certDecorator); } catch (UserException ex) { userEx = ex; } } if (userEx != null) { throw userEx; } } public void copyCRTAuthority() { AnyFile anyFile = null; Location[] locations = m_locationsManager.getLocations(); for (File file : new File(getCATmpDir()).listFiles()) { anyFile = new AnyFile(); anyFile.setDirectory(m_sslAuthDirectory); anyFile.setName(deleteWhitespace(file.getName())); anyFile.setSourceFilePath(file.getAbsolutePath()); for (Location location : locations) { //call replicate on each location so JobStatus page can show replication status on //each affected location m_sipxReplicationContext.replicate(location, anyFile); } } deleteCRTAuthorityTmpDirectory(); } public Set<CertificateDecorator> listCertificates() { Set<CertificateDecorator> certificates = new TreeSet<CertificateDecorator>(); Collection<File> files = FileUtils.listFiles(new File(m_sslAuthDirectory), SSL_CA_EXTENSIONS, false); CertificateDecorator certificateDecorator = null; for (File file : files) { try { file = file.getCanonicalFile(); } catch (IOException ex) { continue; } certificateDecorator = new CertificateDecorator(); certificateDecorator.setFileName(file.getName()); certificateDecorator.setDirName(file.getParentFile().getPath()); certificateDecorator.setSystemGenerated(isSystemGenerated(file.getName())); certificates.add(certificateDecorator); } return certificates; } public void deleteCRTAuthorityTmpDirectory() { try { FileUtils.deleteDirectory(new File(getCATmpDir())); } catch (IOException ex) { LOG.error("Cannot delete temporary certificate directory"); // Temporary path cannot be deleted } } public void writeCRTFile(String crt, String server) { File crtFile = getCRTFile(server); try { FileUtils.writeStringToFile(getCRTFile(server), crt); } catch (IOException e) { throw new UserException(WRITE_ERROR, crtFile.getPath()); } } public void writeExternalCRTFile(String crt) { writeCRTFile(crt, EXTERNAL_KEY_BASED); } public void writeKeyFile(String key) { File keyFile = getExternalKeyFile(); try { FileUtils.writeStringToFile(keyFile, key); } catch (IOException e) { throw new UserException(WRITE_ERROR, keyFile.getPath()); } } public void importKeyAndCertificate(String server, boolean isCsrBased) { File newCertificate = isCsrBased ? getCRTFile(server) : getExternalCRTFile(); if (!newCertificate.exists()) { throw new UserException(ERROR_VALID); } File newKey = isCsrBased ? getKeyFile(server) : getExternalKeyFile(); if (!newKey.exists()) { throw new UserException(ERROR_VALID); } if (!validateCertificate(newCertificate)) { throw new UserException(ERROR_VALID); } File oldCertificate = new File(m_sslDirectory, "ssl-web.crt"); File oldKey = new File(m_sslDirectory, "ssl-web.key"); File backupCertificate = new File(m_sslDirectory, "ssl-web.oldcrt"); File backupKey = new File(m_sslDirectory, "ssl-web.oldkey"); try { if (isCsrBased) { Runtime runtime = Runtime.getRuntime(); String[] cmdLine = new String[] { m_binCertDirectory + GEN_SSL_KEYS_SH, WORKDIR_FLAG, m_certDirectory, "--pkcs", WEB_ONLY, DEFAULTS_FLAG, PARAMETERS_FLAG, PROPERTIES_FILE, }; Process proc = runtime.exec(cmdLine); LOG.debug(RUNNING + StringUtils.join(cmdLine, BLANK)); proc.waitFor(); if (proc.exitValue() != 0) { throw new UserException(SCRIPT_ERROR, SCRIPT_EXCEPTION_MESSAGE + proc.exitValue()); } } FileUtils.copyFile(oldCertificate, backupCertificate); FileUtils.copyFile(oldKey, backupKey); FileUtils.copyFile(newCertificate, oldCertificate); FileUtils.copyFile(newKey, oldKey); } catch (Exception ex) { throw new UserException(ERROR_MSG_COPY); } try { generateKeyStores(); } catch (UserException userException) { try { FileUtils.copyFile(backupCertificate, oldCertificate); FileUtils.copyFile(backupKey, oldKey); backupCertificate.delete(); backupKey.delete(); } catch (Exception ex) { throw new UserException(ERROR_MSG_COPY); } throw userException; } backupCertificate.delete(); backupKey.delete(); } public void setCertdbDirectory(String certdbDirectory) { m_certdbDirectory = certdbDirectory; } public class ScriptExitException extends UserException { public ScriptExitException(int exitCode) { super("&error.script.exit.code", exitCode); } } @Required public void setProcessContext(SipxProcessContext processContext) { m_processContext = processContext; } @Required public void setSipxServiceManager(SipxServiceManager sipxServiceManager) { m_sipxServiceManager = sipxServiceManager; } @Required public void setSipxReplicationContext(SipxReplicationContext sipxReplicationContext) { m_sipxReplicationContext = sipxReplicationContext; } @Required public void setSoftwareAdminApiProvider(ApiProvider<SoftwareAdminApi> softwareAdminApiProvider) { m_softwareAdminApiProvider = softwareAdminApiProvider; } @Required public void setLocationsManager(LocationsManager locationsManager) { m_locationsManager = locationsManager; } protected HttpClient getNewHttpClient() { return new HttpClient(); } }