diff options
author | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2020-06-18 14:39:29 +0200 |
---|---|---|
committer | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2020-06-18 14:39:29 +0200 |
commit | 0d52fe861a46f8ba595bdd34b106c98096c4304b (patch) | |
tree | 566f30e8c6bfebddfa90b7062ec952b1bd945916 /eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential | |
parent | f39a0a004bbe6b6b126218993767b897efa06745 (diff) | |
download | EAAF-Components-0d52fe861a46f8ba595bdd34b106c98096c4304b.tar.gz EAAF-Components-0d52fe861a46f8ba595bdd34b106c98096c4304b.tar.bz2 EAAF-Components-0d52fe861a46f8ba595bdd34b106c98096c4304b.zip |
add symmetric-key functionality into EaafKeyStoreFactory that supports passphrase based symmetric keys and keys from HSM-Facade
Diffstat (limited to 'eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential')
3 files changed, 341 insertions, 8 deletions
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreFactory.java index 9db38670..711a3517 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreFactory.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreFactory.java @@ -2,6 +2,7 @@ package at.gv.egiz.eaaf.core.impl.credential; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.security.Key; @@ -12,13 +13,19 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; import java.security.Security; +import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.PostConstruct; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -26,10 +33,12 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exception.EaafKeyAccessException; 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.credential.SymmetricKeyConfiguration.SymmetricKeyType; import at.gv.egiz.eaaf.core.impl.data.Pair; import at.gv.egiz.eaaf.core.impl.utils.FileUtils; import at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils; @@ -53,6 +62,8 @@ public class EaafKeyStoreFactory { public static final String ERRORCODE_06 = "internal.keystore.06"; public static final String ERRORCODE_07 = "internal.keystore.07"; public static final String ERRORCODE_10 = "internal.keystore.10"; + + public static final String ERRORCODE_KEY_00 = "internal.key.00"; private static final String HSM_FACADE_PROVIDER_CLASS = "at.asitplus.hsmfacade.provider.HsmFacadeProvider"; private static final String HSM_FACADE_KEYSTORELOADPARAMETERS_CLASS @@ -72,6 +83,43 @@ public class EaafKeyStoreFactory { private boolean isHsmFacadeInitialized = false; /** + * Get a new symmetric key based on a {@link SymmetricKeyConfiguration} object. + * + * @param config Symmetric key configuration + * @return {@link Pair} of a new {@link SecretKey} instance and an optional {@link Provider}. + * The {@link SecretKey} is {@link Nonnull}. If the {@link Provider} is not <code>null</code> + * this {@link SecretKey} requires a specific {@link Provider} for {@link Key} operations. + * @throws EaafException In case of a KeyStore initialization error + */ + @Nonnull + public Pair<SecretKey, Provider> buildNewSymmetricKey(SymmetricKeyConfiguration config) throws EaafException { + log.trace("Starting symmetric-key generation based on configuration object ... "); + if (SymmetricKeyType.PASSPHRASE.equals(config.getKeyType())) { + return generatePassPhraseBasedSymmetricKey(config); + + } else if (SymmetricKeyType.HSMFACADE.equals(config.getKeyType())) { + if (isHsmFacadeInitialized) { + return getSymmetricKeyFromHsmFacade(config); + + } else { + log.error("HSMFacade can NOT be used for symmetric Key: {} because {} is not initialized", + config.getFriendlyName()); + throw new EaafConfigurationException(ERRORCODE_00, + new Object[] { config.getFriendlyName() }); + + } + + } else { + log.warn("Symmetric KeyType: {} is unrecognized", config.getKeyType()); + throw new EaafConfigurationException(ERRORCODE_01, + new Object[] { config.getFriendlyName() }); + + } + + + } + + /** * Get a new KeyStore based on a KeyStore configuration-object. * * @param config KeyStore configuration @@ -241,24 +289,31 @@ public class EaafKeyStoreFactory { @Nonnull private Pair<KeyStore, Provider> getKeyStoreFromHsmFacade(KeyStoreConfiguration config) throws EaafFactoryException, EaafConfigurationException { - final String keyStoreName = checkConfigurationParameter(config.getKeyStoreName(), - ERRORCODE_06, config.getFriendlyName(), "KeyStoreName missing for HSM Facade"); + return getKeyStoreFromHsmFacade(config.getKeyStoreName(), config.getFriendlyName()); + + } + + @Nonnull + private Pair<KeyStore, Provider> getKeyStoreFromHsmFacade(String keyStoreName, String friendlyName) + throws EaafFactoryException, EaafConfigurationException { + final String validatedKeyStoreName = checkConfigurationParameter(keyStoreName, + ERRORCODE_06, friendlyName, "KeyStoreName missing for HSM Facade"); try { final KeyStore keyStore = KeyStore.getInstance(HSM_FACADE_KEYSTORE_TYPE, HSM_FACADE_PROVIDER); - keyStore.load(getHsmFacadeKeyStoreParameter(keyStoreName)); + keyStore.load(getHsmFacadeKeyStoreParameter(validatedKeyStoreName)); return Pair.newInstance(keyStore, keyStore.getProvider()); } catch (NoSuchAlgorithmException | CertificateException | IOException | KeyStoreException | NoSuchProviderException | EaafException e) { log.error("Can not initialize KeyStore: {} with reason: {}", - config.getFriendlyName(), e.getMessage()); + friendlyName, e.getMessage()); throw new EaafFactoryException(ERRORCODE_06, - new Object[] { config.getFriendlyName(), e.getMessage() }, e); + new Object[] {friendlyName, e.getMessage() }, e); } } - + private KeyStore.LoadStoreParameter getHsmFacadeKeyStoreParameter(String keyStoreName) throws EaafException { try { Class<?> clazz = Class.forName(HSM_FACADE_KEYSTORELOADPARAMETERS_CLASS); @@ -274,6 +329,62 @@ public class EaafKeyStoreFactory { } + @Nonnull + private Pair<SecretKey, Provider> generatePassPhraseBasedSymmetricKey(SymmetricKeyConfiguration config) + throws EaafConfigurationException { + checkConfigurationParameter(config.getSoftKeyPassphrase(), + ERRORCODE_KEY_00, config.getFriendlyName(), "passphrase missing"); + checkConfigurationParameter(config.getSoftKeySalt(), + ERRORCODE_KEY_00, config.getFriendlyName(), "salt missing"); + + try { + final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA256"); + final KeySpec spec = new PBEKeySpec( + config.getSoftKeyPassphrase().toCharArray(), + config.getSoftKeySalt().getBytes("UTF-8"), + 10000, 128); + return Pair.newInstance(keyFactory.generateSecret(spec), null); + + } catch (NoSuchAlgorithmException | InvalidKeySpecException | UnsupportedEncodingException e) { + log.error("Passphrase based symmetric-key generation FAILED", e); + throw new EaafConfigurationException(ERRORCODE_KEY_00, + new Object[] { config.getFriendlyName(), e.getMessage() }, + e); + + } + } + + @Nonnull + private Pair<SecretKey, Provider> getSymmetricKeyFromHsmFacade(SymmetricKeyConfiguration config) + throws EaafFactoryException, EaafConfigurationException, EaafKeyAccessException { + Pair<KeyStore, Provider> keyStore = getKeyStoreFromHsmFacade( + config.getKeyStoreName(), config.getFriendlyName()); + + checkConfigurationParameter(config.getKeyAlias(), + ERRORCODE_KEY_00, config.getFriendlyName(), "keyAlias missing"); + + try { + SecretKey secretKey = (SecretKey) keyStore.getFirst().getKey(config.getKeyAlias(), null); + if (secretKey == null) { + throw new EaafKeyAccessException(EaafKeyAccessException.ERROR_CODE_09, + config.getFriendlyName(), config.getKeyAlias(), "No SecretKey with Alias "); + + } + + return Pair.newInstance(secretKey, keyStore.getSecond()); + + } catch (UnrecoverableKeyException | KeyStoreException | NoSuchAlgorithmException e) { + throw new EaafKeyAccessException(EaafKeyAccessException.ERROR_CODE_09, e, + config.getFriendlyName(), config.getKeyAlias(), e.getMessage()); + + } catch (ClassCastException e) { + throw new EaafKeyAccessException(EaafKeyAccessException.ERROR_CODE_09, + config.getFriendlyName(), config.getKeyAlias(), "Wrong SecretKey type "); + + } + + } + private X509Certificate getHsmFacadeTrustSslCertificate() throws EaafConfigurationException { try { final String certFilePath = getConfigurationParameter(CONFIG_PROP_HSM_FACADE_SSLTRUST); diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreUtils.java index b4b44724..12541222 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreUtils.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreUtils.java @@ -24,13 +24,13 @@ import lombok.extern.slf4j.Slf4j; public class EaafKeyStoreUtils { private static final String ERROR_MSG_REASON = "Maybe 'Alias' is not valid"; private static final String ERROR_MSG_1 = "Can NOT access key: {} in KeyStore: {}. Reason: {}"; - private static final String ERROR_MSG_2 = "Key: {} will be NOT available"; + private static final String ERROR_MSG_2 = "Key: {} will be NOT available"; /** * Read all certificates from a {@link KeyStore}. * * @param keyStore KeyStore with certificates - * @return {@link List} of {@link X509Certificate}, but never null + * @return Unmodifiable {@link List} of {@link X509Certificate}, but never null * @throws KeyStoreException In case of an error during KeyStore operations */ @Nonnull @@ -45,6 +45,7 @@ public class EaafKeyStoreUtils { final Certificate cert = keyStore.getCertificate(el); if (cert != null && cert instanceof X509Certificate) { result.add((X509Certificate) cert); + } else { log.info("Can not process entry: {}. Reason: {}", el, cert != null ? cert.getType() : "cert is null"); } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/SymmetricKeyConfiguration.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/SymmetricKeyConfiguration.java new file mode 100644 index 00000000..45b51975 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/SymmetricKeyConfiguration.java @@ -0,0 +1,221 @@ +package at.gv.egiz.eaaf.core.impl.credential; + +import java.util.Map; + +import javax.annotation.Nonnull; + +import org.apache.commons.lang3.StringUtils; + +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Getter +@Setter +public class SymmetricKeyConfiguration { + + public static final String PROP_CONFIG_KEY_TYPE = + "key.type"; + + public static final String PROP_CONFIG_HSMFACADE_NAME = + "keystore.name"; + public static final String PROP_CONFIG_HSM_KEY_ALIAS = + "key.alias"; + + public static final String PROP_CONFIG_SOFTWARE_KEY_PASSPHRASE = + "key.passphrase"; + public static final String PROP_CONFIG_SOFTWARE_KEY_SALT = + "key.salt"; + + /** + * FriendlyName for this KeyStore. Mainly used for logging. + */ + private String friendlyName; + + /** + * General type of the KeyStore that should be generated. + */ + private SymmetricKeyType keyType; + + /** + * Name of the KeyStore in HSM Facade. + */ + private String keyStoreName; + + /** + * Alias of the Key in HSM Facade keystore. + */ + private String keyAlias; + + /** + * Software key passphrase. + */ + private String softKeyPassphrase; + + /** + * Software key salt. + */ + private String softKeySalt; + + /** + * Build a {@link SymmetricKeyConfiguration} from a configuration map. <br> + * <p> + * The configuration parameters defined in this class are used to load the + * configuration. + * </p> + * + * @param config Configuration + * @param friendlyName FriendlyName for this KeyStore + * @return Configuration object for {@link EaafKeyStoreFactory} + * @throws EaafConfigurationException In case of a configuration error. + */ + public static SymmetricKeyConfiguration buildFromConfigurationMap(Map<String, String> config, + String friendlyName) throws EaafConfigurationException { + + final SymmetricKeyConfiguration internalConfig = new SymmetricKeyConfiguration(); + internalConfig.setFriendlyName(friendlyName); + + final SymmetricKeyType internalKeyStoreType = SymmetricKeyType.fromString( + getConfigurationParameter(config, PROP_CONFIG_KEY_TYPE)); + if (internalKeyStoreType != null) { + internalConfig.setKeyType(internalKeyStoreType); + + } else { + log.error("Symmetric Key-configuration: {} sets an unknown Keytype: {}", + friendlyName, getConfigurationParameter(config, PROP_CONFIG_KEY_TYPE)); + throw new EaafConfigurationException(EaafKeyStoreFactory.ERRORCODE_01, + new Object[] { friendlyName }); + + } + + if (internalKeyStoreType.equals(SymmetricKeyType.HSMFACADE)) { + log.trace("Set-up HSM-Facade Symmentric-Key ... "); + internalConfig.setKeyStoreName(getConfigurationParameter(config, PROP_CONFIG_HSMFACADE_NAME)); + internalConfig.setKeyAlias(getConfigurationParameter(config, PROP_CONFIG_HSM_KEY_ALIAS)); + + } else { + log.trace("Set-up software passphrase based symmetric key ... "); + internalConfig.setSoftKeyPassphrase(getConfigurationParameter(config, PROP_CONFIG_SOFTWARE_KEY_PASSPHRASE)); + internalConfig.setSoftKeySalt(getConfigurationParameter(config, PROP_CONFIG_SOFTWARE_KEY_SALT)); + + } + + return internalConfig; + } + + /** + * Set the Type of the symmetric key based on String identifier. + * + * @param keyType String based KeyStore type + * @throws EaafConfigurationException In case of an unknown KeyStore type + */ + public void setKeyType(@Nonnull String keyType) throws EaafConfigurationException { + final SymmetricKeyType internalKeyStoreType = SymmetricKeyType.fromString(keyType); + if (internalKeyStoreType != null) { + setKeyType(internalKeyStoreType); + + } else { + log.error("KeyStore: {} sets an unknown KeyStore type: {}", + friendlyName, keyType); + throw new EaafConfigurationException(EaafKeyStoreFactory.ERRORCODE_01, + new Object[] { friendlyName }); + + } + + } + + /** + * Set the Type of the symmetric Key based on String identifier. + * + * @param type of tke symmetric key + */ + public void setKeyType(@Nonnull SymmetricKeyType type) { + this.keyType = type; + + } + + /** + * Validate the internal state of this configuration object. + * + * @throws EaafConfigurationException In case of a configuration error + */ + public void validate() throws EaafConfigurationException { + if (SymmetricKeyType.HSMFACADE.equals(keyType)) { + log.trace("Validate HSM-Facade symmetric key ... "); + checkConfigurationValue(keyStoreName, EaafKeyStoreFactory.ERRORCODE_07, + friendlyName, "Missing 'KeyStoreName' for HSM-Facade"); + checkConfigurationValue(keyStoreName, EaafKeyStoreFactory.ERRORCODE_07, + friendlyName, "Missing 'KeyAlias' for HSM-Facade"); + + } else { + log.trace("Validate passphrase based symmetric key ... "); + checkConfigurationValue(softKeyPassphrase, EaafKeyStoreFactory.ERRORCODE_07, + friendlyName, "Missing 'passphrase' for symmetric-key generation"); + checkConfigurationValue(softKeySalt, EaafKeyStoreFactory.ERRORCODE_07, + friendlyName, "Missing 'salt' for symmetric-key generation"); + + } + } + + public enum SymmetricKeyType { + PASSPHRASE("passphrase"), HSMFACADE("hsmfacade"); + + private final String keyType; + + SymmetricKeyType(final String keyStoreType) { + this.keyType = keyStoreType; + } + + /** + * Get Type of this Key. + * + * @return + */ + public String getKeyType() { + return this.keyType; + } + + /** + * Get KeyType from String representation. + * + * @param s Config parameter + * @return + */ + public static SymmetricKeyType fromString(final String s) { + try { + return SymmetricKeyType.valueOf(s.toUpperCase()); + + } catch (IllegalArgumentException | NullPointerException e) { + return null; + } + } + + @Override + public String toString() { + return getKeyType(); + + } + } + + @Nonnull + private static String getConfigurationParameter(@Nonnull Map<String, String> config, + @Nonnull String configParamKey) + throws EaafConfigurationException { + final String configValue = config.get(configParamKey); + checkConfigurationValue(configValue, EaafKeyStoreFactory.ERRORCODE_04, configParamKey); + return configValue; + + } + + private static void checkConfigurationValue(String configValue, String errorCode, String... params) + throws EaafConfigurationException { + if (StringUtils.isEmpty(configValue)) { + throw new EaafConfigurationException(errorCode, + params); + + } + + } +} |