package at.gv.egiz.eaaf.core.impl.credential; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Security; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import at.asitplus.hsmfacade.provider.HsmFacadeProvider; import at.asitplus.hsmfacade.provider.RemoteKeyStoreLoadParameter; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.exceptions.EaafFactoryException; import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; import at.gv.egiz.eaaf.core.impl.utils.FileUtils; import at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import lombok.extern.slf4j.Slf4j; @Slf4j public class EaafKeyStoreFactory { public static final String CONFIG_PROP_HSM_FACADE_HOST = "security.hsmfacade.host"; public static final String CONFIG_PROP_HSM_FACADE_PORT = "security.hsmfacade.port"; public static final String CONFIG_PROP_HSM_FACADE_SSLTRUST = "security.hsmfacade.trustedsslcert"; public static final String CONFIG_PROP_HSM_FACADE_CLIENT_USERNAME = "security.hsmfacade.username"; public static final String CONFIG_PROP_HSM_FACADE_CLIENT_PASSWORD = "security.hsmfacade.password"; public static final String CONFIG_PROP_HSM_FACADE_HSM_NAME = "security.hsmfacade.hsmname"; public static final String ERRORCODE_00 = "internal.keystore.00"; public static final String ERRORCODE_01 = "internal.keystore.01"; public static final String ERRORCODE_02 = "internal.keystore.02"; public static final String ERRORCODE_03 = "internal.keystore.03"; public static final String ERRORCODE_04 = "internal.keystore.04"; public static final String ERRORCODE_05 = "internal.keystore.05"; public static final String ERRORCODE_06 = "internal.keystore.06"; @Autowired private IConfiguration basicConfig; @Autowired private ResourceLoader resourceLoader; private boolean isHsmFacadeInitialized = false; public KeyStore buildNewKeyStore(KeyStoreConfiguration config) throws EaafException { log.trace("Starting KeyStore generation based on configuration object ... "); if (KeyStoreType.SOFTWARE.equals(config.getKeyStoreType())) { return getKeyStoreFromFileSystem(config); } else if (KeyStoreType.HSMFACADE.equals(config.getKeyStoreType())) { if (isHsmFacadeInitialized) { getKeyStoreFromHsmFacade(config); } else { log.error("HSMFacade can NOT be used for KeyStore: {} because {} is not initialized", config.getFriendlyName()); throw new EaafConfigurationException(ERRORCODE_00, new Object[]{config.getFriendlyName()}); } } else if (KeyStoreType.PKCS11.equals(config.getKeyStoreType())) { log.warn("KeyStoreType: {} is NOT supported", config.getKeyStoreType()); throw new EaafConfigurationException(ERRORCODE_02, new Object[]{config.getFriendlyName(), config.getKeyStoreType()}); } else { log.warn("KeyStoreType: {} is unrecognized", config.getKeyStoreType()); throw new EaafConfigurationException(ERRORCODE_01, new Object[]{config.getFriendlyName()}); } return null; } @PostConstruct private void initialize() throws EaafException { final String hsmFacadeHost = basicConfig.getBasicConfiguration(CONFIG_PROP_HSM_FACADE_HOST); if (StringUtils.isNotEmpty(hsmFacadeHost)) { log.debug("Find host for HSMFacade. Starting crypto provider initialization ... "); try { final int port = Integer.valueOf( getConfigurationParameter(CONFIG_PROP_HSM_FACADE_PORT)); final String clientUsername = getConfigurationParameter(CONFIG_PROP_HSM_FACADE_CLIENT_USERNAME); final String clientPassword = getConfigurationParameter(CONFIG_PROP_HSM_FACADE_CLIENT_PASSWORD); final String hsmName = getConfigurationParameter(CONFIG_PROP_HSM_FACADE_HSM_NAME); final HsmFacadeProvider provider = HsmFacadeProvider.Companion.getInstance(); provider.init(getHsmFacadeTrustSslCertificate(), clientUsername, clientPassword, hsmFacadeHost, port, hsmName); Security.addProvider(provider); isHsmFacadeInitialized = true; log.info("HSM Facade is initialized. {} can provide KeyStores based on remote HSM", EaafKeyStoreFactory.class.getSimpleName()); } catch (final EaafException e) { throw e; } catch (final Exception e) { log.error("HSM Facade initialization FAILED with an generic error.", e); throw new EaafConfigurationException(ERRORCODE_03, new Object[] {e.getMessage()}, e); } } else { log.info("HSM Facade is not configurated. {} can only provide software keystores", EaafKeyStoreFactory.class.getSimpleName()); } } private KeyStore getKeyStoreFromFileSystem(KeyStoreConfiguration config) throws EaafConfigurationException { try { final String keyStorePath = config.getKeyStoreFilePath(); final String keyStorePassword = config.getKeyStorePassword(); //TODO: check config final String absKeyStorePath = FileUtils.makeAbsoluteUrl(keyStorePath, basicConfig.getConfigurationRootDirectory()); final Resource ressource = resourceLoader.getResource(absKeyStorePath); if (!ressource.exists()) { throw new EaafConfigurationException(ERRORCODE_05, new Object[] {CONFIG_PROP_HSM_FACADE_SSLTRUST, "File not found at: " + absKeyStorePath}); } return KeyStoreUtils.loadKeyStore(ressource.getInputStream(), keyStorePassword); } catch (KeyStoreException | IOException e) { log.error("Software KeyStore initialization FAILED with an generic error.", e); throw new EaafConfigurationException(ERRORCODE_03, new Object[] {e.getMessage()}, e); } } private KeyStore getKeyStoreFromHsmFacade(KeyStoreConfiguration config) throws EaafFactoryException, EaafConfigurationException { final String keyStoreName = config.getKeyStoreName(); if (StringUtils.isEmpty(keyStoreName)) { throw new EaafConfigurationException(ERRORCODE_06, new Object[] {config.getFriendlyName(), "No KeyStore name"}); } try { final KeyStore keyStore = KeyStore.getInstance("RemoteKeyStore", "HsmFacade"); keyStore.load(new RemoteKeyStoreLoadParameter(keyStoreName)); return keyStore; } catch (NoSuchAlgorithmException | CertificateException | IOException | KeyStoreException | NoSuchProviderException e) { log.error("Can not initialize KeyStore: {} with reason: {}", config.getFriendlyName(), e.getMessage()); throw new EaafFactoryException(ERRORCODE_06, new Object[] {config.getFriendlyName(), e.getMessage()}, e); } } private X509Certificate getHsmFacadeTrustSslCertificate() throws EaafConfigurationException { try { final String certFilePath = getConfigurationParameter(CONFIG_PROP_HSM_FACADE_SSLTRUST); final String absolutCertFilePath = FileUtils.makeAbsoluteUrl( certFilePath, basicConfig.getConfigurationRootDirectory()); final Resource certFile = resourceLoader.getResource(absolutCertFilePath); if (!certFile.exists()) { throw new EaafConfigurationException(ERRORCODE_05, new Object[] {CONFIG_PROP_HSM_FACADE_SSLTRUST, "File not found at: " + absolutCertFilePath }); } return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(certFile.getInputStream()); } catch (final EaafConfigurationException e) { throw e; } catch (CertificateException | IOException e) { log.error("Can not load trusted server-certificate for HSM-Facade. Reason: {}", e.getMessage()); throw new EaafConfigurationException(ERRORCODE_05, new Object[] {CONFIG_PROP_HSM_FACADE_SSLTRUST, e.getMessage()}, e); } } @Nonnull private String getConfigurationParameter(@Nonnull String configParamKey) throws EaafConfigurationException{ final String configValue = basicConfig.getBasicConfiguration(configParamKey); if (StringUtils.isEmpty(configValue)) { throw new EaafConfigurationException(ERRORCODE_04, new Object[] {configParamKey}); } return configValue; } }