/* * * Copyright (c) 2013 - 2017 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.pki.ca.qa; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.cert.CertificateException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.validation.SchemaFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xipki.commons.common.util.IoUtil; import org.xipki.commons.common.util.LogUtil; import org.xipki.commons.common.util.ParamUtil; import org.xipki.commons.common.util.StringUtil; import org.xipki.pki.ca.api.profile.CertprofileException; import org.xipki.pki.ca.qa.jaxb.FileOrValueType; import org.xipki.pki.ca.qa.jaxb.ObjectFactory; import org.xipki.pki.ca.qa.jaxb.QAConfType; import org.xipki.pki.ca.qa.jaxb.X509CertprofileType; import org.xipki.pki.ca.qa.jaxb.X509IssuerType; import org.xml.sax.SAXException; /** * @author Lijun Liao * @since 2.0.0 */ public class QaSystemManagerImpl implements QaSystemManager { private static final Logger LOG = LoggerFactory.getLogger(QaSystemManagerImpl.class); private final Unmarshaller jaxbUnmarshaller; private String confFile; private Map<String, X509CertprofileQa> x509ProfileMap = new HashMap<>(); private Map<String, X509IssuerInfo> x509IssuerInfoMap = new HashMap<>(); private AtomicBoolean initialized = new AtomicBoolean(false); public QaSystemManagerImpl() throws JAXBException, SAXException { JAXBContext context = JAXBContext.newInstance(ObjectFactory.class); jaxbUnmarshaller = context.createUnmarshaller(); final SchemaFactory schemaFact = SchemaFactory.newInstance( javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI); URL url = QaSystemManagerImpl.class.getResource("/xsd/caqa-conf.xsd"); jaxbUnmarshaller.setSchema(schemaFact.newSchema(url)); } public String getConfFile() { return confFile; } public void setConfFile(final String confFile) { this.confFile = ParamUtil.requireNonBlank("confFile", confFile); } public boolean isInitialized() { return initialized.get(); } public void init() { if (StringUtil.isBlank(confFile)) { throw new IllegalStateException("confFile must not be null and empty"); } LOG.info("initializing ..."); if (initialized.get()) { LOG.info("already initialized, skipping ..."); return; } QAConfType qaConf; try { FileInputStream issuerConfStream = new FileInputStream(confFile); qaConf = parseQaConf(issuerConfStream); } catch (IOException | JAXBException | SAXException ex) { final String message = "could not parse the QA configuration"; LogUtil.error(LOG, ex, message); return; } if (qaConf.getX509Issuers() != null) { List<X509IssuerType> x509IssuerTypes = qaConf.getX509Issuers().getX509Issuer(); for (X509IssuerType issuerType : x509IssuerTypes) { byte[] certBytes; try { certBytes = readData(issuerType.getCert()); } catch (IOException ex) { LogUtil.error(LOG, ex, "could not read the certificate bytes of issuer " + issuerType.getName()); continue; } String str = issuerType.getValidityMode(); boolean cutoffNotAfter; if (StringUtil.isBlank(str) || "CUTOFF".equalsIgnoreCase(str)) { cutoffNotAfter = true; } else if ("LAX".equalsIgnoreCase(str)) { cutoffNotAfter = false; } else { LOG.error("invalid validityMode {}", str); return; } X509IssuerInfo issuerInfo; try { issuerInfo = new X509IssuerInfo(issuerType.getCaIssuerUrl(), issuerType.getOcspUrl(), issuerType.getCrlUrl(), issuerType.getDeltaCrlUrl(), certBytes, cutoffNotAfter); } catch (CertificateException ex) { LogUtil.error(LOG, ex, "could not parse certificate of issuer " + issuerType.getName()); continue; } x509IssuerInfoMap.put(issuerType.getName(), issuerInfo); LOG.info("configured X509 issuer {}", issuerType.getName()); } } if (qaConf.getX509Certprofiles() != null) { List<X509CertprofileType> certprofileTypes = qaConf.getX509Certprofiles().getX509Certprofile(); for (X509CertprofileType type : certprofileTypes) { String name = type.getName(); try { byte[] content = readData(type); x509ProfileMap.put(name, new X509CertprofileQa(content)); LOG.info("configured X509 certificate profile {}", name); } catch (IOException | CertprofileException ex) { LogUtil.error(LOG, ex, "could not parse QA certificate profile " + name); continue; } } } initialized.set(true); LOG.info("initialized"); } // method init public void shutdown() { } @Override public Set<String> getIssuerNames() { return Collections.unmodifiableSet(x509IssuerInfoMap.keySet()); } @Override public X509IssuerInfo getIssuer(final String issuerName) { ParamUtil.requireNonNull("issuerName", issuerName); return x509IssuerInfoMap.get(issuerName); } @Override public Set<String> getCertprofileNames() { return Collections.unmodifiableSet(x509ProfileMap.keySet()); } @Override public X509CertprofileQa getCertprofile(final String certprofileName) { ParamUtil.requireNonNull("certprofileName", certprofileName); return x509ProfileMap.get(certprofileName); } private QAConfType parseQaConf(final InputStream confStream) throws IOException, JAXBException, SAXException { JAXBElement<?> rootElement; try { rootElement = (JAXBElement<?>) jaxbUnmarshaller.unmarshal(confStream); } finally { confStream.close(); } Object rootType = rootElement.getValue(); if (rootType instanceof QAConfType) { return (QAConfType) rootElement.getValue(); } else { throw new SAXException("invalid root element type"); } } private static byte[] readData(final FileOrValueType fileOrValue) throws IOException { byte[] data = fileOrValue.getValue(); if (data == null) { data = IoUtil.read(fileOrValue.getFile()); } return data; } }