Java tutorial
/* * * This file is part of the XiPKI project. * Copyright (c) 2014 - 2015 Lijun Liao * Author: Lijun Liao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3 * as published by the Free Software Foundation with the addition of the * following permission added to Section 15 as permitted in Section 7(a): * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY * THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT * OF THIRD PARTY RIGHTS. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License. * * You can be released from the requirements of the license by purchasing * a commercial license. Buying such a license is mandatory as soon as you * develop commercial activities involving the XiPKI software without * disclosing the source code of your own applications. * * For more information, please contact Lijun Liao at this * address: lijun.liao@gmail.com */ package org.xipki.ca.server.impl; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.math.BigInteger; import java.net.SocketException; import java.security.NoSuchAlgorithmException; import java.security.Security; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.sql.Connection; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.pkcs.Attribute; import org.bouncycastle.asn1.pkcs.CertificationRequest; import org.bouncycastle.asn1.pkcs.CertificationRequestInfo; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.encoders.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xipki.audit.api.AuditLevel; import org.xipki.audit.api.AuditLoggingService; import org.xipki.audit.api.AuditLoggingServiceRegister; import org.xipki.audit.api.AuditStatus; import org.xipki.audit.api.PCIAuditEvent; import org.xipki.ca.api.CertPublisherException; import org.xipki.ca.api.CertprofileException; import org.xipki.ca.api.DfltEnvironmentParameterResolver; import org.xipki.ca.api.EnvironmentParameterResolver; import org.xipki.ca.api.OperationException; import org.xipki.ca.api.X509CertWithDBCertId; import org.xipki.ca.api.publisher.X509CertificateInfo; import org.xipki.ca.server.impl.X509SelfSignedCertBuilder.GenerateSelfSignedResult; import org.xipki.ca.server.impl.store.CertificateStore; import org.xipki.ca.server.mgmt.api.CAEntry; import org.xipki.ca.server.mgmt.api.CAHasRequestorEntry; import org.xipki.ca.server.mgmt.api.CAManager; import org.xipki.ca.server.mgmt.api.CAMgmtException; import org.xipki.ca.server.mgmt.api.CAStatus; import org.xipki.ca.server.mgmt.api.CASystemStatus; import org.xipki.ca.server.mgmt.api.CertprofileEntry; import org.xipki.ca.server.mgmt.api.ChangeCAEntry; import org.xipki.ca.server.mgmt.api.CmpControl; import org.xipki.ca.server.mgmt.api.CmpControlEntry; import org.xipki.ca.server.mgmt.api.CmpRequestorEntry; import org.xipki.ca.server.mgmt.api.CmpResponderEntry; import org.xipki.ca.server.mgmt.api.PublisherEntry; import org.xipki.ca.server.mgmt.api.X509CAEntry; import org.xipki.ca.server.mgmt.api.X509ChangeCrlSignerEntry; import org.xipki.ca.server.mgmt.api.X509CrlSignerEntry; import org.xipki.common.CRLReason; import org.xipki.common.CertRevocationInfo; import org.xipki.common.CmpUtf8Pairs; import org.xipki.common.ConfigurationException; import org.xipki.common.ParamChecker; import org.xipki.common.util.AlgorithmUtil; import org.xipki.common.util.CollectionUtil; import org.xipki.common.util.IoUtil; import org.xipki.common.util.LogUtil; import org.xipki.common.util.SecurityUtil; import org.xipki.common.util.StringUtil; import org.xipki.datasource.api.DataSourceFactory; import org.xipki.datasource.api.DataSourceWrapper; import org.xipki.datasource.api.exception.DataAccessException; import org.xipki.security.api.ConcurrentContentSigner; import org.xipki.security.api.PasswordResolver; import org.xipki.security.api.PasswordResolverException; import org.xipki.security.api.SecurityFactory; import org.xipki.security.api.SignerException; /** * @author Lijun Liao */ public class CAManagerImpl implements CAManager, CmpResponderManager { private class ScheduledPublishQueueCleaner implements Runnable { private boolean inProcess = false; @Override public void run() { if (inProcess || caSystemSetuped == false) { return; } inProcess = true; try { LOG.debug("publishing certificates in PUBLISHQUEUE"); for (String name : x509cas.keySet()) { X509CA ca = x509cas.get(name); boolean b = ca.publishCertsInQueue(); if (b) { LOG.debug(" published certificates of CA '{}' in PUBLISHQUEUE", name); } else { LOG.error("publishing certificates of CA '{}' in PUBLISHQUEUE failed", name); } } } catch (Throwable t) { } finally { inProcess = false; } } } private class ScheduledDeleteCertsInProcessService implements Runnable { private boolean inProcess = false; @Override public void run() { if (inProcess) { return; } inProcess = true; try { try { // older than 10 minutes certstore.deleteCertsInProcessOlderThan(new Date(System.currentTimeMillis() - 10 * 60 * 1000L)); } catch (Throwable t) { final String message = "could not call certstore.deleteCertsInProcessOlderThan"; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), t.getClass().getName(), t.getMessage()); } LOG.debug(message, t); } } finally { inProcess = false; } } } private class ScheduledCARestarter implements Runnable { private boolean inProcess = false; @Override public void run() { if (inProcess) { return; } inProcess = true; try { SystemEvent event = queryExecutor.getSystemEvent(EVENT_CACHAGNE); long caChangedTime = (event == null) ? 0 : event.getEventTime(); if (LOG.isDebugEnabled()) { LOG.debug("check the restart CA system event: CA changed at={}, lastStartTime={}", new Date(caChangedTime * 1000L), lastStartTime); } if (caChangedTime > lastStartTime.getTime() / 1000L) { LOG.info("received event to restart CA"); restartCaSystem(); } else { LOG.debug("received no event to restart CA"); } } catch (Throwable t) { LOG.error("ScheduledCArestarter: " + t.getMessage(), t); } finally { inProcess = false; } } } private static final Logger LOG = LoggerFactory.getLogger(CAManagerImpl.class); private static final String EVENT_LOCK = "LOCK"; private static final String EVENT_CACHAGNE = "CA_CHANGE"; private final String lockInstanceId; private Map<String, CmpResponderEntry> responderDbEntries = new ConcurrentHashMap<>(); private Map<String, CmpResponderEntryWrapper> responders = new ConcurrentHashMap<>(); private boolean caLockedByMe = false; private boolean masterMode = false; private Map<String, DataSourceWrapper> dataSources = null; private final Map<String, X509CAInfo> caInfos = new ConcurrentHashMap<>(); private final Map<String, IdentifiedX509Certprofile> certprofiles = new ConcurrentHashMap<>(); private final Map<String, CertprofileEntry> certprofileDbEntries = new ConcurrentHashMap<>(); private final Map<String, IdentifiedX509CertPublisher> publishers = new ConcurrentHashMap<>(); private final Map<String, PublisherEntry> publisherDbEntries = new ConcurrentHashMap<>(); private final Map<String, CmpControl> cmpControls = new ConcurrentHashMap<>(); private final Map<String, CmpControlEntry> cmpControlDbEntries = new ConcurrentHashMap<>(); private final Map<String, CmpRequestorEntryWrapper> requestors = new ConcurrentHashMap<>(); private final Map<String, CmpRequestorEntry> requestorDbEntries = new ConcurrentHashMap<>(); private final Map<String, X509CrlSignerEntryWrapper> crlSigners = new ConcurrentHashMap<>(); private final Map<String, X509CrlSignerEntry> crlSignerDbEntries = new ConcurrentHashMap<>(); private final Map<String, Map<String, String>> ca_has_profiles = new ConcurrentHashMap<>(); private final Map<String, Set<String>> ca_has_publishers = new ConcurrentHashMap<>(); private final Map<String, Set<CAHasRequestorEntry>> ca_has_requestors = new ConcurrentHashMap<>(); private final Map<String, String> caAliases = new ConcurrentHashMap<>(); private final DfltEnvironmentParameterResolver envParameterResolver = new DfltEnvironmentParameterResolver(); private ScheduledThreadPoolExecutor persistentScheduledThreadPoolExecutor; private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor; private final Map<String, X509CACmpResponder> x509Responders = new ConcurrentHashMap<>(); private final Map<String, X509CA> x509cas = new ConcurrentHashMap<>(); private String caConfFile; private boolean caSystemSetuped = false; private boolean responderInitialized = false; private boolean requestorsInitialized = false; private boolean caAliasesInitialized = false; private boolean certprofilesInitialized = false; private boolean publishersInitialized = false; private boolean crlSignersInitialized = false; private boolean cmpControlInitialized = false; private boolean cAsInitialized = false; private boolean environmentParametersInitialized = false; private Date lastStartTime; private AuditLoggingServiceRegister auditServiceRegister; private DataSourceWrapper dataSource; private CertificateStore certstore; private SecurityFactory securityFactory; private DataSourceFactory dataSourceFactory; private CAManagerQueryExecutor queryExecutor; public CAManagerImpl() throws ConfigurationException { if (Security.getProvider("BC") == null) { Security.addProvider(new BouncyCastleProvider()); } String calockId = null; File caLockFile = new File("calock"); if (caLockFile.exists()) { try { calockId = new String(IoUtil.read(caLockFile)); } catch (IOException e) { } } if (calockId == null) { calockId = UUID.randomUUID().toString(); try { IoUtil.save(caLockFile, calockId.getBytes()); } catch (IOException e) { } } String hostAddress = null; try { hostAddress = IoUtil.getHostAddress(); } catch (SocketException e) { } this.lockInstanceId = (hostAddress == null) ? calockId : hostAddress + "/" + calockId; } public SecurityFactory getSecurityFactory() { return securityFactory; } public void setSecurityFactory(final SecurityFactory securityFactory) { this.securityFactory = securityFactory; } public DataSourceFactory getDataSourceFactory() { return dataSourceFactory; } public void setDataSourceFactory(final DataSourceFactory dataSourceFactory) { this.dataSourceFactory = dataSourceFactory; } private void init() throws CAMgmtException { if (securityFactory == null) { throw new IllegalStateException("securityFactory is not set"); } if (dataSourceFactory == null) { throw new IllegalStateException("dataSourceFactory is not set"); } if (caConfFile == null) { throw new IllegalStateException("caConfFile is not set"); } Properties caConfProps = new Properties(); try { caConfProps.load(new FileInputStream(IoUtil.expandFilepath(caConfFile))); } catch (IOException e) { throw new CAMgmtException("IOException while parsing ca configuration" + caConfFile, e); } String caModeStr = caConfProps.getProperty("ca.mode"); if (caModeStr != null) { if (caModeStr.equalsIgnoreCase("slave")) { masterMode = false; } else if (caModeStr.equalsIgnoreCase("master")) { masterMode = true; } else { throw new CAMgmtException("invalid ca.mode '" + caModeStr + "'"); } } else { masterMode = true; } if (this.dataSources == null) { this.dataSources = new ConcurrentHashMap<>(); for (Object objKey : caConfProps.keySet()) { String key = (String) objKey; if (StringUtil.startsWithIgnoreCase(key, "datasource.") == false) { continue; } String datasourceFile = caConfProps.getProperty(key); try { String datasourceName = key.substring("datasource.".length()); DataSourceWrapper datasource = dataSourceFactory.createDataSourceForFile(datasourceName, datasourceFile, securityFactory.getPasswordResolver()); Connection conn = datasource.getConnection(); datasource.returnConnection(conn); this.dataSources.put(datasourceName, datasource); } catch (DataAccessException | PasswordResolverException | IOException | RuntimeException e) { throw new CAMgmtException(e.getClass().getName() + " while parsing datasoure " + datasourceFile, e); } } this.dataSource = this.dataSources.get("ca"); } if (this.dataSource == null) { throw new CAMgmtException("no datasource configured with name 'ca'"); } this.queryExecutor = new CAManagerQueryExecutor(this.dataSource); if (masterMode) { boolean lockedSuccessfull; try { lockedSuccessfull = lockCA(true); } catch (DataAccessException e) { throw new CAMgmtException("DataAccessException while locking CA", e); } if (lockedSuccessfull == false) { final String msg = "could not lock the CA database. In general this indicates that another CA software in " + "active mode is accessing the database or the last shutdown of CA software in active mode is abnormal."; throw new CAMgmtException(msg); } } try { this.certstore = new CertificateStore(dataSource); } catch (DataAccessException e) { throw new CAMgmtException(e.getMessage(), e); } initDataObjects(); } @Override public CASystemStatus getCASystemStatus() { if (caSystemSetuped) { return masterMode ? CASystemStatus.STARTED_AS_MASTER : CASystemStatus.STARTED_AS_SLAVE; } else if (initializing) { return CASystemStatus.INITIALIZING; } else if (caLockedByMe == false) { return CASystemStatus.LOCK_FAILED; } else { return CASystemStatus.ERROR; } } private boolean lockCA(boolean forceRelock) throws DataAccessException, CAMgmtException { SystemEvent lockInfo = queryExecutor.getSystemEvent(EVENT_LOCK); if (lockInfo != null) { String lockedBy = lockInfo.getOwner(); Date lockedAt = new Date(lockInfo.getEventTime() * 1000L); if (this.lockInstanceId.equals(lockedBy) == false) { LOG.error("could not lock CA, it has been locked by {} since {}", lockedBy, lockedAt); return false; } if (forceRelock == false) { return true; } else { LOG.info("CA has been locked by me since {}, relock it", lockedAt); } } SystemEvent newLockInfo = new SystemEvent(EVENT_LOCK, lockInstanceId, System.currentTimeMillis() / 1000L); return queryExecutor.changeSystemEvent(newLockInfo); } @Override public boolean unlockCA() { if (masterMode == false) { LOG.error("could not unlock CA in slave mode"); return false; } caLockedByMe = false; boolean successfull = false; try { queryExecutor.unlockCA(); successfull = true; } catch (DataAccessException | CAMgmtException e) { final String message = "error in unlockCA()"; if (LOG.isWarnEnabled()) { LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); } if (successfull) { LOG.info("unlocked CA"); } else { LOG.error("unlocking CA failed"); } auditLogPCIEvent(successfull, "UNLOCK"); return successfull; } private void reset() { caSystemSetuped = false; responderInitialized = false; requestorsInitialized = false; caAliasesInitialized = false; certprofilesInitialized = false; publishersInitialized = false; crlSignersInitialized = false; cmpControlInitialized = false; cAsInitialized = false; environmentParametersInitialized = false; shutdownScheduledThreadPoolExecutor(); } private void initDataObjects() throws CAMgmtException { initEnvironemtParamters(); initCaAliases(); initCertprofiles(); initPublishers(); initCmpControls(); initRequestors(); initResponder(); initCrlSigners(); initCAs(); markLastSeqValues(); } @Override public boolean restartCaSystem() { reset(); boolean caSystemStarted = do_startCaSystem(); if (caSystemStarted == false) { String msg = "could not restart CA system"; LOG.error(msg); } auditLogPCIEvent(caSystemStarted, "CA_CHANGE"); return caSystemStarted; } @Override public boolean notifyCAChange() throws CAMgmtException { try { SystemEvent systemEvent = new SystemEvent(EVENT_CACHAGNE, lockInstanceId, System.currentTimeMillis() / 1000L); queryExecutor.changeSystemEvent(systemEvent); LOG.info("notified the change of CA system"); return true; } catch (CAMgmtException e) { final String message = "error while notifying Slave CAs to restart"; if (LOG.isWarnEnabled()) { LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); return false; } } public void startCaSystem() { boolean caSystemStarted = false; try { caSystemStarted = do_startCaSystem(); } catch (Throwable t) { final String message = "do_startCaSystem()"; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), t.getClass().getName(), t.getMessage()); } LOG.debug(message, t); LOG.error(message); } if (caSystemStarted == false) { String msg = "could not start CA system"; LOG.error(msg); } auditLogPCIEvent(caSystemStarted, "START"); } private boolean initializing = false; private boolean do_startCaSystem() { if (caSystemSetuped) { return true; } initializing = true; shutdownScheduledThreadPoolExecutor(); try { LOG.info("starting CA system"); try { init(); } catch (Exception e) { final String message = "do_startCaSystem().init()"; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); return false; } this.lastStartTime = new Date(); x509cas.clear(); x509Responders.clear(); scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(10); scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true); // Add the CAs to the store for (String caName : caInfos.keySet()) { if (startCA(caName) == false) { return false; } } caSystemSetuped = true; StringBuilder sb = new StringBuilder(); sb.append("started CA system"); Set<String> names = new HashSet<>(getCaNames()); if (names.size() > 0) { sb.append(" with following CAs: "); Set<String> caAliasNames = getCaAliasNames(); for (String aliasName : caAliasNames) { String name = getCaNameForAlias(aliasName); names.remove(name); sb.append(name).append(" (alias ").append(aliasName).append(")").append(", "); } for (String name : names) { sb.append(name).append(", "); } int len = sb.length(); sb.delete(len - 2, len); scheduledThreadPoolExecutor.scheduleAtFixedRate(new ScheduledPublishQueueCleaner(), 120, 120, TimeUnit.SECONDS); scheduledThreadPoolExecutor.scheduleAtFixedRate(new ScheduledDeleteCertsInProcessService(), 120, 120, TimeUnit.SECONDS); } else { sb.append(": no CA is configured"); } LOG.info("{}", sb); } finally { initializing = false; if (masterMode == false && persistentScheduledThreadPoolExecutor == null) { persistentScheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1); persistentScheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true); ScheduledCARestarter caRestarter = new ScheduledCARestarter(); persistentScheduledThreadPoolExecutor.scheduleAtFixedRate(caRestarter, 300, 300, TimeUnit.SECONDS); } } return true; } private boolean startCA(final String caName) { X509CAInfo caEntry = caInfos.get(caName); boolean signerRequired = caEntry.isSignerRequired(); X509CrlSignerEntryWrapper crlSignerEntry = null; String crlSignerName = caEntry.getCrlSignerName(); // CRL will be generated only in master mode if (signerRequired && masterMode && crlSignerName != null) { crlSignerEntry = crlSigners.get(crlSignerName); try { crlSignerEntry.getDbEntry().setConfFaulty(true); crlSignerEntry.initSigner(securityFactory); crlSignerEntry.getDbEntry().setConfFaulty(false); } catch (SignerException | OperationException | ConfigurationException e) { final String message = "X09CrlSignerEntryWrapper.initSigner (name=" + crlSignerName + ")"; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); return false; } } X509CA ca; try { ca = new X509CA(this, caEntry, certstore, securityFactory, masterMode); if (auditServiceRegister != null) { ca.setAuditServiceRegister(auditServiceRegister); } } catch (OperationException e) { final String message = "X509CA.<init> (ca=" + caName + ")"; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); return false; } x509cas.put(caName, ca); X509CACmpResponder caResponder = new X509CACmpResponder(this, caName); x509Responders.put(caName, caResponder); return true; } public void shutdown() { LOG.info("stopping CA system"); shutdownScheduledThreadPoolExecutor(); if (persistentScheduledThreadPoolExecutor != null) { persistentScheduledThreadPoolExecutor.shutdown(); while (persistentScheduledThreadPoolExecutor.isTerminated() == false) { try { Thread.sleep(100); } catch (InterruptedException e) { } } persistentScheduledThreadPoolExecutor = null; } for (String caName : x509cas.keySet()) { X509CA ca = x509cas.get(caName); try { ca.getCAInfo().commitNextSerial(); } catch (Throwable t) { LOG.info("Exception while calling CAInfo.commitNextSerial for CA '{}': {}", caName, t.getMessage()); } } if (caLockedByMe) { unlockCA(); } File caLockFile = new File("calock"); if (caLockFile.exists()) { caLockFile.delete(); } for (String dsName : dataSources.keySet()) { DataSourceWrapper ds = dataSources.get(dsName); try { ds.shutdown(); } catch (Exception e) { final String message = "could not shutdown datasource " + dsName; if (LOG.isWarnEnabled()) { LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); } } LOG.info("stopped CA system"); auditLogPCIEvent(true, "SHUTDOWN"); } @Override public X509CACmpResponder getX509CACmpResponder(final String name) { ParamChecker.assertNotBlank("name", name); return x509Responders.get(name.toUpperCase()); } public ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor() { return scheduledThreadPoolExecutor; } @Override public Set<String> getCertprofileNames() { return certprofileDbEntries.keySet(); } @Override public Set<String> getPublisherNames() { return publisherDbEntries.keySet(); } @Override public Set<String> getCmpRequestorNames() { return requestorDbEntries.keySet(); } @Override public Set<String> getCmpResponderNames() { return responderDbEntries.keySet(); } @Override public Set<String> getCrlSignerNames() { return crlSigners.keySet(); } @Override public Set<String> getCmpControlNames() { return cmpControlDbEntries.keySet(); } @Override public Set<String> getCaNames() { return caInfos.keySet(); } private void initRequestors() throws CAMgmtException { if (requestorsInitialized) { return; } requestorDbEntries.clear(); requestors.clear(); List<String> names = queryExecutor.getNamesFromTable("REQUESTOR"); if (CollectionUtil.isNotEmpty(names)) { for (String name : names) { CmpRequestorEntry requestorDbEntry = queryExecutor.createRequestor(name); if (requestorDbEntry == null) { continue; } requestorDbEntries.put(name, requestorDbEntry); CmpRequestorEntryWrapper requestor = new CmpRequestorEntryWrapper(); requestor.setDbEntry(requestorDbEntry); requestors.put(name, requestor); } } requestorsInitialized = true; } private void initResponder() throws CAMgmtException { if (responderInitialized) { return; } responderDbEntries.clear(); responders.clear(); List<String> names = queryExecutor.getNamesFromTable("RESPONDER"); if (CollectionUtil.isNotEmpty(names)) { for (String name : names) { CmpResponderEntry dbEntry = queryExecutor.createResponder(name); if (dbEntry == null) { continue; } dbEntry.setConfFaulty(true); responderDbEntries.put(name, dbEntry); CmpResponderEntryWrapper responder = createCmpResponder(dbEntry); if (responder != null) { dbEntry.setConfFaulty(false); responders.put(name, responder); } } } responderInitialized = true; } private void initEnvironemtParamters() throws CAMgmtException { if (environmentParametersInitialized) { return; } Map<String, String> map = queryExecutor.createEnvParameters(); envParameterResolver.clear(); for (String name : map.keySet()) { envParameterResolver.addEnvParam(name, map.get(name)); } environmentParametersInitialized = true; } private void initCaAliases() throws CAMgmtException { if (caAliasesInitialized) { return; } Map<String, String> map = queryExecutor.createCaAliases(); caAliases.clear(); for (String aliasName : map.keySet()) { caAliases.put(aliasName, map.get(aliasName)); } caAliasesInitialized = true; } private void initCertprofiles() throws CAMgmtException { if (certprofilesInitialized) { return; } for (String name : certprofiles.keySet()) { shutdownCertprofile(certprofiles.get(name)); } certprofileDbEntries.clear(); certprofiles.clear(); List<String> names = queryExecutor.getNamesFromTable("PROFILE"); if (CollectionUtil.isNotEmpty(names)) { for (String name : names) { CertprofileEntry dbEntry = queryExecutor.createCertprofile(name); if (dbEntry != null) { dbEntry.setFaulty(true); certprofileDbEntries.put(name, dbEntry); } IdentifiedX509Certprofile profile = createCertprofile(dbEntry); if (profile != null) { dbEntry.setFaulty(false); certprofiles.put(name, profile); } } } certprofilesInitialized = true; } private void initPublishers() throws CAMgmtException { if (publishersInitialized) { return; } for (String name : publishers.keySet()) { shutdownPublisher(publishers.get(name)); } publishers.clear(); publisherDbEntries.clear(); List<String> names = queryExecutor.getNamesFromTable("PUBLISHER"); if (CollectionUtil.isNotEmpty(names)) { for (String name : names) { PublisherEntry dbEntry = queryExecutor.createPublisher(name); if (dbEntry == null) { continue; } dbEntry.setFaulty(true); publisherDbEntries.put(name, dbEntry); IdentifiedX509CertPublisher publisher = createPublisher(dbEntry); if (publisher != null) { dbEntry.setFaulty(false); publishers.put(name, publisher); } } } publishersInitialized = true; } private void initCrlSigners() throws CAMgmtException { if (crlSignersInitialized) { return; } crlSigners.clear(); crlSignerDbEntries.clear(); List<String> names = queryExecutor.getNamesFromTable("CRLSIGNER"); if (CollectionUtil.isNotEmpty(names)) { for (String name : names) { X509CrlSignerEntry dbEntry = queryExecutor.createCrlSigner(name); if (dbEntry == null) { continue; } crlSignerDbEntries.put(name, dbEntry); X509CrlSignerEntryWrapper crlSigner = createX509CrlSigner(dbEntry); crlSigners.put(name, crlSigner); } } crlSignersInitialized = true; } private void initCmpControls() throws CAMgmtException { if (cmpControlInitialized) { return; } cmpControls.clear(); cmpControlDbEntries.clear(); List<String> names = queryExecutor.getNamesFromTable("CMPCONTROL"); if (CollectionUtil.isNotEmpty(names)) { for (String name : names) { CmpControlEntry cmpControlDb = queryExecutor.createCmpControl(name); if (cmpControlDb == null) { continue; } cmpControlDb.setFaulty(true); cmpControlDbEntries.put(name, cmpControlDb); CmpControl cmpControl; try { cmpControl = new CmpControl(cmpControlDb); cmpControlDb.setFaulty(false); cmpControls.put(name, cmpControl); } catch (ConfigurationException e) { final String message = "could not initialize CMP control " + name + ", ignore it"; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); } } } cmpControlInitialized = true; } private void initCAs() throws CAMgmtException { if (cAsInitialized) { return; } caInfos.clear(); ca_has_requestors.clear(); ca_has_publishers.clear(); ca_has_profiles.clear(); List<String> names = queryExecutor.getNamesFromTable("CA"); if (CollectionUtil.isNotEmpty(names)) { for (String name : names) { createCA(name); } } cAsInitialized = true; } private boolean createCA(final String name) throws CAMgmtException { caInfos.remove(name); ca_has_profiles.remove(name); ca_has_publishers.remove(name); ca_has_requestors.remove(name); X509CA oldCa = x509cas.remove(name); x509Responders.remove(name); if (oldCa != null) { oldCa.shutdown(); } X509CAInfo ca = queryExecutor.createCAInfo(name, masterMode, certstore); try { ca.markMaxSerial(); } catch (OperationException e) { throw new CAMgmtException(e.getMessage(), e); } caInfos.put(name, ca); Set<CAHasRequestorEntry> caHasRequestors = queryExecutor.createCAhasRequestors(name); ca_has_requestors.put(name, caHasRequestors); Map<String, String> profileNames = queryExecutor.createCAhasProfiles(name); ca_has_profiles.put(name, profileNames); Set<String> publisherNames = queryExecutor.createCAhasPublishers(name); ca_has_publishers.put(name, publisherNames); return true; } private void markLastSeqValues() throws CAMgmtException { try { // sequence DCC_ID long maxId = dataSource.getMax(null, "DELTACRL_CACHE", "ID"); dataSource.setLastUsedSeqValue("DCC_ID", maxId); // sequence CERT_ID maxId = dataSource.getMax(null, "CERT", "ID"); dataSource.setLastUsedSeqValue("CERT_ID", maxId); } catch (DataAccessException e) { throw new CAMgmtException(e.getMessage(), e); } } @Override public boolean addCA(final CAEntry caEntry) throws CAMgmtException { ParamChecker.assertNotNull("caEntry", caEntry); asssertMasterMode(); String name = caEntry.getName(); if (caInfos.containsKey(name)) { throw new CAMgmtException("CA named " + name + " exists"); } if (caEntry instanceof X509CAEntry) { X509CAEntry xEntry = (X509CAEntry) caEntry; ConcurrentContentSigner signer; try { List<String[]> signerConfs = splitCASignerConfs(xEntry.getSignerConf()); for (String[] m : signerConfs) { String signerConf = m[1]; signer = securityFactory.createSigner(xEntry.getSignerType(), signerConf, xEntry.getCertificate()); if (xEntry.getCertificate() == null) { xEntry.setCertificate(signer.getCertificate()); } } } catch (SignerException e) { throw new CAMgmtException("could not create signer for new CA " + name + ": " + e.getMessage(), e); } } queryExecutor.addCA(caEntry); createCA(name); startCA(name); return true; } @Override public X509CAEntry getCA(final String name) { ParamChecker.assertNotBlank("name", name); X509CAInfo caInfo = caInfos.get(name.toUpperCase()); return caInfo == null ? null : caInfo.getCaEntry(); } @Override public boolean changeCA(final ChangeCAEntry entry) throws CAMgmtException { ParamChecker.assertNotNull("entry", entry); asssertMasterMode(); String name = entry.getName(); boolean changed = queryExecutor.changeCA(entry, securityFactory); if (changed == false) { LOG.info("no change of CA '{}' is processed", name); } else { createCA(name); startCA(name); } return changed; } @Override public boolean removeCertprofileFromCA(final String profileLocalname, String caName) throws CAMgmtException { ParamChecker.assertNotBlank("profileLocalname", profileLocalname); ParamChecker.assertNotBlank("caName", caName); asssertMasterMode(); caName = caName.toUpperCase(); boolean b = queryExecutor.removeCertprofileFromCA(profileLocalname, caName); if (b == false) { return false; } if (ca_has_profiles.containsKey(caName)) { Map<String, String> map = ca_has_profiles.get(caName); if (map != null) { map.remove(profileLocalname); } } return true; } @Override public boolean addCertprofileToCA(final String profileName, String profileLocalname, String caName) throws CAMgmtException { ParamChecker.assertNotBlank("profileName", profileName); ParamChecker.assertNotBlank("caName", caName); asssertMasterMode(); if (StringUtil.isBlank(profileLocalname)) { profileLocalname = profileName; } caName = caName.toUpperCase(); Map<String, String> map = ca_has_profiles.get(caName); if (map == null) { map = new HashMap<>(); ca_has_profiles.put(caName, map); } else { if (map.containsKey(profileLocalname)) { return false; } } if (certprofiles.containsKey(profileName) == false) { throw new CAMgmtException("cerptofile '" + profileName + "' is faulty"); } queryExecutor.addCertprofileToCA(profileName, profileLocalname, caName); map.put(profileLocalname, profileName); return true; } @Override public boolean removePublisherFromCA(final String publisherName, String caName) throws CAMgmtException { ParamChecker.assertNotBlank("publisherName", publisherName); ParamChecker.assertNotBlank("caName", caName); asssertMasterMode(); caName = caName.toUpperCase(); boolean b = queryExecutor.removePublisherFromCA(publisherName, caName); if (b == false) { return false; } Set<String> publisherNames = ca_has_publishers.get(caName); if (publisherNames != null) { publisherNames.remove(publisherName); } return true; } @Override public boolean addPublisherToCA(final String publisherName, String caName) throws CAMgmtException { ParamChecker.assertNotBlank("publisherName", publisherName); ParamChecker.assertNotBlank("caName", caName); asssertMasterMode(); caName = caName.toUpperCase(); Set<String> publisherNames = ca_has_publishers.get(caName); if (publisherNames == null) { publisherNames = new HashSet<>(); ca_has_publishers.put(caName, publisherNames); } else { if (publisherNames.contains(publisherName)) { return false; } } IdentifiedX509CertPublisher publisher = publishers.get(publisherName); if (publisher == null) { throw new CAMgmtException("publisher '" + publisherName + "' is faulty"); } queryExecutor.addPublisherToCA(publisherName, caName); publisherNames.add(publisherName); ca_has_publishers.get(caName).add(publisherName); publisher.issuerAdded(caInfos.get(caName).getCertificate()); return true; } @Override public Map<String, String> getCertprofilesForCA(final String caName) { ParamChecker.assertNotBlank("caName", caName); return ca_has_profiles.get(caName.toUpperCase()); } @Override public Set<CAHasRequestorEntry> getCmpRequestorsForCA(final String caName) { ParamChecker.assertNotBlank("caName", caName); return ca_has_requestors.get(caName.toUpperCase()); } @Override public CmpRequestorEntry getCmpRequestor(final String name) { ParamChecker.assertNotBlank("name", name); return requestorDbEntries.get(name); } public CmpRequestorEntryWrapper getCmpRequestorWrapper(final String name) { ParamChecker.assertNotBlank("name", name); return requestors.get(name); } @Override public boolean addCmpRequestor(final CmpRequestorEntry dbEntry) throws CAMgmtException { ParamChecker.assertNotNull("dbEntry", dbEntry); asssertMasterMode(); String name = dbEntry.getName(); if (requestorDbEntries.containsKey(name)) { return false; } CmpRequestorEntryWrapper requestor = new CmpRequestorEntryWrapper(); requestor.setDbEntry(dbEntry); queryExecutor.addCmpRequestor(dbEntry); requestorDbEntries.put(name, dbEntry); requestors.put(name, requestor); try { certstore.addRequestorName(name); } catch (OperationException e) { final String message = "exception while publishing requestor name to certStore"; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); throw new CAMgmtException(message + ": " + e.getErrorCode() + ", " + e.getMessage()); } return true; } @Override public boolean removeCmpRequestor(final String requestorName) throws CAMgmtException { ParamChecker.assertNotBlank("requestorName", requestorName); asssertMasterMode(); for (String caName : ca_has_requestors.keySet()) { removeCmpRequestorFromCA(requestorName, caName); } boolean b = queryExecutor.deleteRowWithName(requestorName, "REQUESTOR"); if (b == false) { return false; } requestorDbEntries.remove(requestorName); requestors.remove(requestorName); LOG.info("removed requestor '{}'", requestorName); return true; } @Override public boolean changeCmpRequestor(final String name, final String base64Cert) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); asssertMasterMode(); if (base64Cert == null) { return false; } CmpRequestorEntryWrapper requestor = queryExecutor.changeCmpRequestor(name, base64Cert); if (requestor == null) { return false; } requestorDbEntries.remove(name); requestors.remove(name); requestorDbEntries.put(name, requestor.getDbEntry()); requestors.put(name, requestor); return true; } @Override public boolean removeCmpRequestorFromCA(final String requestorName, String caName) throws CAMgmtException { ParamChecker.assertNotBlank("requestorName", requestorName); ParamChecker.assertNotBlank("caName", caName); asssertMasterMode(); caName = caName.toUpperCase(); boolean b = queryExecutor.removeCmpRequestorFromCA(requestorName, caName); if (b && ca_has_requestors.containsKey(caName)) { Set<CAHasRequestorEntry> entries = ca_has_requestors.get(caName); CAHasRequestorEntry entry = null; for (CAHasRequestorEntry m : entries) { if (m.getRequestorName().equals(requestorName)) { entry = m; } } entries.remove(entry); } return b; } @Override public boolean addCmpRequestorToCA(final CAHasRequestorEntry requestor, String caName) throws CAMgmtException { ParamChecker.assertNotNull("requestor", requestor); ParamChecker.assertNotBlank("caName", caName); asssertMasterMode(); caName = caName.toUpperCase(); String requestorName = requestor.getRequestorName(); Set<CAHasRequestorEntry> cmpRequestors = ca_has_requestors.get(caName); if (cmpRequestors == null) { cmpRequestors = new HashSet<>(); ca_has_requestors.put(caName, cmpRequestors); } else { boolean foundEntry = false; for (CAHasRequestorEntry entry : cmpRequestors) { if (entry.getRequestorName().equals(requestorName)) { foundEntry = true; break; } } // already added if (foundEntry) { return false; } } cmpRequestors.add(requestor); queryExecutor.addCmpRequestorToCA(requestor, caName); ca_has_requestors.get(caName).add(requestor); return true; } @Override public CertprofileEntry getCertprofile(final String profileName) { return certprofileDbEntries.get(profileName); } @Override public boolean removeCertprofile(final String profileName) throws CAMgmtException { ParamChecker.assertNotBlank("profileName", profileName); asssertMasterMode(); for (String caName : ca_has_profiles.keySet()) { removeCertprofileFromCA(profileName, caName); } boolean b = queryExecutor.deleteRowWithName(profileName, "PROFILE"); if (b == false) { return false; } LOG.info("removed profile '{}'", profileName); certprofileDbEntries.remove(profileName); IdentifiedX509Certprofile profile = certprofiles.remove(profileName); shutdownCertprofile(profile); return true; } @Override public boolean changeCertprofile(final String name, final String type, final String conf) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); if (type == null && conf == null) { return false; } asssertMasterMode(); IdentifiedX509Certprofile profile = queryExecutor.changeCertprofile(name, type, conf, this); if (profile == null) { return false; } certprofileDbEntries.remove(name); IdentifiedX509Certprofile oldProfile = certprofiles.remove(name); certprofileDbEntries.put(name, profile.getDbEntry()); certprofiles.put(name, profile); if (oldProfile != null) { shutdownCertprofile(oldProfile); } return true; } @Override public boolean addCertprofile(final CertprofileEntry dbEntry) throws CAMgmtException { ParamChecker.assertNotNull("dbEntry", dbEntry); asssertMasterMode(); String name = dbEntry.getName(); if (certprofileDbEntries.containsKey(name)) { return false; } dbEntry.setFaulty(true); IdentifiedX509Certprofile profile = createCertprofile(dbEntry); if (profile == null) { return false; } dbEntry.setFaulty(false); certprofiles.put(name, profile); queryExecutor.addCertprofile(dbEntry); certprofileDbEntries.put(name, dbEntry); try { certstore.addCertprofileName(name); } catch (OperationException e) { final String message = "exception while publishing certprofile name to certStore"; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); } return true; } @Override public boolean addCmpResponder(final CmpResponderEntry dbEntry) throws CAMgmtException { ParamChecker.assertNotNull("dbEntry", dbEntry); asssertMasterMode(); String name = dbEntry.getName(); if (crlSigners.containsKey(name)) { return false; } CmpResponderEntryWrapper _responder = createCmpResponder(dbEntry); queryExecutor.addCmpResponder(dbEntry); responders.put(name, _responder); responderDbEntries.put(name, dbEntry); return true; } @Override public boolean removeCmpResponder(final String name) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); asssertMasterMode(); boolean b = queryExecutor.deleteRowWithName(name, "RESPONDER"); if (b == false) { return false; } for (String caName : caInfos.keySet()) { X509CAInfo caInfo = caInfos.get(caName); if (name.equals(caInfo.getResponderName())) { caInfo.setResponderName(null); } } responderDbEntries.remove(name); responders.remove(name); LOG.info("removed Responder '{}'", name); return true; } @Override public boolean changeCmpResponder(final String name, final String type, final String conf, final String base64Cert) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); asssertMasterMode(); if (type == null && conf == null && base64Cert == null) { return false; } CmpResponderEntryWrapper newResponder = queryExecutor.changeCmpResponder(name, type, conf, base64Cert, this); if (newResponder == null) { return false; } responders.remove(name); responderDbEntries.remove(name); responderDbEntries.put(name, newResponder.getDbEntry()); responders.put(name, newResponder); return true; } @Override public CmpResponderEntry getCmpResponder(final String name) { return responderDbEntries.get(name); } public CmpResponderEntryWrapper getCmpResponderWrapper(final String name) { return responders.get(name); } @Override public boolean addCrlSigner(final X509CrlSignerEntry dbEntry) throws CAMgmtException { ParamChecker.assertNotNull("dbEntry", dbEntry); asssertMasterMode(); String name = dbEntry.getName(); if (crlSigners.containsKey(name)) { return false; } X509CrlSignerEntryWrapper crlSigner = createX509CrlSigner(dbEntry); X509CrlSignerEntry _dbEntry = crlSigner.getDbEntry(); queryExecutor.addCrlSigner(_dbEntry); crlSigners.put(name, crlSigner); crlSignerDbEntries.put(name, _dbEntry); return true; } @Override public boolean removeCrlSigner(final String name) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); asssertMasterMode(); boolean b = queryExecutor.deleteRowWithName(name, "CRLSIGNER"); if (b == false) { return false; } for (String caName : caInfos.keySet()) { X509CAInfo caInfo = caInfos.get(caName); if (name.equals(caInfo.getCrlSignerName())) { caInfo.setCrlSignerName(null); } } crlSigners.remove(name); crlSignerDbEntries.remove(name); LOG.info("removed CRLSigner '{}'", name); return true; } @Override public boolean changeCrlSigner(final X509ChangeCrlSignerEntry dbEntry) throws CAMgmtException { ParamChecker.assertNotNull("dbEntry", dbEntry); asssertMasterMode(); String name = dbEntry.getName(); String signer_type = dbEntry.getSignerType(); String signer_conf = dbEntry.getSignerConf(); String signer_cert = dbEntry.getBase64Cert(); String crlControl = dbEntry.getCrlControl(); X509CrlSignerEntryWrapper crlSigner = queryExecutor.changeCrlSigner(name, signer_type, signer_conf, signer_cert, crlControl, this); if (crlSigner == null) { return false; } crlSigners.remove(name); crlSignerDbEntries.remove(name); crlSignerDbEntries.put(name, crlSigner.getDbEntry()); crlSigners.put(name, crlSigner); return true; } @Override public X509CrlSignerEntry getCrlSigner(final String name) { ParamChecker.assertNotBlank("name", name); return crlSignerDbEntries.get(name); } public X509CrlSignerEntryWrapper getCrlSignerWrapper(final String name) { return crlSigners.get(name); } @Override public boolean addPublisher(final PublisherEntry dbEntry) throws CAMgmtException { ParamChecker.assertNotNull("dbEntry", dbEntry); asssertMasterMode(); String name = dbEntry.getName(); if (publisherDbEntries.containsKey(name)) { return false; } dbEntry.setFaulty(true); IdentifiedX509CertPublisher publisher = createPublisher(dbEntry); if (publisher == null) { return false; } dbEntry.setFaulty(false); queryExecutor.addPublisher(dbEntry); publisherDbEntries.put(name, dbEntry); publishers.put(name, publisher); try { certstore.addPublisherName(name); } catch (OperationException e) { final String message = "exception while publishing publisher nameto certStore"; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); } return true; } @Override public List<PublisherEntry> getPublishersForCA(final String caName) { ParamChecker.assertNotBlank("caName", caName); Set<String> publisherNames = ca_has_publishers.get(caName.toUpperCase()); if (publisherNames == null) { return Collections.emptyList(); } List<PublisherEntry> ret = new ArrayList<>(publisherNames.size()); for (String publisherName : publisherNames) { ret.add(publisherDbEntries.get(publisherName)); } return ret; } @Override public PublisherEntry getPublisher(final String name) { ParamChecker.assertNotBlank("name", name); return publisherDbEntries.get(name); } @Override public boolean removePublisher(final String name) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); asssertMasterMode(); for (String caName : ca_has_publishers.keySet()) { removePublisherFromCA(name, caName); } boolean b = queryExecutor.deleteRowWithName(name, "PUBLISHER"); if (b == false) { return false; } LOG.info("removed publisher '{}'", name); publisherDbEntries.remove(name); IdentifiedX509CertPublisher publisher = publishers.remove(name); shutdownPublisher(publisher); return true; } @Override public boolean changePublisher(final String name, final String type, final String conf) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); asssertMasterMode(); if (type == null && conf == null) { return false; } IdentifiedX509CertPublisher publisher = queryExecutor.changePublisher(name, type, conf, this); if (publisher == null) { return false; } IdentifiedX509CertPublisher oldPublisher = publishers.remove(name); if (publisher != null) { shutdownPublisher(oldPublisher); } publisherDbEntries.put(name, publisher.getDbEntry()); publishers.put(name, publisher); return true; } @Override public CmpControlEntry getCmpControl(final String name) { ParamChecker.assertNotBlank("name", name); return cmpControlDbEntries.get(name); } public CmpControl getCmpControlObject(final String name) { ParamChecker.assertNotBlank("name", name); return cmpControls.get(name); } @Override public boolean addCmpControl(final CmpControlEntry dbEntry) throws CAMgmtException { ParamChecker.assertNotNull("dbEntry", dbEntry); asssertMasterMode(); final String name = dbEntry.getName(); if (cmpControlDbEntries.containsKey(name)) { return false; } CmpControl cmpControl; try { cmpControl = new CmpControl(dbEntry); } catch (ConfigurationException e) { final String message = "exception while adding CMP requestor to certStore"; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); return false; } CmpControlEntry _dbEntry = cmpControl.getDbEntry(); queryExecutor.addCmpControl(_dbEntry); cmpControls.put(name, cmpControl); cmpControlDbEntries.put(name, _dbEntry); return true; } @Override public boolean removeCmpControl(final String name) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); asssertMasterMode(); boolean b = queryExecutor.deleteRowWithName(name, "CMPCONTROL"); if (b == false) { return false; } for (String caName : caInfos.keySet()) { X509CAInfo caInfo = caInfos.get(caName); if (name.equals(caInfo.getCmpControlName())) { caInfo.setCmpControlName(null); } } cmpControlDbEntries.remove(name); cmpControls.remove(name); LOG.info("removed CMPControl '{}'", name); return true; } @Override public boolean changeCmpControl(final String name, final String conf) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); ParamChecker.assertNotBlank("conf", conf); asssertMasterMode(); CmpControl newCmpControl = queryExecutor.changeCmpControl(name, conf); if (newCmpControl == null) { return false; } cmpControlDbEntries.put(name, newCmpControl.getDbEntry()); cmpControls.put(name, newCmpControl); return true; } public EnvironmentParameterResolver getEnvParameterResolver() { return envParameterResolver; } @Override public Set<String> getEnvParamNames() { return envParameterResolver.getAllParameterNames(); } @Override public String getEnvParam(final String name) { ParamChecker.assertNotBlank("name", name); return envParameterResolver.getEnvParam(name); } @Override public boolean addEnvParam(final String name, final String value) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); ParamChecker.assertNotBlank("value", value); asssertMasterMode(); if (envParameterResolver.getEnvParam(name) != null) { return false; } queryExecutor.addEnvParam(name, value); envParameterResolver.addEnvParam(name, value); return true; } @Override public boolean removeEnvParam(final String name) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); asssertMasterMode(); boolean b = queryExecutor.deleteRowWithName(name, "ENVIRONMENT"); if (b == false) { return false; } LOG.info("removed environment param '{}'", name); envParameterResolver.removeEnvParam(name); return true; } @Override public boolean changeEnvParam(final String name, final String value) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); ParamChecker.assertNotNull("value", value); asssertMasterMode(); assertNotNULL("value", value); if (envParameterResolver.getEnvParam(name) == null) { throw new CAMgmtException("could not find environment paramter " + name); } boolean changed = queryExecutor.changeEnvParam(name, value); if (changed == false) { return false; } envParameterResolver.addEnvParam(name, value); return true; } public String getCaConfFile() { return caConfFile; } public void setCaConfFile(final String caConfFile) { this.caConfFile = caConfFile; } @Override public boolean addCaAlias(final String aliasName, String caName) throws CAMgmtException { ParamChecker.assertNotBlank("aliasName", aliasName); ParamChecker.assertNotBlank("caName", caName); asssertMasterMode(); caName = caName.toUpperCase(); if (caAliases.get(aliasName) != null) { return false; } queryExecutor.addCaAlias(aliasName, caName); caAliases.put(aliasName, caName); return true; } @Override public boolean removeCaAlias(final String name) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); asssertMasterMode(); boolean b = queryExecutor.removeCaAlias(name); if (b == false) { return false; } caAliases.remove(name); return true; } @Override public String getCaNameForAlias(final String aliasName) { ParamChecker.assertNotBlank("aliasName", aliasName); return caAliases.get(aliasName); } @Override public Set<String> getAliasesForCA(String caName) { ParamChecker.assertNotBlank("caName", caName); caName = caName.toUpperCase(); Set<String> aliases = new HashSet<>(); for (String alias : caAliases.keySet()) { String thisCaName = caAliases.get(alias); if (thisCaName.equals(caName)) { aliases.add(alias); } } return aliases; } @Override public Set<String> getCaAliasNames() { return caAliases.keySet(); } @Override public boolean removeCA(String name) throws CAMgmtException { ParamChecker.assertNotBlank("name", name); asssertMasterMode(); name = name.toUpperCase(); boolean b = queryExecutor.removeCA(name); if (b == false) { return false; } CAMgmtException exception = null; X509CAInfo caInfo = caInfos.get(name); if (caInfo != null && caInfo.getCaEntry().getNextSerial() > 0) { // drop the serial number sequence final String sequenceName = caInfo.getCaEntry().getSerialSeqName(); try { dataSource.dropSequence(sequenceName); } catch (DataAccessException e) { final String message = "error in dropSequence " + sequenceName; if (LOG.isWarnEnabled()) { LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); if (exception == null) { exception = new CAMgmtException(e.getMessage(), e); } } } LOG.info("removed CA '{}'", name); caInfos.remove(name); ca_has_profiles.remove(name); ca_has_publishers.remove(name); ca_has_requestors.remove(name); X509CA ca = x509cas.remove(name); x509Responders.remove(name); if (ca != null) { ca.shutdown(); } if (exception != null) { throw exception; } return true; } @Override public boolean publishRootCA(String caName, final String certprofile) throws CAMgmtException { ParamChecker.assertNotBlank("caName", caName); ParamChecker.assertNotBlank("certprofile", certprofile); asssertMasterMode(); caName = caName.toUpperCase(); X509CA ca = x509cas.get(caName); if (ca == null) { throw new CAMgmtException("could not find CA named " + caName); } X509CertWithDBCertId certInfo = ca.getCAInfo().getCertificate(); if (certInfo.getCert().getSubjectX500Principal() .equals(certInfo.getCert().getIssuerX500Principal()) == false) { throw new CAMgmtException("CA named " + caName + " is not a self-signed CA"); } byte[] encodedSubjectPublicKey = certInfo.getCert().getPublicKey().getEncoded(); X509CertificateInfo ci; try { ci = new X509CertificateInfo(certInfo, certInfo, encodedSubjectPublicKey, certprofile == null ? "UNKNOWN" : certprofile); } catch (CertificateEncodingException e) { throw new CAMgmtException(e.getMessage(), e); } ca.publishCertificate(ci); return true; } @Override public boolean republishCertificates(String caName, final List<String> publisherNames) throws CAMgmtException { ParamChecker.assertNotBlank("caName", caName); ParamChecker.assertNotEmpty("publisherNames", publisherNames); asssertMasterMode(); Set<String> caNames; if (caName == null) { caNames = x509cas.keySet(); } else { caName = caName.toUpperCase(); caNames = new HashSet<>(); caNames.add(caName); } for (String name : caNames) { X509CA ca = x509cas.get(name); if (ca == null) { throw new CAMgmtException("could not find CA named " + name); } boolean successfull = ca.republishCertificates(publisherNames); if (successfull == false) { throw new CAMgmtException("republishing certificates of CA " + name + " failed"); } } return true; } @Override public boolean revokeCa(String name, final CertRevocationInfo revocationInfo) throws CAMgmtException { ParamChecker.assertNotBlank("caName", name); ParamChecker.assertNotNull("revocationInfo", revocationInfo); asssertMasterMode(); ParamChecker.assertNotBlank("caName", name); ParamChecker.assertNotNull("revocationInfo", revocationInfo); name = name.toUpperCase(); if (x509cas.containsKey(name) == false) { return false; } LOG.info("revoking CA '{}'", name); X509CA ca = x509cas.get(name); CertRevocationInfo currentRevInfo = ca.getCAInfo().getRevocationInfo(); if (currentRevInfo != null) { CRLReason currentReason = currentRevInfo.getReason(); if (currentReason != CRLReason.CERTIFICATE_HOLD) { throw new CAMgmtException("CA " + name + " has been revoked with reason " + currentReason.name()); } } boolean b = queryExecutor.revokeCa(name, revocationInfo); if (b == false) { return false; } try { ca.revoke(revocationInfo); } catch (OperationException e) { throw new CAMgmtException("error while revoking CA " + e.getMessage(), e); } LOG.info("revoked CA '{}'", name); auditLogPCIEvent(true, "REVOKE CA " + name); return true; } @Override public boolean unrevokeCa(String name) throws CAMgmtException { ParamChecker.assertNotBlank("caName", name); asssertMasterMode(); name = name.toUpperCase(); if (x509cas.containsKey(name) == false) { throw new CAMgmtException("could not find CA named " + name); } LOG.info("unrevoking of CA '{}'", name); boolean b = queryExecutor.unrevokeCa(name); if (b == false) { return false; } X509CA ca = x509cas.get(name); try { ca.unrevoke(); } catch (OperationException e) { throw new CAMgmtException("error while unrevoking of CA " + e.getMessage(), e); } LOG.info("unrevoked CA '{}'", name); auditLogPCIEvent(true, "UNREVOKE CA " + name); return true; } public void setAuditServiceRegister(final AuditLoggingServiceRegister serviceRegister) { this.auditServiceRegister = serviceRegister; for (String name : publishers.keySet()) { IdentifiedX509CertPublisher publisherEntry = publishers.get(name); publisherEntry.setAuditServiceRegister(auditServiceRegister); } for (String name : x509cas.keySet()) { X509CA ca = x509cas.get(name); ca.setAuditServiceRegister(serviceRegister); } } private void auditLogPCIEvent(final boolean successfull, final String eventType) { AuditLoggingService auditLoggingService = auditServiceRegister == null ? null : auditServiceRegister.getAuditLoggingService(); if (auditLoggingService == null) { return; } PCIAuditEvent auditEvent = new PCIAuditEvent(new Date()); auditEvent.setUserId("CA-SYSTEM"); auditEvent.setEventType(eventType); auditEvent.setAffectedResource("CORE"); if (successfull) { auditEvent.setStatus(AuditStatus.SUCCESSFUL.name()); auditEvent.setLevel(AuditLevel.INFO); } else { auditEvent.setStatus(AuditStatus.FAILED.name()); auditEvent.setLevel(AuditLevel.ERROR); } auditLoggingService.logEvent(auditEvent); } @Override public boolean clearPublishQueue(String caName, final List<String> publisherNames) throws CAMgmtException { asssertMasterMode(); if (caName == null) { try { certstore.clearPublishQueue((X509CertWithDBCertId) null, (String) null); return true; } catch (OperationException e) { throw new CAMgmtException(e.getMessage(), e); } } caName = caName.toUpperCase(); X509CA ca = x509cas.get(caName); if (ca == null) { throw new CAMgmtException("could not find CA named " + caName); } return ca.clearPublishQueue(publisherNames); } private void shutdownScheduledThreadPoolExecutor() { if (scheduledThreadPoolExecutor == null) { return; } scheduledThreadPoolExecutor.shutdown(); while (scheduledThreadPoolExecutor.isTerminated() == false) { try { Thread.sleep(100); } catch (InterruptedException e) { } } scheduledThreadPoolExecutor = null; } @Override public boolean revokeCertificate(final String caName, final BigInteger serialNumber, final CRLReason reason, final Date invalidityTime) throws CAMgmtException { ParamChecker.assertNotBlank("caName", caName); ParamChecker.assertNotNull("serialNumber", serialNumber); X509CA ca = getX509CA(caName); try { return ca.revokeCertificate(serialNumber, reason, invalidityTime) != null; } catch (OperationException e) { throw new CAMgmtException(e.getMessage(), e); } } @Override public boolean unrevokeCertificate(final String caName, final BigInteger serialNumber) throws CAMgmtException { ParamChecker.assertNotBlank("caName", caName); ParamChecker.assertNotNull("serialNumber", serialNumber); X509CA ca = getX509CA(caName); try { return ca.unrevokeCertificate(serialNumber) != null; } catch (OperationException e) { throw new CAMgmtException(e.getMessage(), e); } } @Override public boolean removeCertificate(final String caName, final BigInteger serialNumber) throws CAMgmtException { ParamChecker.assertNotBlank("caName", caName); ParamChecker.assertNotNull("serialNumber", serialNumber); asssertMasterMode(); X509CA ca = getX509CA(caName); if (ca == null) { return false; } try { return ca.removeCertificate(serialNumber) != null; } catch (OperationException e) { throw new CAMgmtException(e.getMessage(), e); } } @Override public X509Certificate generateCertificate(final String caName, final String profileName, final String user, final byte[] encodedPkcs10Request) throws CAMgmtException { ParamChecker.assertNotBlank("caName", caName); ParamChecker.assertNotBlank("profileName", profileName); ParamChecker.assertNotNull("encodedPkcs10Request", encodedPkcs10Request); X509CA ca = getX509CA(caName); CertificationRequest p10cr; try { p10cr = CertificationRequest.getInstance(encodedPkcs10Request); } catch (Exception e) { throw new CAMgmtException("invalid PKCS#10 request. ERROR: " + e.getMessage()); } if (securityFactory.verifyPOPO(p10cr) == false) { throw new CAMgmtException("could not validate POP for the pkcs#10 requst"); } CertificationRequestInfo certTemp = p10cr.getCertificationRequestInfo(); Extensions extensions = null; ASN1Set attrs = certTemp.getAttributes(); for (int i = 0; i < attrs.size(); i++) { Attribute attr = Attribute.getInstance(attrs.getObjectAt(i)); if (PKCSObjectIdentifiers.pkcs_9_at_extensionRequest.equals(attr.getAttrType())) { extensions = Extensions.getInstance(attr.getAttributeValues()[0]); } } X500Name subject = certTemp.getSubject(); SubjectPublicKeyInfo publicKeyInfo = certTemp.getSubjectPublicKeyInfo(); X509CertificateInfo certInfo; try { certInfo = ca.generateCertificate(false, null, profileName, user, subject, publicKeyInfo, null, null, extensions); } catch (OperationException e) { throw new CAMgmtException(e.getMessage(), e); } return certInfo.getCert().getCert(); } public X509CA getX509CA(final String name) throws CAMgmtException { X509CA ca = x509cas.get(name.toUpperCase()); if (ca == null) { throw new CAMgmtException("unknown CA " + name); } return ca; } public IdentifiedX509Certprofile getIdentifiedCertprofile(final String profileName) { return certprofiles.get(profileName); } public List<IdentifiedX509CertPublisher> getIdentifiedPublishersForCa(String caName) { ParamChecker.assertNotBlank("caName", caName); caName = caName.toUpperCase(); List<IdentifiedX509CertPublisher> ret = new LinkedList<>(); Set<String> publisherNames = ca_has_publishers.get(caName); if (publisherNames == null) { return ret; } for (String publisherName : publisherNames) { IdentifiedX509CertPublisher publisher = publishers.get(publisherName); ret.add(publisher); } return ret; } @Override public X509Certificate generateRootCA(final X509CAEntry caEntry, final String certprofileName, final byte[] p10Req) throws CAMgmtException { ParamChecker.assertNotNull("caEntry", caEntry); ParamChecker.assertNotBlank("certprofileName", certprofileName); ParamChecker.assertNotNull("p10Req", p10Req); String name = caEntry.getName(); long nextSerial = caEntry.getNextSerial(); int numCrls = caEntry.getNumCrls(); int expirationPeriod = caEntry.getExpirationPeriod(); int nextCrlNumber = caEntry.getNextCRLNumber(); CAStatus status = caEntry.getStatus(); List<String> crl_uris = caEntry.getCrlUris(); List<String> delta_crl_uris = caEntry.getDeltaCrlUris(); List<String> ocsp_uris = caEntry.getOcspUris(); List<String> cacert_uris = caEntry.getCacertUris(); String signer_type = caEntry.getSignerType(); String signer_conf = caEntry.getSignerConf(); asssertMasterMode(); if (nextSerial < 0) { System.err.println("invalid serial number: " + nextSerial); return null; } if (numCrls < 0) { System.err.println("invalid numCrls: " + numCrls); return null; } if (expirationPeriod < 0) { System.err.println("invalid expirationPeriod: " + expirationPeriod); return null; } CertificationRequest p10Request; if (p10Req == null) { System.err.println("p10Req is null"); return null; } try { p10Request = CertificationRequest.getInstance(p10Req); } catch (Exception e) { System.err.println("invalid p10Req"); return null; } IdentifiedX509Certprofile certprofile = getIdentifiedCertprofile(certprofileName); if (certprofile == null) { throw new CAMgmtException("unknown cert profile " + certprofileName); } long serialOfThisCert; if (nextSerial > 0) { serialOfThisCert = nextSerial; nextSerial++; } else { serialOfThisCert = RandomSerialNumberGenerator.getInstance().getSerialNumber().longValue(); } GenerateSelfSignedResult result; try { result = X509SelfSignedCertBuilder.generateSelfSigned(securityFactory, signer_type, signer_conf, certprofile, p10Request, serialOfThisCert, cacert_uris, ocsp_uris, crl_uris, delta_crl_uris); } catch (OperationException | ConfigurationException e) { throw new CAMgmtException(e.getClass().getName() + ": " + e.getMessage(), e); } String signerConf = result.getSignerConf(); X509Certificate caCert = result.getCert(); if ("PKCS12".equalsIgnoreCase(signer_type) || "JKS".equalsIgnoreCase(signer_type)) { try { signerConf = canonicalizeSignerConf(signer_type, signerConf, securityFactory.getPasswordResolver(), new X509Certificate[] { caCert }); } catch (Exception e) { throw new CAMgmtException(e.getClass().getName() + ": " + e.getMessage(), e); } } X509CAEntry entry = new X509CAEntry(name, nextSerial, nextCrlNumber, signer_type, signerConf, cacert_uris, ocsp_uris, crl_uris, delta_crl_uris, numCrls, expirationPeriod); entry.setCertificate(caCert); entry.setCmpControlName(caEntry.getCmpControlName()); entry.setCrlSignerName(caEntry.getCrlSignerName()); entry.setDuplicateKeyMode(caEntry.getDuplicateKeyMode()); entry.setDuplicateSubjectMode(caEntry.getDuplicateSubjectMode()); entry.setExtraControl(caEntry.getExtraControl()); entry.setMaxValidity(caEntry.getMaxValidity()); entry.setPermissions(caEntry.getPermissions()); entry.setResponderName(caEntry.getResponderName()); entry.setStatus(status); entry.setValidityMode(caEntry.getValidityMode()); addCA(entry); return caCert; } private void asssertMasterMode() throws CAMgmtException { if (masterMode == false) { throw new CAMgmtException("operation not allowed in slave mode"); } } private static void assertNotNULL(final String parameterName, final String parameterValue) { if (CAManager.NULL.equalsIgnoreCase(parameterValue)) { throw new IllegalArgumentException(parameterName + " could not be " + CAManager.NULL); } } private static String canonicalizeSignerConf(final String keystoreType, final String signerConf, final PasswordResolver passwordResolver, final X509Certificate[] certChain) throws Exception { if (signerConf.contains("file:") == false && signerConf.contains("base64:") == false) { return signerConf; } CmpUtf8Pairs utf8Pairs = new CmpUtf8Pairs(signerConf); String keystoreConf = utf8Pairs.getValue("keystore"); String passwordHint = utf8Pairs.getValue("password"); String keyLabel = utf8Pairs.getValue("key-label"); byte[] keystoreBytes; if (StringUtil.startsWithIgnoreCase(keystoreConf, "file:")) { String keystoreFile = keystoreConf.substring("file:".length()); keystoreBytes = IoUtil.read(keystoreFile); } else if (StringUtil.startsWithIgnoreCase(keystoreConf, "base64:")) { keystoreBytes = Base64.decode(keystoreConf.substring("base64:".length())); } else { return signerConf; } keystoreBytes = SecurityUtil.extractMinimalKeyStore(keystoreType, keystoreBytes, keyLabel, passwordResolver.resolvePassword(passwordHint), certChain); utf8Pairs.putUtf8Pair("keystore", "base64:" + Base64.toBase64String(keystoreBytes)); return utf8Pairs.getEncoded(); } void shutdownCertprofile(final IdentifiedX509Certprofile profile) { if (profile == null) { return; } try { profile.shutdown(); } catch (Exception e) { final String message = "could not shutdown Certprofile " + profile.getName(); if (LOG.isWarnEnabled()) { LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); } } void shutdownPublisher(final IdentifiedX509CertPublisher publisher) { if (publisher == null) { return; } try { publisher.shutdown(); } catch (Exception e) { final String message = "could not shutdown CertPublisher " + publisher.getName(); if (LOG.isWarnEnabled()) { LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); } } CmpResponderEntryWrapper createCmpResponder(final CmpResponderEntry dbEntry) throws CAMgmtException { ParamChecker.assertNotNull("dbEntry", dbEntry); CmpResponderEntryWrapper ret = new CmpResponderEntryWrapper(); ret.setDbEntry(dbEntry); try { ret.initSigner(securityFactory); } catch (SignerException e) { final String message = "createCmpResponder"; LOG.debug(message, e); throw new CAMgmtException(e.getMessage()); } return ret; } X509CrlSignerEntryWrapper createX509CrlSigner(final X509CrlSignerEntry dbEntry) throws CAMgmtException { ParamChecker.assertNotNull("dbEntry", dbEntry); X509CrlSignerEntryWrapper signer = new X509CrlSignerEntryWrapper(); try { signer.setDbEntry(dbEntry); } catch (ConfigurationException e) { throw new CAMgmtException("ConfigurationException: " + e.getMessage()); } try { signer.initSigner(securityFactory); } catch (SignerException | OperationException | ConfigurationException e) { final String message = "exception while creating CRL signer " + dbEntry.getName(); if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); if (e instanceof OperationException) { throw new CAMgmtException( message + ": " + ((OperationException) e).getErrorCode() + ", " + e.getMessage()); } else { throw new CAMgmtException(message + ": " + e.getMessage()); } } return signer; } IdentifiedX509Certprofile createCertprofile(final CertprofileEntry dbEntry) { ParamChecker.assertNotNull("dbEntry", dbEntry); try { String realType = getRealCertprofileType(dbEntry.getType()); IdentifiedX509Certprofile ret = new IdentifiedX509Certprofile(dbEntry, realType); ret.setEnvironmentParameterResolver(envParameterResolver); ret.validate(); return ret; } catch (CertprofileException e) { final String message = "could not initialize Certprofile " + dbEntry.getName() + ", ignore it"; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); return null; } } IdentifiedX509CertPublisher createPublisher(final PublisherEntry dbEntry) throws CAMgmtException { ParamChecker.assertNotNull("dbEntry", dbEntry); String name = dbEntry.getName(); String type = dbEntry.getType(); String realType = getRealPublisherType(type); IdentifiedX509CertPublisher ret; try { ret = new IdentifiedX509CertPublisher(dbEntry, realType); ret.initialize(securityFactory.getPasswordResolver(), dataSources); return ret; } catch (CertPublisherException | RuntimeException e) { final String message = "invalid configuration for the certPublisher " + name; if (LOG.isErrorEnabled()) { LOG.error(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage()); } LOG.debug(message, e); return null; } } private String getRealCertprofileType(final String certprofileType) { return getRealType(envParameterResolver.getParameterValue("certprofileType.map"), certprofileType); } private String getRealPublisherType(final String publisherType) { return getRealType(envParameterResolver.getParameterValue("publisherType.map"), publisherType); } private static String getRealType(String typeMap, final String type) { if (typeMap == null) { return null; } typeMap = typeMap.trim(); if (StringUtil.isBlank(typeMap)) { return null; } CmpUtf8Pairs pairs; try { pairs = new CmpUtf8Pairs(typeMap); } catch (IllegalArgumentException e) { LOG.error("CA environment {}: '{}' is not valid CMP UTF-8 pairs", typeMap, type); return null; } return pairs.getValue(type); } static List<String[]> splitCASignerConfs(String conf) throws SignerException { CmpUtf8Pairs pairs = new CmpUtf8Pairs(conf); String str = pairs.getValue("algo"); List<String> list = StringUtil.split(str, ", "); if (list == null) { throw new SignerException("no algo is defined in CA signerConf"); } List<String[]> signerConfs = new ArrayList<>(list.size()); for (String n : list) { String c14nAlgo; try { c14nAlgo = AlgorithmUtil.canonicalizeSignatureAlgo(n); } catch (NoSuchAlgorithmException e) { throw new SignerException(e.getMessage(), e); } pairs.putUtf8Pair("algo", c14nAlgo); signerConfs.add(new String[] { c14nAlgo, pairs.getEncoded() }); } return signerConfs; } }