/** * alert-common * * Copyright (c) 2020 Synopsys, Inc. * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.synopsys.integration.alert.common.security; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.synopsys.integration.alert.common.AlertProperties; import com.synopsys.integration.alert.common.exception.AlertConfigurationException; import com.synopsys.integration.alert.common.exception.AlertException; import com.synopsys.integration.alert.common.persistence.model.CustomCertificateModel; @Component public class CertificateUtility { private final Logger logger = LoggerFactory.getLogger(CertificateUtility.class); private AlertProperties alertProperties; @Autowired public CertificateUtility(AlertProperties alertProperties) { this.alertProperties = alertProperties; } public synchronized void importCertificate(CustomCertificateModel customCertificate) throws AlertException { logger.debug("Importing certificate into trust store."); validateCustomCertificateHasValues(customCertificate); File trustStoreFile = getAndValidateTrustStoreFile(); KeyStore trustStore = getAsKeyStore(trustStoreFile, getTrustStorePassword(), getTrustStoreType()); try { Certificate cert = getAsJavaCertificate(customCertificate); trustStore.setCertificateEntry(customCertificate.getAlias(), cert); try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(trustStoreFile))) { trustStore.store(stream, getTrustStorePassword()); } } catch (IOException | CertificateException | NoSuchAlgorithmException | KeyStoreException e) { throw new AlertException("There was a problem storing the certificate.", e); } } public synchronized void removeCertificate(CustomCertificateModel customCertificate) throws AlertException { logger.debug("Removing certificate from trust store."); if (null == customCertificate) { throw new AlertException("The alias could not be determined from the custom certificate because it was null."); } removeCertificate(customCertificate.getAlias()); } public synchronized void removeCertificate(String certificateAlias) throws AlertException { logger.debug("Removing certificate by alias from trust store."); if (StringUtils.isBlank(certificateAlias)) { throw new AlertException("The alias cannot be blank"); } try { File trustStore = getAndValidateTrustStoreFile(); KeyStore keyStore = getAsKeyStore(trustStore, getTrustStorePassword(), getTrustStoreType()); if (keyStore.containsAlias(certificateAlias)) { keyStore.deleteEntry(certificateAlias); try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(trustStore))) { keyStore.store(stream, getTrustStorePassword()); } } } catch (IOException | CertificateException | NoSuchAlgorithmException | KeyStoreException e) { throw new AlertException("There was a problem removing the certificate.", e); } } public synchronized KeyStore getAsKeyStore(File keyStore, char[] keyStorePass, String keyStoreType) throws AlertException { logger.debug("Get key store."); KeyStore.PasswordProtection protection = new KeyStore.PasswordProtection(keyStorePass); try { return KeyStore.Builder.newInstance(keyStoreType, null, keyStore, protection).getKeyStore(); } catch (KeyStoreException e) { throw new AlertException("There was a problem accessing the trust store.", e); } } public synchronized File getAndValidateTrustStoreFile() throws AlertConfigurationException { logger.debug("Get and validate trust store."); Optional<String> optionalTrustStoreFileName = alertProperties.getTrustStoreFile(); if (optionalTrustStoreFileName.isPresent()) { String trustStoreFileName = optionalTrustStoreFileName.get(); File trustStoreFile; try { URI trustStoreUri = new URI(trustStoreFileName); trustStoreFile = new File(trustStoreUri); } catch (IllegalArgumentException | URISyntaxException ex) { logger.debug("Error getting Java trust store from file URI", ex); trustStoreFile = new File(trustStoreFileName); } if (!trustStoreFile.isFile()) { throw new AlertConfigurationException("The trust store provided is not a file: " + trustStoreFileName); } if (!trustStoreFile.canWrite()) { throw new AlertConfigurationException("The trust store provided cannot be written by Alert: " + trustStoreFileName); } return trustStoreFile; } else { throw new AlertConfigurationException("No trust store file has been provided."); } } public synchronized void validateCertificateContent(CustomCertificateModel customCertificateModel) throws AlertException { getAsJavaCertificate(customCertificateModel); } private void validateCustomCertificateHasValues(CustomCertificateModel customCertificate) throws AlertException { if (null == customCertificate) { throw new AlertException("The custom certificate cannot be null."); } if (StringUtils.isBlank(customCertificate.getAlias())) { throw new AlertException("The alias cannot be blank."); } if (StringUtils.isBlank(customCertificate.getCertificateContent())) { throw new AlertException("The certificate content cannot be blank."); } } private Certificate getAsJavaCertificate(CustomCertificateModel customCertificate) throws AlertException { try { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); String certificateContent = customCertificate.getCertificateContent(); try (ByteArrayInputStream certInputStream = new ByteArrayInputStream(certificateContent.getBytes())) { return certFactory.generateCertificate(certInputStream); } } catch (CertificateException | IOException e) { throw new AlertException("The custom certificate could not be read.", e); } } private String getTrustStoreType() { return alertProperties.getTrustStoreType().orElse(KeyStore.getDefaultType()); } private char[] getTrustStorePassword() { return alertProperties .getTrustStorePass() .map(String::toCharArray) .orElse(null); } }