/* * 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: * 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. */ package at.gv.egiz.eaaf.modules.pvp2.impl.utils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; 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.impl.utils.KeyStoreUtils; 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.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.context.annotation.Lazy; import org.springframework.core.io.Resource; 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; 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 In case of an invalid filepath */ @Nonnull 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(); /** * 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, 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, 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, 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 getTrustedCertificates() throws CredentialsNotAvailableException { final List result = new ArrayList<>(); try { final Enumeration aliases = keyStore.aliases(); while (aliases.hasMoreElements()) { final String el = aliases.nextElement(); log.trace("Process TrustStoreEntry: " + el); if (keyStore.isCertificateEntry(el)) { 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"); } } } } catch (final KeyStoreException e) { throw new CredentialsNotAvailableException("internal.pvp.01", new Object[] { getFriendlyName(), TRUSTED_CERTIFICATES_OPERATION }, e); } return Collections.unmodifiableList(result); } private X509Certificate getRootCertificate() throws CertificateException { String pem = "-----BEGIN CERTIFICATE-----\n" + "MIIDFDCCAfygAwIBAgIEXIjqbjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARy\n" + "b290MB4XDTE5MDMxMzExMzMwMloXDTIwMDMxMjExMzMwMlowDzENMAsGA1UEAwwE\n" + "cm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKijWXfb7bvQ7CIw\n" + "FuyuPUz+aN7uBgSSnpYamtzjagacdtGR2V2OVHfjVHhw+cSoNPaEEV2x0O9A+w8F\n" + "FCatBT30l7/2scuJmrdXYlIhd17NU6HG/HKYvRYROkXrprsbdZobWqdF/zShLIvv\n" + "0bwconAu7AxwlDgNJQz2pL0e94OkCT5rZyA4HFgzJ34XynXaCMbUbVXxVk6EuNaX\n" + "hbyco0qhjOjSn7Rwk3iXp21V4vcYRVq44sG3ieU6jHq6LKmYSGJ1y0yv9ADYJwSp\n" + "jCzRbOEKe/7QVvZIyzzqjhO3SAHONuFNX0V6zPCgMCjUOgHuOIEKLJR9p0YYYocX\n" + "GBLcVuECAwEAAaN4MHYwDAYDVR0TBAUwAwEB/zA6BgNVHSMEMzAxgBQueuDUlVbB\n" + "LBjP+iRFr6lUDBh58qETpBEwDzENMAsGA1UEAwwEcm9vdIIEXIjqbjAdBgNVHQ4E\n" + "FgQULnrg1JVWwSwYz/okRa+pVAwYefIwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEB\n" + "CwUAA4IBAQCEYSVpiKFO7FjCqTlkxNBY7e7891dq43DfX9i/Hb/AIvZDPe/RC46t\n" + "EXd9LN7QYaXe35U5ZD1q7qmK7NoFJ9zp4D4mxA2iiBHz40GnRt+0abNdQiyw913W\n" + "s/VIElAOv0tvCw+3SwzvLRU/AVCM1weW6IUbYv/Ty5zmLBsG3do3MmVF3cqXho2m\n" + "pNaiubuaUsR8Ms1LqIr6R7Yf8MKSrgYWCOw60gj5O64RHnEJli52D+S/8Cue5GvG\n" + "ECckmgLgGsRcWfFwRqqS7+XWt8Dv8xxD5vurvcs547Hn28kSHtF2i+KYLDVH2QjN\n" + "dbO0qgEJlMPi7oGrsNjIkndrWseNrPA4\n" + "-----END CERTIFICATE-----\n"; return (java.security.cert.X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(pem.getBytes())); } @Lazy @PostConstruct private void initialize() throws Exception { try { final HsmFacadeProvider provider = HsmFacadeProvider.Companion.getInstance(); String clientUsername = "shibboleth-idp"; String clientPassword = "supersecret123"; String host = "localhost"; int port = 9000; String hsmName = "software"; String keyStoreName = "shibboleth"; String keyStoreAlias = "shibboleth-sign"; provider.init(getRootCertificate(), clientUsername, clientPassword, host, port, hsmName); Security.addProvider(provider); //Security.insertProviderAt(provider, 1); JCEMapper.setProviderId(provider.getName()); keyStore = KeyStore.getInstance("RemoteKeyStore", "HsmFacade"); keyStore.load(new RemoteKeyStoreLoadParameter(keyStoreName)); if (keyStore == null) { throw new EaafConfigurationException("module.00", new Object[] { getFriendlyName(), "KeyStore initialization failed. Maybe wrong password" }); } } catch (IOException | KeyStoreException | 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; } }