diff options
Diffstat (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/AbstractCredentialProvider.java')
-rw-r--r-- | eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/AbstractCredentialProvider.java | 535 |
1 files changed, 319 insertions, 216 deletions
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/AbstractCredentialProvider.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/AbstractCredentialProvider.java index acc5357e..26a5c5f6 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/AbstractCredentialProvider.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/AbstractCredentialProvider.java @@ -1,225 +1,328 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. +*/ + package at.gv.egiz.eaaf.modules.pvp2.impl.utils; import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.RSAPrivateKey; +import java.security.KeyStoreException; +import java.security.Provider; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; -import org.apache.commons.lang3.StringUtils; -import org.opensaml.xml.security.credential.Credential; -import org.opensaml.xml.security.credential.UsageType; -import org.opensaml.xml.security.x509.X509Credential; -import org.opensaml.xml.signature.Signature; -import org.opensaml.xml.signature.SignatureConstants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import at.gv.egiz.eaaf.core.exceptions.EAAFException; -import at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils; +import javax.annotation.Nonnull; +import javax.annotation.PostConstruct; + +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.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; +import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential; +import at.gv.egiz.eaaf.modules.pvp2.api.utils.IPvp2CredentialProvider; import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException; -import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.KeyStoreX509CredentialAdapter; - -public abstract class AbstractCredentialProvider { - - private static final Logger log = LoggerFactory.getLogger(AbstractCredentialProvider.class); - - private KeyStore keyStore = null; - - /** - * Get a friendlyName for this keyStore implementation - * This friendlyName is used for logging - * - * @return keyStore friendlyName - */ - public abstract String getFriendlyName(); - - /** - * Get KeyStore - * - * @return URL to the keyStore - * @throws EAAFException - */ - public abstract String getKeyStoreFilePath() throws EAAFException; - - /** - * Get keyStore password - * - * @return Password of the keyStore - */ - public abstract String getKeyStorePassword(); - - /** - * Get alias of key for metadata signing - * - * @return key alias - */ - public abstract String getMetadataKeyAlias(); - - /** - * Get password of key for metadata signing - * - * @return key password - */ - public abstract String getMetadataKeyPassword(); - - /** - * Get alias of key for request/response signing - * - * @return key alias - */ - public abstract String getSignatureKeyAlias(); - - /** - * Get password of key for request/response signing - * - * @return key password - */ - public abstract String getSignatureKeyPassword(); - - /** - * Get alias of key for IDP response encryption - * - * @return key alias - */ - public abstract String getEncryptionKeyAlias(); - - /** - * Get password of key for IDP response encryption - * - * @return key password - */ - public abstract String getEncryptionKeyPassword(); - - - public X509Credential getIDPMetaDataSigningCredential() - throws CredentialsNotAvailableException { - try { - - if (keyStore == null) - keyStore = KeyStoreUtils.loadKeyStore(getKeyStoreFilePath(), - getKeyStorePassword()); - - KeyStoreX509CredentialAdapter credentials = new KeyStoreX509CredentialAdapter( - keyStore, getMetadataKeyAlias(), getMetadataKeyPassword().toCharArray()); - - credentials.setUsageType(UsageType.SIGNING); - if (credentials.getPrivateKey() == null && credentials.getSecretKey() == null) { - log.error(getFriendlyName() + " Metadata Signing credentials is not found or contains no PrivateKey."); - throw new CredentialsNotAvailableException("config.27", new Object[]{getFriendlyName() + " Assertion Signing credentials (Alias: " - + getMetadataKeyAlias() + ") is not found or contains no PrivateKey."}); - - } - return credentials; - } catch (Exception e) { - log.error("Failed to generate " + getFriendlyName() + " Metadata Signing credentials"); - e.printStackTrace(); - throw new CredentialsNotAvailableException("config.27", new Object[]{e.getMessage()}, e); - } - } - - public X509Credential getIDPAssertionSigningCredential() - throws CredentialsNotAvailableException { - try { - if (keyStore == null) - keyStore = KeyStoreUtils.loadKeyStore(getKeyStoreFilePath(), - getKeyStorePassword()); - - KeyStoreX509CredentialAdapter credentials = new KeyStoreX509CredentialAdapter( - keyStore, getSignatureKeyAlias(), getSignatureKeyPassword().toCharArray()); - - credentials.setUsageType(UsageType.SIGNING); - if (credentials.getPrivateKey() == null && credentials.getSecretKey() == null) { - log.error(getFriendlyName() + " Assertion Signing credentials is not found or contains no PrivateKey."); - throw new CredentialsNotAvailableException("config.27", new Object[]{getFriendlyName() + " Assertion Signing credentials (Alias: " - + getSignatureKeyAlias() + ") is not found or contains no PrivateKey."}); - - } - - return (X509Credential) credentials; - } catch (Exception e) { - log.error("Failed to generate " + getFriendlyName() + " Assertion Signing credentials"); - e.printStackTrace(); - throw new CredentialsNotAvailableException("config.27", new Object[]{e.getMessage()}, e); - } - } - - public X509Credential getIDPAssertionEncryptionCredential() - throws CredentialsNotAvailableException { - try { - if (keyStore == null) - keyStore = KeyStoreUtils.loadKeyStore(getKeyStoreFilePath(), - getKeyStorePassword()); - - //if no encryption key is configured return null - if (StringUtils.isEmpty(getEncryptionKeyAlias())) - return null; - - KeyStoreX509CredentialAdapter credentials = new KeyStoreX509CredentialAdapter( - keyStore, getEncryptionKeyAlias(), getEncryptionKeyPassword().toCharArray()); - - credentials.setUsageType(UsageType.ENCRYPTION); - - if (credentials.getPrivateKey() == null && credentials.getSecretKey() == null) { - log.error(getFriendlyName() + " Assertion Encryption credentials is not found or contains no PrivateKey."); - throw new CredentialsNotAvailableException("config.27", new Object[]{getFriendlyName() + " Assertion Encryption credentials (Alias: " - + getEncryptionKeyAlias() + ") is not found or contains no PrivateKey."}); - - } - - return (X509Credential) credentials; - - } catch (Exception e) { - log.error("Failed to generate " + getFriendlyName() + " Assertion Encryption credentials"); - e.printStackTrace(); - throw new CredentialsNotAvailableException("config.27", new Object[]{e.getMessage()}, e); - } - } - - public static Signature getIDPSignature(Credential credentials) { - PrivateKey privatekey = credentials.getPrivateKey(); - Signature signer = SAML2Utils.createSAMLObject(Signature.class); - - if (privatekey instanceof RSAPrivateKey) { - signer.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256); - - } else if (privatekey instanceof ECPrivateKey) { - signer.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA256); - - } else { - log.warn("Could NOT evaluate the Private-Key type from " + credentials.getEntityId() + " credential."); - - - } - - signer.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); - signer.setSigningCredential(credentials); - return signer; - - } +import at.gv.egiz.eaaf.modules.pvp2.exception.SamlSigningException; +import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.EaafKeyStoreX509CredentialAdapter; + +import org.apache.commons.lang3.StringUtils; +import org.apache.xml.security.algorithms.JCEMapper; +import org.opensaml.security.credential.UsageType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ResourceLoader; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public abstract class AbstractCredentialProvider implements IPvp2CredentialProvider { + + private static final String TRUSTED_CERTIFICATES_OPERATION = "Trusted Certificate Entries"; + + @Autowired + protected ResourceLoader resourceLoader; + @Autowired + protected IConfiguration basicConfig; + + @Autowired + private EaafKeyStoreFactory keyStoreFactory; + + private Pair<KeyStore, Provider> keyStore = null; + + /** + * Get a friendlyName for this keyStore implementation This friendlyName is used + * for logging. + * + * @return keyStore friendlyName + */ + public final String getFriendlyName() { + try { + return getBasicKeyStoreConfig().getFriendlyName(); + + } catch (final EaafConfigurationException e) { + return "No KeyStoreName"; + + } + + } + + /** + * Get the basic KeyStore configuration object for this SAML2 credential. + * + * @return KeyStore configuration object + * @throws EaafConfigurationException In case of a configuration error + */ + @Nonnull + public abstract KeyStoreConfiguration getBasicKeyStoreConfig() throws EaafConfigurationException; + + /** + * Get alias of key for metadata signing. + * + * @return key alias + */ + public abstract String getMetadataKeyAlias(); + + /** + * Get password of key for metadata signing. + * + * @return key password + */ + public abstract String getMetadataKeyPassword(); + + /** + * Get alias of key for request/response signing. + * + * @return key alias + */ + public abstract String getSignatureKeyAlias(); + + /** + * Get password of key for request/response signing. + * + * @return key password + */ + public abstract String getSignatureKeyPassword(); + + /** + * Get alias of key for IDP response encryption. + * + * @return key alias + */ + public abstract String getEncryptionKeyAlias(); + + /** + * Get password of key for IDP response encryption. + * + * @return key password + */ + public abstract String getEncryptionKeyPassword(); + + /** + * Get Credentials to sign metadata. + * + * @return Credentials + * @throws CredentialsNotAvailableException In case of a credential error + */ + @Override + public EaafX509Credential getMetaDataSigningCredential() throws CredentialsNotAvailableException { + try { + final EaafKeyStoreX509CredentialAdapter credentials = new EaafKeyStoreX509CredentialAdapter( + keyStore.getFirst(), getMetadataKeyAlias(), + getPassCharArrayOrNull(getMetadataKeyPassword()), getFriendlyName()); + credentials.setUsageType(UsageType.SIGNING); + credentials.setSignatureAlgorithmForSigning(selectSigningAlgorithm(credentials)); + credentials.setKeyEncryptionAlgorithmForDataEncryption(selectKeyEncryptionAlgorithm(credentials)); + return credentials; + + } catch (final Exception e) { + throw new CredentialsNotAvailableException("internal.pvp.01", + new Object[] { getFriendlyName(), getMetadataKeyAlias() }, e); + + } + } + + /** + * Get Credentials to sign SAML2 messages, like AuthnRequest, Response, + * Assertions as some examples. + * + * @return Credentials + * @throws CredentialsNotAvailableException In case of a credential error + */ + @Override + public EaafX509Credential getMessageSigningCredential() throws CredentialsNotAvailableException { + try { + final EaafKeyStoreX509CredentialAdapter credentials = new EaafKeyStoreX509CredentialAdapter( + keyStore.getFirst(), getSignatureKeyAlias(), + getPassCharArrayOrNull(getSignatureKeyPassword()), getFriendlyName()); + credentials.setUsageType(UsageType.SIGNING); + credentials.setSignatureAlgorithmForSigning(selectSigningAlgorithm(credentials)); + credentials.setKeyEncryptionAlgorithmForDataEncryption(selectKeyEncryptionAlgorithm(credentials)); + return credentials; + + } catch (final Exception e) { + throw new CredentialsNotAvailableException("internal.pvp.01", + new Object[] { getFriendlyName(), getSignatureKeyAlias() }, e); + + } + } + + /** + * Get Credentials to encrypt messages, like Assertion as example. + * + * @return Credentials + * @throws CredentialsNotAvailableException In case of a credential error + */ + @Override + public EaafX509Credential getMessageEncryptionCredential() + throws CredentialsNotAvailableException { + // if no encryption key is configured return null + if (StringUtils.isEmpty(getEncryptionKeyAlias())) { + return null; + } + + try { + final EaafKeyStoreX509CredentialAdapter credentials = new EaafKeyStoreX509CredentialAdapter( + keyStore.getFirst(), getEncryptionKeyAlias(), + getPassCharArrayOrNull(getEncryptionKeyPassword()), getFriendlyName()); + credentials.setUsageType(UsageType.ENCRYPTION); + credentials.setSignatureAlgorithmForSigning(selectSigningAlgorithm(credentials)); + credentials.setKeyEncryptionAlgorithmForDataEncryption(selectKeyEncryptionAlgorithm(credentials)); + return credentials; + + } catch (final Exception e) { + throw new CredentialsNotAvailableException("internal.pvp.01", + new Object[] { getFriendlyName(), getEncryptionKeyAlias() }, e); + + } + + } + + /** + * Get a List of trusted {@link X509Certificate} that are available in this + * KeyStore. + * + * @return List of trusted {@link X509Certificate}, or an emptry {@link List} if + * no certificates are available + * @throws CredentialsNotAvailableException In case of a KeyStore error + */ + @Override + @Nonnull + public List<X509Certificate> getTrustedCertificates() + throws CredentialsNotAvailableException { + final List<X509Certificate> result = new ArrayList<>(); + + try { + final Enumeration<String> aliases = keyStore.getFirst().aliases(); + while (aliases.hasMoreElements()) { + final String el = aliases.nextElement(); + log.trace("Process TrustStoreEntry: " + el); + if (keyStore.getFirst().isCertificateEntry(el)) { + final Certificate cert = keyStore.getFirst().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"); + + } + } + } + } catch (final KeyStoreException e) { + throw new CredentialsNotAvailableException("internal.pvp.01", + new Object[] { getFriendlyName(), TRUSTED_CERTIFICATES_OPERATION }, e); + } + + return Collections.unmodifiableList(result); + + } + + @PostConstruct + private void initialize() throws Exception { + try { + final KeyStoreConfiguration keyStoreConfig = getBasicKeyStoreConfig(); + keyStore = keyStoreFactory.buildNewKeyStore(keyStoreConfig); + + if (JCEMapper.getProviderId() != null && keyStore.getSecond() != null + && !JCEMapper.getProviderId().equals(keyStore.getSecond().getName())) { + log.error("OpenSAML3.x can ONLY use a single type of CryptoProvider in an application. " + + "Can NOT set: {}, because {} was already set", keyStore.getSecond().getName(), + JCEMapper.getProviderId()); + throw new EaafConfigurationException(EaafKeyStoreFactory.ERRORCODE_06, + new Object[] { keyStoreConfig.getFriendlyName(), + "OpenSAML3.x can ONLY use a single type of CryptoProvider" }); + + } + + // Set JCEMapper only in case of HSM based KeyStores because Software KeyStores + // can use + // the default SecurityProvider system in OpenSAML3.x signing engine + if (keyStore.getSecond() != null + && JCEMapper.getProviderId() == null) { + log.info("Register CryptoProvider: {} as defaut for OpenSAML3.x", + keyStore.getSecond().getName()); + JCEMapper.setProviderId(keyStore.getSecond().getName()); + + } + + } catch (final EaafException e) { + log.error("Can not initialize KeyStore for eIDAS authentication client.", e); + throw e; + + } + } + + private String selectSigningAlgorithm(EaafKeyStoreX509CredentialAdapter credentials) + throws SamlSigningException { + return Saml2Utils.getKeyOperationAlgorithmFromCredential( + credentials, + basicConfig.getBasicConfiguration( + PvpConstants.CONFIG_PROP_SEC_SIGNING_RSA_ALG, + PvpConstants.DEFAULT_SIGNING_METHODE_RSA), + basicConfig.getBasicConfiguration( + PvpConstants.CONFIG_PROP_SEC_SIGNING_EC_ALG, + PvpConstants.DEFAULT_SIGNING_METHODE_EC)); + } + + private String selectKeyEncryptionAlgorithm(EaafKeyStoreX509CredentialAdapter credentials) + throws SamlSigningException { + return Saml2Utils.getKeyOperationAlgorithmFromCredential( + credentials, + basicConfig.getBasicConfiguration( + PvpConstants.CONFIG_PROP_SEC_ENCRYPTION_KEY_RSA_ALG, + PvpConstants.DEFAULT_ASYM_ENCRYPTION_METHODE_RSA), + basicConfig.getBasicConfiguration( + PvpConstants.CONFIG_PROP_SEC_ENCRYPTION_KEY_EC_ALG, + PvpConstants.DEFAULT_ASYM_ENCRYPTION_METHODE_EC)); + } + + private char[] getPassCharArrayOrNull(String metadataKeyPassword) { + char[] keyPassChar = null; + if (metadataKeyPassword != null) { + keyPassChar = metadataKeyPassword.toCharArray(); + + } + + return keyPassChar; + } + } |