diff --git a/core/org.wso2.carbon.core/pom.xml b/core/org.wso2.carbon.core/pom.xml index 90cb6e262e1..81c94aaf025 100644 --- a/core/org.wso2.carbon.core/pom.xml +++ b/core/org.wso2.carbon.core/pom.xml @@ -94,6 +94,10 @@ org.wso2.carbon org.wso2.carbon.utils + + org.wso2.carbon.utils + org.wso2.carbon.database.utils + org.wso2.carbon.crypto org.wso2.carbon.crypto.api @@ -248,6 +252,7 @@ org.wso2.carbon.registry.core.service, org.wso2.carbon.user.core.*; version=0.0.0, org.bouncycastle.*; version="${imp.pkg.version.bcp}", + org.wso2.carbon.database.utils.*;version="${org.wso2.carbon.database.utils.version.range}", *;resolution:=optional diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/internal/KeyStoreMgtComponent.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/internal/KeyStoreMgtComponent.java new file mode 100644 index 00000000000..0eaffecce2c --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/internal/KeyStoreMgtComponent.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.internal; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.wso2.carbon.base.ServerConfiguration; +import org.wso2.carbon.core.keystore.KeyStoreManagementException; +import org.wso2.carbon.core.keystore.KeyStoreManagementService; +import org.wso2.carbon.core.keystore.KeyStoreManagementServiceImpl; +import org.wso2.carbon.user.core.service.RealmService; +import org.wso2.carbon.utils.ConfigurationContextService; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; + +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.KEYSTORE_DATASOURCE; + +@Component( + name = "keystore.mgt.service.component", + immediate = true +) +public class KeyStoreMgtComponent { + + private static final Log log = LogFactory.getLog(KeyStoreMgtComponent.class); + + @Activate + protected void activate(ComponentContext ctxt) { + + try { + initDataSource(); + BundleContext bundleCtx = ctxt.getBundleContext(); + bundleCtx.registerService(KeyStoreManagementService.class.getName(), new KeyStoreManagementServiceImpl(), + null); + log.debug("Security Mgt bundle is activated"); + } catch (KeyStoreManagementException e) { + log.error("Failed to activate SecurityMgtServiceComponent", e); + } + } + + @Deactivate + protected void deactivate(ComponentContext ctxt) { + + log.debug("Security Mgt bundle is deactivated"); + } + + @Reference( + name = "config.context.service", + service = ConfigurationContextService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetConfigurationContextService" + ) + protected void setConfigurationContextService(ConfigurationContextService contextService) { + + if (log.isDebugEnabled()) { + log.debug("Setting the ConfigurationContext"); + } + KeyStoreMgtDataHolder.setConfigurationContextService(contextService); + } + + @Reference( + name = "user.realmservice.default", + service = RealmService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetRealmService" + ) + protected void setRealmService(RealmService realmService) { + + if (log.isDebugEnabled()) { + log.debug("Setting the RealmService"); + } + KeyStoreMgtDataHolder.setRealmService(realmService); + } + + protected void unsetRealmService(RealmService realmService) { + + if (log.isDebugEnabled()) { + log.debug("Unsetting the RealmService"); + } + KeyStoreMgtDataHolder.setRealmService(null); + } + + protected void unsetConfigurationContextService(ConfigurationContextService contextService) { + + if (log.isDebugEnabled()) { + log.debug("Unsetting the ConfigurationContext"); + } + KeyStoreMgtDataHolder.setConfigurationContextService(null); + } + + private void initDataSource() throws KeyStoreManagementException { + + String dataSourceName = ServerConfiguration.getInstance() + .getFirstProperty(KEYSTORE_DATASOURCE); + Context ctx = null; + try { + ctx = new InitialContext(); + DataSource dataSource = (DataSource) ctx.lookup(dataSourceName); + KeyStoreMgtDataHolder.setDataSource(dataSource); + } catch (NamingException e) { + throw new KeyStoreManagementException(e.getMessage()); + } + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/internal/KeyStoreMgtDataHolder.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/internal/KeyStoreMgtDataHolder.java new file mode 100644 index 00000000000..b6948659c39 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/internal/KeyStoreMgtDataHolder.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.internal; + +import org.apache.axis2.context.ConfigurationContext; +import org.wso2.carbon.core.keystore.KeyStoreManagementException; +import org.wso2.carbon.registry.core.Resource; +import org.wso2.carbon.user.core.service.RealmService; +import org.wso2.carbon.utils.ConfigurationContextService; + +import java.util.HashMap; +import java.util.Map; + +import javax.sql.DataSource; + +public class KeyStoreMgtDataHolder { + + private static RealmService realmService; + private static ConfigurationContextService ccService; + private static DataSource dataSource; + private static Map policyResourceMap = new HashMap<>(); + + private KeyStoreMgtDataHolder() { + + } + + public static RealmService getRealmService() throws KeyStoreManagementException { + + if (realmService == null) { + throw new KeyStoreManagementException("The main user realm is null"); + } + return realmService; + } + + public static void setRealmService(RealmService realmService) { + + KeyStoreMgtDataHolder.realmService = realmService; + } + + public static ConfigurationContext getConfigurationContext() throws Exception { + + if (ccService == null) { + throw new KeyStoreManagementException("CC service is null"); + } + return ccService.getClientConfigContext(); + } + + public static void setConfigurationContextService(ConfigurationContextService ccService) { + + KeyStoreMgtDataHolder.ccService = ccService; + } + + public static void addPolicyResource(String location, Resource resource) { + + policyResourceMap.put(location, resource); + } + + public static DataSource getDataSource() { + + return dataSource; + } + + public static void setDataSource(DataSource dataSource) { + + KeyStoreMgtDataHolder.dataSource = dataSource; + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreAdmin.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreAdmin.java new file mode 100644 index 00000000000..e05db86dc4e --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreAdmin.java @@ -0,0 +1,1259 @@ +/* + * Copyright (c) 2010, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore; + +import org.apache.axiom.om.util.Base64; +import org.apache.axis2.context.MessageContext; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.base.ServerConfiguration; +import org.wso2.carbon.core.RegistryResources; +import org.wso2.carbon.core.keystore.util.KeyStoreMgtUtil; +import org.wso2.carbon.core.util.CryptoException; +import org.wso2.carbon.core.util.CryptoUtil; +import org.wso2.carbon.core.util.KeyStoreManager; +import org.wso2.carbon.core.util.KeyStoreUtil; +import org.wso2.carbon.core.keystore.dao.KeyStoreDAO; +import org.wso2.carbon.core.keystore.dao.PubCertDAO; +import org.wso2.carbon.core.keystore.dao.impl.KeyStoreDAOImpl; +import org.wso2.carbon.core.keystore.dao.impl.PubCertDAOImpl; +import org.wso2.carbon.core.keystore.model.KeyStoreModel; +import org.wso2.carbon.core.keystore.service.CertData; +import org.wso2.carbon.core.keystore.service.CertDataDetail; +import org.wso2.carbon.core.keystore.service.KeyStoreData; +import org.wso2.carbon.core.keystore.service.PaginatedCertData; +import org.wso2.carbon.core.keystore.service.PaginatedKeyStoreData; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.utils.CarbonUtils; +import org.wso2.carbon.utils.multitenancy.MultitenantConstants; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.text.Format; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; + +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.CACHING_PAGE_SIZE; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_MESSAGE_RETRIEVE_KEYSTORE; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_MESSAGE_RETRIEVE_PUBLIC_CERT; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ITEMS_PER_PAGE; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.KEY_STORES; + +/** + * Key Store Admin class. + */ +public class KeyStoreAdmin { + + // todo: add public method comments + + //trust store + public static final String SERVER_TRUSTSTORE_FILE = "Security.TrustStore.Location"; + public static final String SERVER_TRUSTSTORE_PASSWORD = "Security.TrustStore.Password"; + public static final String SERVER_TRUSTSTORE_TYPE = "Security.TrustStore.Type"; + + private static final String PATH_SEPARATOR = "/"; + + // This is used as the alternative for RegistryResources.SecurityManagement.PRIMARY_KEYSTORE_PHANTOM_RESOURCE. + private static final String ALTERNATE_PRIMARY_KEYSTORE_PHANTOM_RESOURCE = "carbon-primary-ks"; + + private static final Log log = LogFactory.getLog(KeyStoreAdmin.class); + private static final String PROP_TRUST_STORE_UPDATE_REQUIRED = + "org.wso2.carbon.identity.core.util.TRUST_STORE_UPDATE_REQUIRED"; + private int tenantId; + private String tenantUUID; + private boolean includeCert = false; + private KeyStoreDAO keyStoreDAO; + private PubCertDAO pubCertDAO; + private final String trustStoreLocation; + private final String trustStorePassword; + + public KeyStoreAdmin(int tenantId) { + + ServerConfiguration config = ServerConfiguration.getInstance(); + trustStoreLocation = config.getFirstProperty(SERVER_TRUSTSTORE_FILE); + trustStorePassword = config.getFirstProperty(SERVER_TRUSTSTORE_PASSWORD); + this.tenantId = tenantId; + try { + tenantUUID = KeyStoreMgtUtil.getTenantUUID(tenantId); + keyStoreDAO = new KeyStoreDAOImpl(); + pubCertDAO = new PubCertDAOImpl(); + } catch (KeyStoreManagementException e) { + log.error("Error while retrieving the tenant ID.", e); + } catch (UserStoreException e) { + log.error("Error while retrieving the tenant UUID.", e); + } + } + + public boolean isIncludeCert() { + + return includeCert; + } + + public void setIncludeCert(boolean includeCert) { + + this.includeCert = includeCert; + } + + /** + * Method to retrieve keystore data. + * + * @param isSuperTenant Indication whether the querying super tenant data. + * @return A key store data array. + * @throws KeyStoreManagementException Throws if an error occurred while getting the key stores. + */ + public KeyStoreData[] getKeyStores(boolean isSuperTenant) throws KeyStoreManagementException { + + CarbonUtils.checkSecurity(); + KeyStoreData[] names = new KeyStoreData[0]; + + try { + List keyStores = keyStoreDAO.getKeyStores(tenantUUID); + List keyStoreDataList = new ArrayList<>(); + if (!CollectionUtils.isEmpty(keyStores)) { + for (KeyStoreModel keyStoreModel : keyStores) { + if (ALTERNATE_PRIMARY_KEYSTORE_PHANTOM_RESOURCE.equals(keyStoreModel.getFileName())) { + continue; + } + + KeyStoreData data = new KeyStoreData(); + data.setKeyStoreName(keyStoreModel.getFileName()); + data.setKeyStoreType(keyStoreModel.getType()); + data.setProvider(keyStoreModel.getProvider()); + + String alias = keyStoreModel.getPrivateKeyAlias(); + if (alias != null) { + data.setPrivateStore(true); + } else { + data.setPrivateStore(false); + } + + if (!isSuperTenant) { + Optional pubCertId = + keyStoreDAO.getPubCertIdFromKeyStore(tenantUUID, keyStoreModel.getFileName()); + + pubCertId.flatMap(id -> { + try { + return pubCertDAO.getPubCert(id); + } catch (KeyStoreManagementException e) { + log.error(ERROR_MESSAGE_RETRIEVE_PUBLIC_CERT.getMessage(), e); + return Optional.empty(); + } + }) + .ifPresent(pubCert -> { + String fileName = generatePubCertFileName(KEY_STORES + PATH_SEPARATOR + + keyStoreModel.getFileName(), pubCert.getFileNameAppender()); + if (MessageContext.getCurrentMessageContext() != null) { + String pubKeyFilePath = KeyStoreMgtUtil.dumpCert( + MessageContext.getCurrentMessageContext().getConfigurationContext(), + pubCert.getContent(), fileName); + data.setPubKeyFilePath(pubKeyFilePath); + } + }); + } + keyStoreDataList.add(data); + } + } + + // Prepare the next position for the super tenant keystore data. + names = new KeyStoreData[keyStoreDataList.size() + 1]; + Iterator keyStoreDataIterator = keyStoreDataList.iterator(); + int count = 0; + while (keyStoreDataIterator.hasNext()) { + names[count] = keyStoreDataIterator.next(); + count++; + } + + if (isSuperTenant) { + KeyStoreData data = new KeyStoreData(); + ServerConfiguration config = ServerConfiguration.getInstance(); + String fileName = config + .getFirstProperty(RegistryResources.SecurityManagement.SERVER_PRIMARY_KEYSTORE_FILE); + String type = config + .getFirstProperty(RegistryResources.SecurityManagement.SERVER_PRIMARY_KEYSTORE_TYPE); + String name = KeyStoreUtil.getKeyStoreFileName(fileName); + data.setKeyStoreName(name); + data.setKeyStoreType(type); + data.setProvider(" "); + data.setPrivateStore(true); + + names[count] = data; + } + return names; + } catch (KeyStoreManagementException e) { + log.error(ERROR_MESSAGE_RETRIEVE_KEYSTORE.getMessage(), e); + throw new KeyStoreManagementException(ERROR_MESSAGE_RETRIEVE_KEYSTORE.getMessage(), e); + } + } + + /** + * Method to add keystore when a file path is given instead of file data. + * + * @param filePath File path of the keystore data. + * @param filename Name of the keystore. + * @param password Password of the keystore. + * @param provider Provider of the keystore. + * @param type Type of the keystore. + * @param pvtKeyPass Password of the private key. + * @throws KeyStoreManagementException Throws if an error occurred while adding the keystore. + */ + public void addKeyStoreWithFilePath(String filePath, String filename, String password, + String provider, String type, String pvtKeyPass) + throws KeyStoreManagementException { + + try { + addKeyStore(readBytesFromFile(filePath), filename, password, provider, type, pvtKeyPass); + } catch (IOException e) { + throw new KeyStoreManagementException("Error while loading keystore from file " + filePath, e); + } + + } + + /** + * Method to add keystore when a file data is given. + * + * @param fileData File data of the keystore. + * @param filename Name of the keystore. + * @param password Password of the keystore. + * @param provider Provider of the keystore. + * @param type Type of the keystore. + * @param pvtKeyPass Password of the private key. + * @throws KeyStoreManagementException Throws if an error occurred while adding the keystore. + */ + public void addKeyStore(String fileData, String filename, String password, String provider, + String type, String pvtKeyPass) throws KeyStoreManagementException { + + byte[] content = Base64.decode(fileData); + addKeyStore(content, filename, password, provider, type, pvtKeyPass); + } + + /** + * Method to add keystore when a byte array is given for the file data. + * + * @param content Byte array of the keystore data. + * @param filename Name of the keystore. + * @param password Password of the keystore. + * @param provider Provider of the keystore. + * @param type Type of the keystore. + * @param pvtKeyPass Password of the private key. + * @throws KeyStoreManagementException Throws if an error occurred while adding the keystore. + */ + public void addKeyStore(byte[] content, String filename, String password, String provider, + String type, String pvtKeyPass) throws KeyStoreManagementException { + + if (filename == null) { + throw new KeyStoreManagementException("Key Store name can't be null"); + } + try { + if (KeyStoreUtil.isPrimaryStore(filename)) { + throw new KeyStoreManagementException("Key store " + filename + " already available"); + } + if (isTrustStore(filename)) { + throw new KeyStoreManagementException("Key store " + filename + " already available"); + } + if (isExistKeyStore(filename)) { + throw new KeyStoreManagementException("Key store " + filename + " already available"); + } + + KeyStore keyStore = KeyStore.getInstance(type); + keyStore.load(new ByteArrayInputStream(content), password.toCharArray()); + + // check for more private keys + Enumeration enumeration = keyStore.aliases(); + String pvtKeyAlias = null; + while (enumeration.hasMoreElements()) { + String alias = (String) enumeration.nextElement(); + if (keyStore.isKeyEntry(alias)) { + pvtKeyAlias = alias; + } + } + + // just to test weather pvt key password is correct. + keyStore.getKey(pvtKeyAlias, pvtKeyPass.toCharArray()); + + CryptoUtil cryptoUtil = CryptoUtil.getDefaultCryptoUtil(); + + KeyStoreModel data; + + if (pvtKeyAlias == null) { + data = new KeyStoreModel.KeyStoreModelBuilder() + .fileName(filename) + .type(type) + .provider(provider) + .password(cryptoUtil.encryptAndBase64Encode(password.getBytes()).toCharArray()) + .content(content) + .build(); + } else { + data = new KeyStoreModel.KeyStoreModelBuilder() + .fileName(filename) + .type(type) + .provider(provider) + .password(cryptoUtil.encryptAndBase64Encode(password.getBytes()).toCharArray()) + .privateKeyAlias(pvtKeyAlias) + .privateKeyPass(cryptoUtil.encryptAndBase64Encode(pvtKeyPass.getBytes()).toCharArray()) + .content(content) + .build(); + } + + keyStoreDAO.addKeyStore(tenantUUID, data); + } catch (KeyStoreManagementException | CryptoException | IOException | NoSuchAlgorithmException | + CertificateException | UnrecoverableKeyException | KeyStoreException e) { + String msg = "Error when adding a keyStore"; + log.error(msg, e); + throw new KeyStoreManagementException(msg, e); + } + } + + /** + * Method to add trust store when trust store file data is given. + * + * @param fileData File data of the trust store. + * @param filename Name of the trust store. + * @param password Password of the trust store. + * @param provider Provider of the trust store. + * @param type Type of the trust store. + * @throws KeyStoreManagementException Throws if an error occurred while adding the trust store. + */ + public void addTrustStore(String fileData, String filename, String password, String provider, + String type) throws KeyStoreManagementException { + + byte[] content = Base64.decode(fileData); + addTrustStore(content, filename, password, provider, type); + } + + /** + * Method to add trust store when trust store file path is given for the file data. + * + * @param content Byte array of the trust store data. + * @param filename Name of the trust store. + * @param password Password of the trust store. + * @param provider Provider of the trust store. + * @param type Type of the trust store. + * @throws KeyStoreManagementException Throws if an error occurred while adding the trust store. + */ + public void addTrustStore(byte[] content, String filename, String password, String provider, String type) + throws KeyStoreManagementException { + + if (filename == null) { + throw new KeyStoreManagementException("Key Store name can't be null"); + } + try { + if (KeyStoreUtil.isPrimaryStore(filename)) { + throw new KeyStoreManagementException("Key store " + filename + " already available"); + } + + if (isExistKeyStore(filename)) { + throw new KeyStoreManagementException("Key store " + filename + " already available"); + } + + KeyStore keyStore = KeyStore.getInstance(type); + keyStore.load(new ByteArrayInputStream(content), password.toCharArray()); + CryptoUtil cryptoUtil = CryptoUtil.getDefaultCryptoUtil(); + + KeyStoreModel data = new KeyStoreModel.KeyStoreModelBuilder() + .fileName(filename) + .type(type) + .provider(provider) + .password(cryptoUtil.encryptAndBase64Encode(password.getBytes()).toCharArray()) + .content(content) + .build(); + keyStoreDAO.addKeyStore(tenantUUID, data); + } catch (KeyStoreManagementException e) { + throw e; + } catch (Exception e) { + String msg = "Error when adding a trustStore"; + log.error(msg, e); + throw new KeyStoreManagementException(msg, e); + } + } + + /** + * Method to delete a key store when a store data file name is given. + * + * @param keyStoreName Name of the key store. + * @throws KeyStoreManagementException Throws if an error occurred while adding the trust store. + */ + public void deleteStore(String keyStoreName) throws KeyStoreManagementException { + + try { + + if (StringUtils.isBlank(keyStoreName)) { + throw new KeyStoreManagementException("Key Store name can't be null"); + } + + if (KeyStoreUtil.isPrimaryStore(keyStoreName)) { + throw new KeyStoreManagementException("Not allowed to delete the primary key store : " + + keyStoreName); + } + if (isTrustStore(keyStoreName)) { + throw new KeyStoreManagementException("Not allowed to delete the trust store : " + + keyStoreName); + } + + // TODO: verify that this behaves as expected + if (keyStoreDAO.getPubCertIdFromKeyStore(tenantUUID, keyStoreName).isPresent()) { + throw new KeyStoreManagementException("Key store : " + keyStoreName + + " is already in use and can't be deleted"); + } + + keyStoreDAO.deleteKeyStore(tenantUUID, keyStoreName); + } catch (KeyStoreManagementException e) { + // Catch KeyStoreManagementException and throw the expected KeyStoreManagementException. + String msg = "Error when deleting a keyStore"; + log.error(msg, e); + throw new KeyStoreManagementException(msg, e); + } + } + + /** + * Method to import a public certificate to the keystore. + * + * @param fileName Name of the certificate. + * @param certData Certificate data. + * @param keyStoreName Name of the keystore. + * @throws KeyStoreManagementException Throws if an error occurred while importing the public certificate to the + * trust store. + */ + public void importCertToStore(String fileName, String certData, String keyStoreName) + throws KeyStoreManagementException { + + try { + if (keyStoreName == null) { + throw new KeyStoreManagementException("Key Store name can't be null"); + } + + KeyStore ks = getKeyStore(keyStoreName); + X509Certificate cert = extractCertificate(certData); + + if (ks.getCertificateAlias(cert) != null) { + // We already have this certificate in the key store - ignore + // adding it twice + return; + } + + ks.setCertificateEntry(fileName, cert); + + updateKeyStore(keyStoreName, ks); + + if (isTrustStore(keyStoreName)) { + System.setProperty(PROP_TRUST_STORE_UPDATE_REQUIRED, "true"); + } + + } catch (KeyStoreManagementException e) { + throw e; + } catch (Exception e) { + String msg = "Error when importing cert to the keyStore"; + log.error(msg, e); + throw new KeyStoreManagementException(msg, e); + } + + } + + /** + * Method to import a public certificate to the keystore when a public certificate filename is not given. + * + * @param certData Certificate data. + * @param keyStoreName Name of the keystore. + * @return Returns the alias or the filename of the certificate. + * @throws KeyStoreManagementException Throws if an error occurred while importing the public certificate to the + * trust store. + */ + public String importCertToStore(String certData, String keyStoreName) + throws KeyStoreManagementException { + + String alias = null; + + try { + if (keyStoreName == null) { + throw new KeyStoreManagementException("Key Store name can't be null"); + } + + KeyStore ks = getKeyStore(keyStoreName); + X509Certificate cert = extractCertificate(certData); + + if (ks.getCertificateAlias(cert) != null) { + // We already have this certificate in the key store - ignore + // adding it twice + return null; + } + alias = cert.getSubjectDN().getName(); + ks.setCertificateEntry(alias, cert); + + updateKeyStore(keyStoreName, ks); + + if (isTrustStore(keyStoreName)) { + System.setProperty(PROP_TRUST_STORE_UPDATE_REQUIRED, "true"); + } + + return alias; + + } catch (KeyStoreManagementException e) { + throw e; + } catch (Exception e) { + String msg = "Error when importing cert to keyStore"; + log.error(msg, e); + throw new KeyStoreManagementException(msg); + } + } + + /** + * Remove public certificate from store. + * + * @param alias Alias of the certificate. + * @param keyStoreName Name of the keystore. + * @throws KeyStoreManagementException Throws if an error occurred while removing the public certificate from the + * trust store. + */ + public void removeCertFromStore(String alias, String keyStoreName) + throws KeyStoreManagementException { + + try { + if (keyStoreName == null) { + throw new KeyStoreManagementException("Key Store name can't be null"); + } + + KeyStore ks = getKeyStore(keyStoreName); + + if (ks.getCertificate(alias) == null) { + return; + } + + ks.deleteEntry(alias); + updateKeyStore(keyStoreName, ks); + + if (isTrustStore(keyStoreName)) { + System.setProperty(PROP_TRUST_STORE_UPDATE_REQUIRED, Boolean.TRUE.toString()); + } + } catch (KeyStoreManagementException e) { + throw e; + } catch (Exception e) { + String msg = "Error when removing cert from store"; + log.error(msg, e); + throw new KeyStoreManagementException(msg); + } + } + + /** + * Retrieve list of alias entries of a given keystore. + * + * @param keyStoreName Name of the keystore. + * @return Returns the list of alias entries of a given keystore. + * @throws KeyStoreManagementException Throws if an error occurred while getting the store entries. + */ + public String[] getStoreEntries(String keyStoreName) throws KeyStoreManagementException { + + String[] names; + try { + if (keyStoreName == null) { + throw new Exception("keystore name cannot be null"); + } + + //KeyStoreManager keyMan = KeyStoreManager.getInstance(tenantId); + KeyStore ks = getKeyStore(keyStoreName); + + Enumeration enm = ks.aliases(); + List lst = new ArrayList<>(); + while (enm.hasMoreElements()) { + lst.add(enm.nextElement()); + } + + names = lst.toArray(new String[lst.size()]); + } catch (KeyStoreManagementException e) { + throw e; + } catch (Exception e) { + String msg = "Error when getting store entries"; + log.error(msg, e); + throw new KeyStoreManagementException(msg); + } + + return names; + } + + /** + * This method will list 1. Certificate aliases 2. Private key alise 3. Private key value to a + * given keystore. + * + * @param keyStoreName The name of the keystore + * @return Instance of KeyStoreData. + * @throws KeyStoreManagementException will be thrown if an error occurs while getting the keystore. + */ + public KeyStoreData getKeystoreInfo(String keyStoreName) throws KeyStoreManagementException { + + try { + + if (keyStoreName == null) { + throw new Exception("keystore name cannot be null"); + } + + KeyStore keyStore; + String keyStoreType; + String privateKeyPassword = null; + if (KeyStoreUtil.isPrimaryStore(keyStoreName)) { + KeyStoreManager keyMan = KeyStoreManager.getInstance(tenantId); + keyStore = keyMan.getPrimaryKeyStore(); + ServerConfiguration serverConfig = ServerConfiguration.getInstance(); + keyStoreType = serverConfig + .getFirstProperty(RegistryResources.SecurityManagement.SERVER_PRIMARY_KEYSTORE_TYPE); + privateKeyPassword = serverConfig + .getFirstProperty(RegistryResources.SecurityManagement.SERVER_PRIVATE_KEY_PASSWORD); + } else if (isTrustStore(keyStoreName)) { + keyStore = getTrustStore(); + ServerConfiguration serverConfig = ServerConfiguration.getInstance(); + keyStoreType = serverConfig.getFirstProperty(SERVER_TRUSTSTORE_TYPE); + privateKeyPassword = serverConfig.getFirstProperty(SERVER_TRUSTSTORE_PASSWORD); + } else { + if (!isExistKeyStore(keyStoreName)) { + throw new KeyStoreManagementException("Key Store not found"); + } + KeyStoreModel resource = keyStoreDAO.getKeyStore(tenantUUID, keyStoreName).get(); + keyStore = getKeyStore(keyStoreName); + keyStoreType = resource.getType(); + + String encryptionPassphrase = String.valueOf(resource.getPrivateKeyPass()); + // todo: check the actual value is empty or null + if (encryptionPassphrase != null || encryptionPassphrase.isEmpty()) { + CryptoUtil util = CryptoUtil.getDefaultCryptoUtil(); + privateKeyPassword = new String(util.base64DecodeAndDecrypt(encryptionPassphrase)); + } + } + // Fill the information about the certificates + Enumeration aliases = keyStore.aliases(); + List certDataList = new ArrayList<>(); + Format formatter = new SimpleDateFormat("dd/MM/yyyy"); + + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + if (keyStore.isCertificateEntry(alias)) { + X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); + certDataList.add(fillCertData(cert, alias, formatter)); + } + } + + // Create a cert array + CertData[] certs = certDataList.toArray(new CertData[certDataList.size()]); + + // Create a KeyStoreData bean, set the name and fill in the cert information + KeyStoreData keyStoreData = new KeyStoreData(); + keyStoreData.setKeyStoreName(keyStoreName); + keyStoreData.setCerts(certs); + keyStoreData.setKeyStoreType(keyStoreType); + + aliases = keyStore.aliases(); + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + // There be only one entry in WSAS related keystores + if (keyStore.isKeyEntry(alias)) { + X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); + keyStoreData.setKey(fillCertData(cert, alias, formatter)); + PrivateKey key = (PrivateKey) keyStore.getKey(alias, privateKeyPassword + .toCharArray()); + String pemKey; + pemKey = "-----BEGIN PRIVATE KEY-----\n"; + pemKey += Base64.encode(key.getEncoded()); + pemKey += "\n-----END PRIVATE KEY-----"; + keyStoreData.setKeyValue(pemKey); + break; + + } + } + return keyStoreData; + } catch (Exception e) { + String msg = "Error has encounted while loading the keystore to the given keystore name " + + keyStoreName; + log.error(msg, e); + throw new KeyStoreManagementException(msg); + } + + } + + /** + * Retrieve private key of a given alias. + * + * @param alias Alias of the key. + * @param isSuperTenant Indication whether the querying super tenant data. + * @return Returns the private key of a given alias. + * @throws KeyStoreManagementException Throws if an error occurred while getting the private key. + */ + public Key getPrivateKey(String alias, boolean isSuperTenant) throws KeyStoreManagementException { + + KeyStoreData[] keystores = getKeyStores(isSuperTenant); + KeyStore keyStore = null; + String privateKeyPassowrd = null; + + try { + + for (int i = 0; i < keystores.length; i++) { + if (KeyStoreUtil.isPrimaryStore(keystores[i].getKeyStoreName())) { + KeyStoreManager keyMan = KeyStoreManager.getInstance(tenantId); + keyStore = keyMan.getPrimaryKeyStore(); + ServerConfiguration serverConfig = ServerConfiguration.getInstance(); + privateKeyPassowrd = serverConfig + .getFirstProperty(RegistryResources.SecurityManagement.SERVER_PRIVATE_KEY_PASSWORD); + return keyStore.getKey(alias, privateKeyPassowrd.toCharArray()); + } + } + } catch (Exception e) { + String msg = "Error has encounted while loading the key for the given alias " + alias; + log.error(msg, e); + throw new KeyStoreManagementException(msg); + } + return null; + } + + /** + * Fill certificate data. + * + * @param cert Certificate. + * @param alise Certificate alias. + * @param formatter Formatter. + * @return Filled certificate data. + * @throws CertificateEncodingException Certificate encoding exception. + */ + private CertData fillCertData(X509Certificate cert, String alise, Format formatter) + throws CertificateEncodingException { + + CertData certData = null; + + if (includeCert) { + certData = new CertDataDetail(); + } else { + certData = new CertData(); + } + certData.setAlias(alise); + certData.setSubjectDN(cert.getSubjectDN().getName()); + certData.setIssuerDN(cert.getIssuerDN().getName()); + certData.setSerialNumber(cert.getSerialNumber()); + certData.setVersion(cert.getVersion()); + certData.setNotAfter(formatter.format(cert.getNotAfter())); + certData.setNotBefore(formatter.format(cert.getNotBefore())); + certData.setPublicKey(Base64.encode(cert.getPublicKey().getEncoded())); + + if (includeCert) { + ((CertDataDetail) certData).setCertificate(cert); + } + + return certData; + } + + /** + * Read store data using the given store name. + * + * @param filePath File path of the keystore. + * @return Returns the keystore data as bytes stream. + * @throws IOException Throws if an error occurred while reading the keystore. + */ + private byte[] readBytesFromFile(String filePath) throws IOException { + + InputStream inputStream = null; + File file = new File(filePath); + long length; + byte[] bytes; + int offset = 0; + int numRead = 0; + + try { + inputStream = new FileInputStream(file); + length = file.length(); + bytes = new byte[(int) length]; + + while (offset < bytes.length + && (numRead = inputStream.read(bytes, offset, bytes.length - offset)) >= 0) { + offset += numRead; + } + } finally { + if (inputStream != null) { + inputStream.close(); + } + } + + return bytes; + } + + /** + * This method is used to generate the file name of the pub. cert of a tenant. + * + * @param ksLocation keystore location in the registry. + * @param uuid UUID appender + * @return file name of the pub. cert + */ + private String generatePubCertFileName(String ksLocation, String uuid) { + + String tenantName = ksLocation.substring(ksLocation.lastIndexOf("/")); + if (tenantName.endsWith(".jks")) { + tenantName = tenantName.replace(".jks", ""); + } + return tenantName + "-" + uuid + ".cert"; + } + + /** + * This method is used internally to do the pagination purposes. + * + * @param pageNumber Page Number. + * @param certDataSet Set of keyStoreData. + * @return PaginatedPolicySetDTO object containing the number of pages and the set of policies + * that reside in the given page. + */ + private PaginatedCertData doPaging(int pageNumber, CertData[] certDataSet) { + + PaginatedCertData paginatedCertData = new PaginatedCertData(); + if (certDataSet.length == 0) { + paginatedCertData.setCertDataSet(new CertData[0]); + return paginatedCertData; + } + int itemsPerPageInt = ITEMS_PER_PAGE; + int numberOfPages = (int) Math.ceil((double) certDataSet.length / itemsPerPageInt); + if (pageNumber > numberOfPages - 1) { + pageNumber = numberOfPages - 1; + } + int startIndex = pageNumber * itemsPerPageInt; + int endIndex = certDataSet.length; + if (numberOfPages > CACHING_PAGE_SIZE) { + endIndex = (pageNumber + CACHING_PAGE_SIZE) * itemsPerPageInt; + } + CertData[] returnedCertDataSet = new CertData[endIndex]; + + for (int i = startIndex, j = 0; i < endIndex && i < certDataSet.length; i++, j++) { + returnedCertDataSet[j] = certDataSet[i]; + } + + paginatedCertData.setCertDataSet(returnedCertDataSet); + paginatedCertData.setNumberOfPages(numberOfPages); + + return paginatedCertData; + } + + /** + * This method is used internally for the filtering purposes. + * + * @param filter Filter string. + * @param certDataSet Certificate or key array. + * @return Cert Data array after filtering. + */ + private static CertData[] doFilter(String filter, CertData[] certDataSet) { + + if (certDataSet != null && certDataSet.length != 0) { + String regPattern = filter.replace("*", ".*"); + List certDataList = new ArrayList(); + + for (CertData cert : certDataSet) { + if (cert != null && cert.getAlias().toLowerCase().matches(regPattern.toLowerCase())) { + certDataList.add(cert); + } + } + + return (CertData[]) certDataList.toArray(new CertData[0]); + } else { + return new CertData[0]; + } + } + + /** + * Gets the keystore info by keystore name with its certificates and key certificates. + * + * @param keyStoreName The name of the keystore. + * @param pageNumber Page number. + * @return Instance of KeyStoreData. + * @throws KeyStoreManagementException If an error occurs while getting the keystore this exception will be thrown. + */ + public PaginatedKeyStoreData getPaginatedKeystoreInfo(String keyStoreName, int pageNumber) + throws KeyStoreManagementException { + + if (StringUtils.isEmpty(keyStoreName)) { + throw new KeyStoreManagementException("Keystore name cannot be empty or null."); + } + + try { + // Get keystore. + KeyStore keyStore = getKeyStore(tenantId, keyStoreName); + // Get keystore type. + String keyStoreType = getKeyStoreType(keyStoreName); + + // Extract certificates from aliases as list. + List certDataList = getCertificates(keyStore); + List keyCertDataList = getKeyCertificates(keyStore); + + // Create a certificate array. + CertData[] certs = certDataList.toArray(new CertData[certDataList.size()]); + // Get paginated certificates. + PaginatedCertData paginatedCerts = doPaging(pageNumber, certs); + + // Create a key certificate array. + CertData[] keyCerts = keyCertDataList.toArray(new CertData[keyCertDataList.size()]); + // Get paginated key certificates. + PaginatedCertData paginatedKeyCerts = doPaging(pageNumber, keyCerts); + + // Fill information about the keystore to PaginatedKeyStoreData. + PaginatedKeyStoreData keyStoreData = fillPaginatedKeyStoreData(keyStoreName, keyStoreType, + paginatedCerts, paginatedKeyCerts); + + return keyStoreData; + } catch (Exception e) { + throw new KeyStoreManagementException(e.getMessage()); + } + + } + + /** + * Gets the keystore info by keystore name and filters its certificates and key certificates + * by applying the filter for certificate aliases. + * + * @param keyStoreName The name of the keystore. + * @param pageNumber Page number. + * @param filter Filter for certificate alias. + * @return Instance of KeyStoreData. + * @throws KeyStoreManagementException will be thrown. + */ + public PaginatedKeyStoreData getFilteredPaginatedKeyStoreInfo(String keyStoreName, int pageNumber, + String filter) throws KeyStoreManagementException { + + if (StringUtils.isEmpty(keyStoreName)) { + throw new KeyStoreManagementException("Keystore name cannot be empty or null."); + } + + try { + // Get keystore. + KeyStore keyStore = getKeyStore(tenantId, keyStoreName); + // Get keystore type. + String keyStoreType = getKeyStoreType(keyStoreName); + + // Extract certificates from aliases as list. + List certDataList = getCertificates(keyStore); + List keyCertDataList = getKeyCertificates(keyStore); + // Filter and paginate certs and keyCerts. + PaginatedCertData paginatedCerts = filterAndPaginateCerts(certDataList, filter, pageNumber); + PaginatedCertData paginatedKeyCerts = filterAndPaginateCerts(keyCertDataList, filter, pageNumber); + // Fill information about the keystore to PaginatedKeyStoreData. + PaginatedKeyStoreData keyStoreData = fillPaginatedKeyStoreData(keyStoreName, keyStoreType, + paginatedCerts, paginatedKeyCerts); + + return keyStoreData; + } catch (Exception e) { + throw new KeyStoreManagementException(e.getMessage()); + } + } + + /** + * Retrieves key store for a given keystore name of a tenant. + * + * @param tenantId Tenant Id. + * @param keyStoreName Keystore Name. + * @return KeyStore. + * @throws Exception This will be thrown if an error occurs while retrieving the keystore. + */ + private KeyStore getKeyStore(int tenantId, String keyStoreName) throws Exception { + + KeyStore keyStore; + if (KeyStoreUtil.isPrimaryStore(keyStoreName)) { + KeyStoreManager keyStoreManager = KeyStoreManager.getInstance(tenantId); + keyStore = keyStoreManager.getPrimaryKeyStore(); + } else if (isTrustStore(keyStoreName)) { + keyStore = getTrustStore(); + } else { + keyStore = getKeyStore(keyStoreName); + } + return keyStore; + } + + /** + * Get keystore type. + * + * @param keyStoreName Keystore name. + * @return Keystore type. + * @throws KeyStoreManagementException If an error occurs while retrieving the keystore. + */ + private String getKeyStoreType(String keyStoreName) throws KeyStoreManagementException { + + String keyStoreType; + if (KeyStoreUtil.isPrimaryStore(keyStoreName)) { + ServerConfiguration serverConfig = ServerConfiguration.getInstance(); + keyStoreType = serverConfig + .getFirstProperty(RegistryResources.SecurityManagement.SERVER_PRIMARY_KEYSTORE_TYPE); + } else if (isTrustStore(keyStoreName)) { + ServerConfiguration serverConfig = ServerConfiguration.getInstance(); + keyStoreType = serverConfig.getFirstProperty(SERVER_TRUSTSTORE_TYPE); + } else { + if (!isExistKeyStore(keyStoreName)) { + throw new KeyStoreManagementException("Keystore " + keyStoreName + " not found."); + } + KeyStoreModel keyStoreModel = keyStoreDAO.getKeyStore(tenantUUID, keyStoreName).get(); + keyStoreType = keyStoreModel.getType(); + } + return keyStoreType; + } + + /** + * Fill PaginatedKeyStoreData with keystore details. + * + * @param keyStoreName Name of the keystore. + * @param keyStoreType Type of the keystore. + * @param certs Paginated certificates. + * @param keyCerts Paginated key certificates. + * @return Paginated KeyStore Data. + */ + private PaginatedKeyStoreData fillPaginatedKeyStoreData(String keyStoreName, String keyStoreType, + PaginatedCertData certs, PaginatedCertData keyCerts) { + + // Create a KeyStoreData bean, set the name, type and fill in the cert information. + PaginatedKeyStoreData keyStoreData = new PaginatedKeyStoreData(); + keyStoreData.setKeyStoreName(keyStoreName); + keyStoreData.setKeyStoreType(keyStoreType); + keyStoreData.setPaginatedCertData(certs); + keyStoreData.setPaginatedKeyData(keyCerts); + return keyStoreData; + } + + /** + * Get certificates related to alias from the keystore. + * + * @param keyStore Keystore + * @return List of certificate data. + * @throws KeyStoreException If an error occurs while retrieving the keystore. + * @throws CertificateEncodingException If an error occurs while encoding the certificate. + */ + private List getCertificates(KeyStore keyStore) + throws KeyStoreException, CertificateEncodingException { + + Enumeration aliases = keyStore.aliases(); + // Create lists for cert and key lists. + List certDataList = new ArrayList<>(); + Format formatter = new SimpleDateFormat("dd/MM/yyyy"); + + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); + if (keyStore.isCertificateEntry(alias)) { + certDataList.add(fillCertData(cert, alias, formatter)); + } + } + return certDataList; + } + + /** + * Get key certificates related to alias from the keystore. + * + * @param keyStore Keystore + * @return List of certificate data. + * @throws KeyStoreException If an error occurs while retrieving the keystore. + * @throws CertificateEncodingException If an error occurs while encoding the certificate. + */ + private List getKeyCertificates(KeyStore keyStore) + throws KeyStoreException, CertificateEncodingException { + + Enumeration aliases = keyStore.aliases(); + // Create lists for cert and key lists. + List certDataList = new ArrayList<>(); + Format formatter = new SimpleDateFormat("dd/MM/yyyy"); + + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); + if (keyStore.isKeyEntry(alias)) { + certDataList.add(fillCertData(cert, alias, formatter)); + } + } + return certDataList; + } + + /** + * Filter and paginate certificate list. + * + * @param certDataList Certificate list. + * @param filterString Filter text. + * @param pageNumber Page number. + * @return Paginated and Filtered Certificate Data. + */ + private PaginatedCertData filterAndPaginateCerts(List certDataList, String filterString, int pageNumber) { + + PaginatedCertData paginatedCerts; + CertData[] certs = certDataList.toArray(new CertData[0]); + certs = (doFilter(filterString, certs)); + paginatedCerts = doPaging(pageNumber, certs); + return paginatedCerts; + } + + /** + * Load the default trust store (allowed only for super tenant). + * + * @return trust store object + * @throws KeyStoreManagementException if retrieving the truststore fails. + */ + public KeyStore getTrustStore() throws KeyStoreManagementException { + + //Allow only the super tenant to access the default trust store. + if (tenantId != MultitenantConstants.SUPER_TENANT_ID) { + throw new KeyStoreManagementException("Permission denied for accessing trust store"); + } + + KeyStore trustStore; + ServerConfiguration serverConfiguration = ServerConfiguration.getInstance(); + String file = new File(serverConfiguration.getFirstProperty(SERVER_TRUSTSTORE_FILE)).getAbsolutePath(); + + KeyStore store; + try { + store = KeyStore.getInstance(serverConfiguration.getFirstProperty(SERVER_TRUSTSTORE_TYPE)); + } catch (KeyStoreException e) { + throw new KeyStoreManagementException("Error occurred while loading keystore.", e); + } + + String password = serverConfiguration.getFirstProperty(SERVER_TRUSTSTORE_PASSWORD); + + try (FileInputStream in = new FileInputStream(file)) { + store.load(in, password.toCharArray()); + trustStore = store; + } catch (CertificateException | NoSuchAlgorithmException | IOException e) { + throw new KeyStoreManagementException("Error occurred while loading trust store", e); + } + return trustStore; + } + + /** + * Check if the supplied id is the system configured trust store + * + * @param id id (file name) of the keystore + * @return boolean true if supplied id is the configured trust store + */ + private boolean isTrustStore(String id) { + + ServerConfiguration serverConfiguration = ServerConfiguration.getInstance(); + String fileName = serverConfiguration.getFirstProperty(SERVER_TRUSTSTORE_FILE); + int index = fileName.lastIndexOf('/'); + if (index != -1) { + String name = fileName.substring(index + 1); + if (name.equals(id)) { + return true; + } + } else { + index = fileName.lastIndexOf(File.separatorChar); + String name; + if (index != -1) { + name = fileName.substring(fileName.lastIndexOf(File.separatorChar)); + } else { + name = fileName; + } + + if (name.equals(id)) { + return true; + } + } + return false; + } + + /** + * Retrieves the {@link KeyStore} object of the given keystore name. + * + * @param keyStoreName name of the keystore. + * @return {@link KeyStore} object. + * @throws Exception if retrieving the keystore fails. + */ + public KeyStore getKeyStore(String keyStoreName) throws Exception { + + if (isTrustStore(keyStoreName)) { + return getTrustStore(); + } else { + KeyStoreManager keyMan = KeyStoreManager.getInstance(tenantId); + return keyMan.getKeyStore(keyStoreName); + } + } + + /** + * Updates the key store. + * + * @param name Name of the key store. + * @param keyStore KeyStore object. + * @throws Exception If an error occurred while updating the key store. + */ + private void updateKeyStore(String name, KeyStore keyStore) throws Exception { + + FileOutputStream resource1; + String outputStream1; + String path; + if (isTrustStore(name)) { + path = (new File(trustStoreLocation)).getAbsolutePath(); + resource1 = null; + + try { + resource1 = new FileOutputStream(path); + outputStream1 = trustStorePassword; + keyStore.store(resource1, outputStream1.toCharArray()); + } finally { + if (resource1 != null) { + resource1.close(); + } + } + } else { + KeyStoreManager keyStoreManager = KeyStoreManager.getInstance(tenantId); + keyStoreManager.updateKeyStore(name, keyStore); + } + } + + /** + * Extract the encoded certificate into {@link X509Certificate}. + * + * @param certData encoded certificate. + * @return {@link X509Certificate} object. + * @throws KeyStoreManagementException if extracting the certificate fails. + */ + public X509Certificate extractCertificate(String certData) throws KeyStoreManagementException { + + byte[] bytes = Base64.decode(certData); + X509Certificate cert; + try { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + cert = (X509Certificate) factory + .generateCertificate(new ByteArrayInputStream(bytes)); + } catch (CertificateException e) { + if (log.isDebugEnabled()) { + log.debug(e.getMessage(), e); + } + throw new KeyStoreManagementException("Invalid format of the provided certificate file"); + } + return cert; + } + + /** + * Returns whether the key store exists or not. + * + * @param fileName Name of the key store. + * @return True if the key store exists. + * @throws KeyStoreManagementException If an error occurred while checking the existence of the key store. + */ + private boolean isExistKeyStore(String fileName) throws KeyStoreManagementException { + + return keyStoreDAO.getKeyStore(tenantUUID, fileName).isPresent(); + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementClientException.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementClientException.java new file mode 100755 index 00000000000..c1a0f479822 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementClientException.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore; + +/** + * Handles the Keystore Management client level errors. + */ +public class KeyStoreManagementClientException extends KeyStoreManagementException { + + public KeyStoreManagementClientException(String errorCode, String message) { + super(errorCode, message); + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementException.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementException.java new file mode 100755 index 00000000000..0ccb9f011fc --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementException.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore; + +import org.wso2.carbon.core.keystore.constants.KeyStoreConstants; + +/** + * Handles the Keystore Management errors. + */ +public class KeyStoreManagementException extends Exception { + + private String errorCode = null; + + public String getErrorCode() { + + return this.errorCode; + } + + public KeyStoreManagementException(String message) { + super(message); + } + + public KeyStoreManagementException(String errorCode, String message) { + super(message); + this.errorCode = errorCode; + } + + public KeyStoreManagementException(String message, Throwable cause) { + super(message, cause); + } + + public KeyStoreManagementException(String errorCode, String message, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + } + + public KeyStoreManagementException(KeyStoreConstants.ErrorMessage message, + KeyStoreManagementException e) { + + + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementServerException.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementServerException.java new file mode 100755 index 00000000000..c52bac36342 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementServerException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore; + +/** + * Handles the Keystore Management server level errors. + */ +public class KeyStoreManagementServerException extends KeyStoreManagementException { + + public KeyStoreManagementServerException(String errorCode, String message) { + super(errorCode, message); + } + + public KeyStoreManagementServerException(String errorCode, String message, Throwable cause) { + super(errorCode, message, cause); + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementService.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementService.java new file mode 100755 index 00000000000..02e8e49f625 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementService.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore; + +import java.security.cert.X509Certificate; +import java.util.List; +import java.util.Map; + +/** + * This service contains the methods to manage certificates of the keystore and client truststore. + */ +public interface KeyStoreManagementService { + + /** + * Retrieves the list of certificate aliases from the keystore. + * + * @param tenantDomain tenant domain of the keystore. + * @param filter used to filter the result. Supports sw, ew, eq & co. eg:filter=alias+sw+wso2. + * @return the {@link List} of alias. + * @throws KeyStoreManagementException when retrieving the certificate aliases failed. + */ + List getKeyStoreCertificateAliases(String tenantDomain, String filter) throws KeyStoreManagementException; + + /** + * Retrieves the public certificate from the keystore. + * + * @param tenantDomain tenant domain of the keystore. + * @return a {@link Map} with public key alias and {@link X509Certificate}. + * @throws KeyStoreManagementException when retrieving the public certificate. + */ + Map getPublicCertificate(String tenantDomain) throws KeyStoreManagementException; + + /** + * Retrieves the certificate of the given alias from the keystore. + * + * @param tenantDomain tenant domain of the keystore. + * @param alias of the certificate. + * @return the {@link X509Certificate} + * @throws KeyStoreManagementException when retrieving the certificate failed. + */ + X509Certificate getKeyStoreCertificate(String tenantDomain, String alias) throws KeyStoreManagementException; + + /** + * Retrieves the list of certificate aliases from the client truststore. + * + * @param tenantDomain tenant domain of the keystore. + * @param filter used to filter the result. Supports sw, ew, eq & co. eg:filter=alias+sw+wso2. + * @return the {@link List} of alias + * @throws KeyStoreManagementException when retrieving the certificate aliases failed. + */ + List getClientCertificateAliases(String tenantDomain, String filter) throws KeyStoreManagementException; + + /** + * Retrieves the certificate of the given alias from the client truststore. + * + * @param tenantDomain tenant domain of the keystore. + * @param alias of the certificate. + * @return the {@link X509Certificate} + * @throws KeyStoreManagementException when retrieving the certificate failed. + */ + X509Certificate getClientCertificate(String tenantDomain, String alias) throws KeyStoreManagementException; + + /** + * Imports the certificate to the keystore. + * + * @param tenantDomain tenant domain of the keystore. + * @param alias of the certificate. + * @param certificate the certificate to be imported. + * @throws KeyStoreManagementException when importing the certificate failed. + */ + void addCertificate(String tenantDomain, String alias, String certificate) throws KeyStoreManagementException; + + /** + * Deletes the certificate from the keystore. + * + * @param tenantDomain tenant domain of the keystore. + * @param alias of the certificate. + * @throws KeyStoreManagementException when importing the certificate failed. + */ + void deleteCertificate(String tenantDomain, String alias) throws KeyStoreManagementException; +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementServiceImpl.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementServiceImpl.java new file mode 100755 index 00000000000..4a67a607632 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/KeyStoreManagementServiceImpl.java @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2019, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore; + +import org.apache.commons.lang.StringUtils; +import org.wso2.carbon.base.MultitenantConstants; +import org.wso2.carbon.base.ServerConfiguration; +import org.wso2.carbon.core.internal.KeyStoreMgtDataHolder; +import org.wso2.carbon.core.keystore.constants.KeyStoreConstants; +import org.wso2.carbon.core.util.KeyStoreUtil; +import org.wso2.carbon.core.keystore.service.CertData; +import org.wso2.carbon.core.keystore.service.CertDataDetail; +import org.wso2.carbon.core.keystore.service.KeyStoreData; +import org.wso2.carbon.user.api.TenantManager; +import org.wso2.carbon.user.api.UserRealmService; +import org.wso2.carbon.user.api.UserStoreException; + +import java.nio.file.Paths; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_ALIAS_EXISTS; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_BAD_VALUE_FOR_FILTER; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_CANNOT_DELETE_TENANT_CERT; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_CERTIFICATE_EXISTS; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_DELETE_CERTIFICATE; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_EMPTY_ALIAS; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_INITIALIZE_REGISTRY; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_RETRIEVE_CLIENT_TRUSTSTORE; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_RETRIEVE_CLIENT_TRUSTSTORE_CERTIFICATE; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_RETRIEVE_KEYSTORE; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_RETRIEVE_KEYSTORE_INFORMATION; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_UNSUPPORTED_FILTER_OPERATION; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.ErrorMessage.ERROR_CODE_VALIDATE_CERTIFICATE; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.FILTER_FIELD_ALIAS; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.FILTER_OPERATION_CONTAINS; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.FILTER_OPERATION_ENDS_WITH; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.FILTER_OPERATION_EQUALS; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.FILTER_OPERATION_STARTS_WITH; +import static org.wso2.carbon.core.keystore.constants.KeyStoreConstants.SERVER_TRUSTSTORE_FILE; + +/** + * This class is used to manage the keystore certificates. + */ +public class KeyStoreManagementServiceImpl implements KeyStoreManagementService { + + @Override + public List getKeyStoreCertificateAliases(String tenantDomain, String filter) + throws KeyStoreManagementException { + + KeyStoreData keyStoreInfo = getKeystoreData(tenantDomain, getKeyStoreName(tenantDomain)); + return filterAlias(getAliasList(keyStoreInfo), filter); + } + + @Override + public Map getPublicCertificate(String tenantDomain) throws KeyStoreManagementException { + + Map certData = new HashMap<>(); + KeyStoreData keyStoreInfo = getKeystoreData(tenantDomain, getKeyStoreName(tenantDomain)); + CertData key = keyStoreInfo.getKey(); + certData.put(key.getAlias(), ((CertDataDetail) key).getCertificate()); + return certData; + } + + @Override + public X509Certificate getKeyStoreCertificate(String tenantDomain, String alias) + throws KeyStoreManagementException { + + if (StringUtils.isEmpty(alias)) { + throw handleClientException(ERROR_CODE_EMPTY_ALIAS, null); + } + + KeyStoreData keyStoreInfo = getKeystoreData(tenantDomain, getKeyStoreName(tenantDomain)); + CertData key = keyStoreInfo.getKey(); + if (key != null && StringUtils.equals(key.getAlias(), alias)) { + return ((CertDataDetail) key).getCertificate(); + } + + CertData[] certDataArray = keyStoreInfo.getCerts(); + for (CertData certData : certDataArray) { + String aliasFromKeyStore = certData.getAlias(); + if (StringUtils.equals(aliasFromKeyStore, alias)) { + return ((CertDataDetail) certData).getCertificate(); + } + } + return null; + } + + @Override + public List getClientCertificateAliases(String tenantDomain, String filter) + throws KeyStoreManagementException { + + KeyStoreData truststoreInfo = getKeystoreData(tenantDomain, getTrustStoreName()); + return filterAlias(getAliasList(truststoreInfo), filter); + } + + @Override + public X509Certificate getClientCertificate(String tenantDomain, String alias) throws KeyStoreManagementException { + + if (StringUtils.isEmpty(alias)) { + throw handleClientException(ERROR_CODE_EMPTY_ALIAS, null); + } + + KeyStore trustStore = null; + try { + trustStore = getKeyStoreAdmin(tenantDomain).getTrustStore(); + } catch (KeyStoreManagementException e) { + throw handleServerException(ERROR_CODE_RETRIEVE_CLIENT_TRUSTSTORE, tenantDomain, e); + } + + if (trustStore != null) { + try { + if (trustStore.containsAlias(alias)) { + return (X509Certificate) trustStore.getCertificate(alias); + } + } catch (KeyStoreException e) { + throw handleServerException(ERROR_CODE_RETRIEVE_CLIENT_TRUSTSTORE_CERTIFICATE, alias, e); + } + } + return null; + } + + @Override + public void addCertificate(String tenantDomain, String alias, String certificate) + throws KeyStoreManagementException { + + KeyStoreAdmin keyStoreAdmin = getKeyStoreAdmin(tenantDomain); + String keyStoreName = getKeyStoreName(tenantDomain); + X509Certificate cert; + cert = keyStoreAdmin.extractCertificate(certificate); + KeyStore keyStore; + String certAlias; + boolean isAliasExists; + try { + keyStore = keyStoreAdmin.getKeyStore(keyStoreName); + isAliasExists = keyStore.containsAlias(alias); + certAlias = keyStore.getCertificateAlias(cert); + } catch (Exception e) { + throw handleServerException(ERROR_CODE_VALIDATE_CERTIFICATE, null, e); + } + if (isAliasExists) { + throw handleClientException(ERROR_CODE_ALIAS_EXISTS, alias); + } + if (certAlias != null) { + throw handleClientException(ERROR_CODE_CERTIFICATE_EXISTS, certAlias); + } + keyStoreAdmin.importCertToStore(alias, certificate, keyStoreName); + } + + @Override + public void deleteCertificate(String tenantDomain, String alias) throws KeyStoreManagementException { + + try { + Map publicCertificate = getPublicCertificate(tenantDomain); + if (publicCertificate.keySet().contains(alias)) { + throw handleClientException(ERROR_CODE_CANNOT_DELETE_TENANT_CERT, alias); + } + getKeyStoreAdmin(tenantDomain).removeCertFromStore(alias, getKeyStoreName(tenantDomain)); + } catch (KeyStoreManagementException e) { + throw handleServerException(ERROR_CODE_DELETE_CERTIFICATE, alias, e); + } + } + + private String getKeyStoreName(String tenantDomain) throws KeyStoreManagementException { + + KeyStoreData[] keyStoreDataArray = getKeyStoreAdmin(tenantDomain).getKeyStores(isSuperTenant(tenantDomain)); + + for (KeyStoreData keyStoreData : keyStoreDataArray) { + if (keyStoreData == null) { + break; + } + String keyStoreName = keyStoreData.getKeyStoreName(); + if (isSuperTenant(tenantDomain)) { + if (KeyStoreUtil.isPrimaryStore(keyStoreName)) { + return keyStoreName; + } + } else { + String tenantKeyStoreName = tenantDomain.trim().replace(".", "-") + ".jks"; + if (StringUtils.equals(keyStoreName, tenantKeyStoreName)) { + return keyStoreName; + } + } + } + throw handleServerException(ERROR_CODE_RETRIEVE_KEYSTORE, tenantDomain); + } + + private KeyStoreData getKeystoreData(String tenantDomain, String keyStoreName) throws KeyStoreManagementException { + + KeyStoreAdmin keyStoreAdmin = getKeyStoreAdmin(tenantDomain); + KeyStoreData keyStoreData = null; + keyStoreAdmin.setIncludeCert(true); + try { + keyStoreData = keyStoreAdmin.getKeystoreInfo(keyStoreName); + } catch (KeyStoreManagementException e) { + throw handleServerException(ERROR_CODE_RETRIEVE_KEYSTORE_INFORMATION, keyStoreName, e); + } + return keyStoreData; + } + + private List getAliasList(KeyStoreData keyStoreData) { + + List aliasList = new ArrayList<>(); + CertData key = keyStoreData.getKey(); + if (key != null && key.getAlias() != null) { + aliasList.add(key.getAlias()); + } + + CertData[] certDataArray = keyStoreData.getCerts(); + for (CertData certData : certDataArray) { + String alias = certData.getAlias(); + if (alias != null) { + aliasList.add(alias); + } + } + return aliasList; + } + + private List filterAlias(List aliases, String filter) throws KeyStoreManagementException { + + if (filter != null) { + filter = filter.replace(" ", "+"); + String[] extractedFilter = filter.split("[+]"); + if (extractedFilter.length == 3) { + if (StringUtils.equals(extractedFilter[0], FILTER_FIELD_ALIAS)) { + String operation = extractedFilter[1]; + String value = extractedFilter[2]; + if (StringUtils.equals(operation, FILTER_OPERATION_EQUALS)) { + aliases = aliases.stream().filter(alias -> alias.matches(value)) + .collect(Collectors.toList()); + } else if (StringUtils.equals(operation, FILTER_OPERATION_STARTS_WITH)) { + aliases = aliases.stream().filter(alias -> alias.startsWith(value)) + .collect(Collectors.toList()); + } else if (StringUtils.equals(operation, FILTER_OPERATION_ENDS_WITH)) { + aliases = aliases.stream().filter(alias -> alias.endsWith(value)) + .collect(Collectors.toList()); + } else if (StringUtils.equals(operation, FILTER_OPERATION_CONTAINS)) { + aliases = aliases.stream().filter(alias -> alias.contains(value)) + .collect(Collectors.toList()); + } else { + throw handleClientException(ERROR_CODE_UNSUPPORTED_FILTER_OPERATION, operation); + } + } + } else { + throw handleClientException(ERROR_CODE_BAD_VALUE_FOR_FILTER, filter); + } + } + return aliases; + } + + private int getTenantId(String tenantDomain) throws KeyStoreManagementServerException { + + try { + UserRealmService userRealmService = KeyStoreMgtDataHolder.getRealmService(); + TenantManager tenantManager = userRealmService.getTenantManager(); + return tenantManager.getTenantId(tenantDomain); + } catch (KeyStoreManagementException | UserStoreException e) { + throw handleServerException(ERROR_CODE_INITIALIZE_REGISTRY, tenantDomain, e); + } + } + + private KeyStoreAdmin getKeyStoreAdmin(String tenantDomain) throws KeyStoreManagementServerException { + + return new KeyStoreAdmin(getTenantId(tenantDomain)); + } + + private boolean isSuperTenant(String tenantDomain) throws KeyStoreManagementServerException { + + return getTenantId(tenantDomain) == MultitenantConstants.SUPER_TENANT_ID; + } + + private String getTrustStoreName() { + + ServerConfiguration serverConfiguration = ServerConfiguration.getInstance(); + String filePath = serverConfiguration.getFirstProperty(SERVER_TRUSTSTORE_FILE); + return Paths.get(filePath).getFileName().toString(); + } + + private KeyStoreManagementServerException handleServerException( + KeyStoreConstants.ErrorMessage error, String data) { + + String message = includeData(error, data); + return new KeyStoreManagementServerException(error.getCode(), message); + } + + private KeyStoreManagementServerException handleServerException( + KeyStoreConstants.ErrorMessage error, String data, + Throwable e) { + + String message = includeData(error, data); + return new KeyStoreManagementServerException(error.getCode(), message, e); + } + + private KeyStoreManagementClientException handleClientException( + KeyStoreConstants.ErrorMessage error, String data) { + + String message = includeData(error, data); + return new KeyStoreManagementClientException(error.getCode(), message); + } + + private static String includeData(KeyStoreConstants.ErrorMessage error, String data) { + + String message; + if (StringUtils.isNotBlank(data)) { + message = String.format(error.getMessage(), data); + } else { + message = error.getMessage(); + } + return message; + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/constants/KeyStoreConstants.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/constants/KeyStoreConstants.java new file mode 100644 index 00000000000..bc38e3c51da --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/constants/KeyStoreConstants.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.constants; + +import org.wso2.carbon.core.RegistryResources; + +/** + * Constants related to the KeyStore Management. + */ +public class KeyStoreConstants { + + public static final String FILTER_FIELD_ALIAS = "alias"; + public static final String FILTER_OPERATION_EQUALS = "eq"; + public static final String FILTER_OPERATION_STARTS_WITH = "sw"; + public static final String FILTER_OPERATION_ENDS_WITH = "ew"; + public static final String FILTER_OPERATION_CONTAINS = "co"; + public static final int ITEMS_PER_PAGE = 10; + public static final int CACHING_PAGE_SIZE = 5; + public static final String KEY_STORES = RegistryResources.SecurityManagement.KEY_STORES; + public static final String SERVER_TRUSTSTORE_FILE = "Security.TrustStore.Location"; + public static final String KEYSTORE_DATASOURCE = "KeyStoreDataPersistenceManager.DataSource.Name"; + + /** + * Enum for Keystore management service related errors. + */ + public enum ErrorMessage { + + /** + * Server errors. + */ + ERROR_CODE_RETRIEVE_KEYSTORE("KSS-65001", + "Unable to retrieve the keystore for tenant: %s."), + ERROR_CODE_RETRIEVE_KEYSTORE_INFORMATION("KSS-65002", + "Unable to retrieve keystore information for keystore: %s"), + ERROR_CODE_RETRIEVE_CLIENT_TRUSTSTORE("KSS-65003", + "Unable to retrieve client truststore for tenant: %s"), + ERROR_CODE_RETRIEVE_CLIENT_TRUSTSTORE_ALIASES("KSS-65004", + "Unable to retrieve the client truststore aliases for tenant: %s."), + ERROR_CODE_RETRIEVE_CLIENT_TRUSTSTORE_CERTIFICATE("KSS-65005", + "Unable to retrieve the client truststore certificate for alias: %s."), + ERROR_CODE_ADD_CERTIFICATE("KSS-65006", + "Unable to add certificate with alias: %s"), + ERROR_CODE_DELETE_CERTIFICATE("KSS-65007", + "Unable to delete certificate with alias: %s"), + ERROR_CODE_VALIDATE_CERTIFICATE("KSS-65008", "Error occurred while validating the " + + "certificate."), + ERROR_CODE_INITIALIZE_REGISTRY("KSS-65009", + "Unable to initialize the registry for the tenant: %s."), + /** + * Client error. + */ + ERROR_CODE_CERTIFICATE_EXISTS("KSS-60001", + "Provided certificate already exists with the alias: %s"), + ERROR_CODE_ALIAS_EXISTS("KSS-60002", + "Provided alias '%s' is already available in the keystore."), + ERROR_CODE_BAD_VALUE_FOR_FILTER("KSS-60003", + "Unsupported filter: %s."), + ERROR_CODE_UNSUPPORTED_FILTER_OPERATION("KSS-60004", + "Unsupported filter operation %s."), + ERROR_CODE_EMPTY_ALIAS("KSS-60005", "Alias value can not be null."), + ERROR_CODE_INVALID_CERTIFICATE("KSS-60006", "Provided certificate is invalid."), + ERROR_CODE_CANNOT_DELETE_TENANT_CERT("KSS-60007", "Not allowed to delete the tenant certificate %s."), + + /** + * Common Error messages without a code. + */ + ERROR_MESSAGE_RETRIEVE_KEYSTORE("KSS-62501", "Error when getting keyStore data."), + ERROR_MESSAGE_RETRIEVE_PUBLIC_CERT("KSS-62502", "Error when getting public certificate data."); + private final String code; + private final String message; + + ErrorMessage(String code, String message) { + this.code = code; + this.message = message; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } + + @Override + public String toString() { + return code + " : " + message; + } + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/KeyStoreDAO.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/KeyStoreDAO.java new file mode 100755 index 00000000000..356e86e78a1 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/KeyStoreDAO.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.dao; + +import org.wso2.carbon.core.keystore.KeyStoreManagementException; +import org.wso2.carbon.core.keystore.model.KeyStoreModel; + +import java.util.List; +import java.util.Optional; + +/** + * Data Access Object for KeyStore. + */ +public interface KeyStoreDAO { + + /** + * Add a new KeyStore. + * + * @param tenantUUID Tenant UUID. + * @param keyStoreModel KeyStore model where the keystore data is maintained. + * @throws KeyStoreManagementException If an error occurs while adding the KeyStore. + */ + void addKeyStore(String tenantUUID, KeyStoreModel keyStoreModel) throws + KeyStoreManagementException; + + /** + * Get all KeyStores. + * + * @param tenantUUID tenant UUID + * @return List of KeyStoreModels. + * @throws KeyStoreManagementException If an error occurs while retrieving the KeyStores. + */ + List getKeyStores(String tenantUUID) throws KeyStoreManagementException; + + /** + * Get a KeyStore. + * + * @param tenantUUID Tenant UUID. + * @param fileName Name of the KeyStore file. + * @return KeyStoreModel. + * @throws KeyStoreManagementException If an error occurs while retrieving the KeyStore. + */ + Optional getKeyStore(String tenantUUID, String fileName) throws KeyStoreManagementException; + + /** + * Delete a KeyStore. + * + * @param tenantUUID Tenant UUID. + * @param fileName Name of the KeyStore file. + * @throws KeyStoreManagementException If an error occurs while deleting the KeyStore. + */ + void deleteKeyStore(String tenantUUID, String fileName) throws KeyStoreManagementException; + + /** + * Update a KeyStore. + * + * @param tenantUUID Tenant UUID. + * @param keyStoreModel KeyStore model where the keystore data is maintained. + * @throws KeyStoreManagementException If an error occurs while updating the KeyStore. + */ + void updateKeyStore(String tenantUUID, KeyStoreModel keyStoreModel) throws KeyStoreManagementException; + + /** + * Add a public certificate to a KeyStore. + * + * @param tenantUUID Tenant UUID. + * @param fileName Name of the KeyStore file. + * @param pubCertId Public certificate ID. + * @throws KeyStoreManagementException If an error occurs while adding the public certificate. + */ + void addPubCertIdToKeyStore(String tenantUUID, String fileName, String pubCertId) + throws KeyStoreManagementException; + + /** + * Get the public certificate ID of a KeyStore. + * + * @param tenantUUID Tenant UUID. + * @param fileName Name of the KeyStore file. + * @return Public certificate ID. + * @throws KeyStoreManagementException If an error occurs while retrieving the public certificate ID. + */ + Optional getPubCertIdFromKeyStore(String tenantUUID, String fileName) throws KeyStoreManagementException; +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/PubCertDAO.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/PubCertDAO.java new file mode 100755 index 00000000000..d9ca212591f --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/PubCertDAO.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.dao; + +import org.wso2.carbon.core.keystore.KeyStoreManagementException; +import org.wso2.carbon.core.keystore.model.PubCertModel; + +import java.util.Optional; + +/** + * Data Access Object for PubCert. + */ +public interface PubCertDAO { + + /** + * Add a new Public Certificate. + * + * @param pubCertModel PubCert model where the PubCert data is maintained. + * @throws KeyStoreManagementException If an error occurs while adding the PubCert. + */ + String addPubCert(PubCertModel pubCertModel) throws KeyStoreManagementException; + + /** + * Get a Public Certificate. + * + * @param uuid UUID of the PubCert. + * @return PubCertModel. + * @throws KeyStoreManagementException If an error occurs while retrieving the PubCert. + */ + Optional getPubCert(String uuid) throws KeyStoreManagementException; +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/constants/KeyStoreDAOConstants.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/constants/KeyStoreDAOConstants.java new file mode 100755 index 00000000000..f70b195f18d --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/constants/KeyStoreDAOConstants.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.dao.constants; + +/** + * Constants related to the KeyStoreDAO. + */ +public class KeyStoreDAOConstants { + + private KeyStoreDAOConstants() { + + } + + public static class KeyStoreTableColumns { + + private KeyStoreTableColumns() { + + } + + public static final String ID = "ID"; + public static final String FILE_NAME = "FILE_NAME"; + public static final String TYPE = "TYPE"; + public static final String PROVIDER = "PROVIDER"; + public static final String PASSWORD = "PASSWORD"; + public static final String PRIVATE_KEY_ALIAS = "PRIVATE_KEY_ALIAS"; + public static final String PRIVATE_KEY_PASS = "PRIVATE_KEY_PASS"; + public static final String TENANT_UUID = "TENANT_UUID"; + public static final String PUB_CERT_ID = "PUB_CERT_ID"; + public static final String LAST_UPDATED = "LAST_UPDATED"; + public static final String CONTENT = "CONTENT"; + } + + public static class SqlQueries { + + private SqlQueries() { + + } + + public static final String ADD_KEY_STORE = "INSERT INTO IDN_KEY_STORE " + + "(ID, FILE_NAME, TYPE, PROVIDER, PASSWORD, PRIVATE_KEY_ALIAS, PRIVATE_KEY_PASS, TENANT_UUID, " + + "LAST_UPDATED, CONTENT) VALUES (:ID;, :FILE_NAME;, :TYPE;, :PROVIDER;, :PASSWORD;, " + + ":PRIVATE_KEY_ALIAS;, :PRIVATE_KEY_PASS;, :TENANT_UUID;, :LAST_UPDATED;, ?)"; + + public static final String UPDATE_KEY_STORE_BY_FILE_NAME = "UPDATE IDN_KEY_STORE SET TYPE = :TYPE;, " + + "PROVIDER = :PROVIDER;, PASSWORD = :PASSWORD;, PRIVATE_KEY_ALIAS = :PRIVATE_KEY_ALIAS;, " + + "PRIVATE_KEY_PASS = :PRIVATE_KEY_PASS;, LAST_UPDATED = :LAST_UPDATED;, CONTENT = ? " + + "WHERE FILE_NAME = :FILE_NAME; AND TENANT_UUID = :TENANT_UUID;"; + + public static final String GET_KEY_STORE_BY_ID = + "SELECT * FROM IDN_KEY_STORE WHERE ID = :ID;"; + + public static final String GET_KEY_STORE_BY_FILE_NAME = + "SELECT * FROM IDN_KEY_STORE WHERE FILE_NAME = :FILE_NAME; AND TENANT_UUID = :TENANT_UUID;"; + + public static final String GET_KEY_STORES = + "SELECT * FROM IDN_KEY_STORE WHERE TENANT_UUID = :TENANT_UUID;"; + + public static final String DELETE_KEY_STORE_BY_ID = + "DELETE FROM IDN_KEY_STORE WHERE ID = :ID; AND TENANT_UUID = :TENANT_UUID;"; + + public static final String DELETE_KEY_STORE_BY_FILE_NAME = + "DELETE FROM IDN_KEY_STORE WHERE FILE_NAME = :FILE_NAME; AND TENANT_UUID = :TENANT_UUID;"; + + // TODO: refactor to use named prep statement after adding a method to set named bytes in named prep statement. + + public static final String GET_PUB_CERT_ID_OF_KEY_STORE = + "SELECT PUB_CERT_ID FROM IDN_KEY_STORE WHERE FILE_NAME = :FILE_NAME; AND TENANT_UUID = :TENANT_UUID;"; + + public static final String ADD_PUB_CERT_ID_TO_KEY_STORE = + "UPDATE IDN_KEY_STORE SET PUB_CERT_ID = :PUB_CERT_ID;, LAST_UPDATED = :LAST_UPDATED; " + + "WHERE FILE_NAME = :FILE_NAME; AND TENANT_UUID = :TENANT_UUID;"; + } + + public static class ErrorMessages { + + private ErrorMessages() { + + } + + public static final String ERROR_ADD_KEY_STORE = "Error while adding key store."; + public static final String ERROR_UPDATE_KEY_STORE = "Error while updating key store."; + public static final String ERROR_GET_KEY_STORES = "Error while retrieving key stores."; + public static final String ERROR_GET_KEY_STORE = "Error while retrieving the key store."; + public static final String ERROR_DELETE_KEY_STORE_BY_FILE_NAME = "Error while deleting key store by file name."; + public static final String ERROR_LINK_PUB_CERT_TO_KEY_STORE = "Error while linking public certificate to key " + + "store."; + public static final String ERROR_GET_PUB_CERT_OF_KEY_STORE = "Error while retrieving public certificate of " + + "key store."; + public static final String ERROR_CANNOT_RETRIEVE_DB_CONN = "Error while retrieving database connection."; + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/constants/PubCertDAOConstants.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/constants/PubCertDAOConstants.java new file mode 100755 index 00000000000..405bb7a2bc2 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/constants/PubCertDAOConstants.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.dao.constants; + +/** + * Constants related to the PubCertDAOConstants + */ +public class PubCertDAOConstants { + + private PubCertDAOConstants() {} + + public static class PubCertTableColumns { + + private PubCertTableColumns() {} + public static final String ID = "ID"; + public static final String FILE_NAME_APPENDER = "FILE_NAME_APPENDER"; + public static final String TENANT_UUID = "TENANT_UUID"; + public static final String CONTENT = "CONTENT"; + } + + public static class SQLQueries { + + private SQLQueries() {} + + public static final String ADD_PUB_CERT = "INSERT INTO IDN_PUB_CERT (ID, FILE_NAME_APPENDER, CONTENT) " + + "VALUES (:ID;, :FILE_NAME_APPENDER;, ?)"; + + public static final String GET_PUB_CERT = + "SELECT * FROM IDN_PUB_CERT WHERE ID = :ID;"; + } + + public static class ErrorMessages { + + private ErrorMessages() {} + + public static final String ERROR_MESSAGE_ADDING_PUB_CERT = "Error while adding public certificate."; + public static final String ERROR_MESSAGE_RETRIEVING_PUB_CERT = "Error while retrieving public certificate."; + public static final String DB_CONN_RETRIEVAL_ERROR_MSG = "Error while getting the DB connection."; + } + +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/impl/KeyStoreDAOImpl.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/impl/KeyStoreDAOImpl.java new file mode 100755 index 00000000000..27be4b6cca4 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/impl/KeyStoreDAOImpl.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.dao.impl; + +import org.wso2.carbon.core.internal.KeyStoreMgtDataHolder; +import org.wso2.carbon.database.utils.jdbc.NamedPreparedStatement; +import org.wso2.carbon.user.core.util.DatabaseUtil; +import org.wso2.carbon.core.keystore.KeyStoreManagementException; +import org.wso2.carbon.core.keystore.dao.KeyStoreDAO; +import org.wso2.carbon.core.keystore.dao.constants.KeyStoreDAOConstants; +import org.wso2.carbon.core.keystore.dao.constants.KeyStoreDAOConstants.KeyStoreTableColumns; +import org.wso2.carbon.core.keystore.model.KeyStoreModel; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.TimeZone; +import java.util.UUID; + +import javax.sql.DataSource; + +import static java.time.ZoneOffset.UTC; + +/** + * This class provides the implementation of the KeyStoreDAO interface. + */ +public class KeyStoreDAOImpl implements KeyStoreDAO { + + private final Calendar CALENDAR = Calendar.getInstance(TimeZone.getTimeZone(UTC)); + private final DataSource dataSource; + + public KeyStoreDAOImpl() { + + this.dataSource = KeyStoreMgtDataHolder.getDataSource(); + } + + /** + * {@inheritDoc} + */ + @Override + public void addKeyStore(String tenantUUID, KeyStoreModel keyStoreModel) throws KeyStoreManagementException { + + try (Connection connection = DatabaseUtil.getDBConnection(this.dataSource)) { + try { + processAddKeyStore(connection, keyStoreModel, tenantUUID); + connection.commit(); + } catch (SQLException e) { + connection.rollback(); + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages.ERROR_ADD_KEY_STORE, e); + } + } catch (SQLException e) { + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages.ERROR_CANNOT_RETRIEVE_DB_CONN, e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public List getKeyStores(String tenantUUID) throws KeyStoreManagementException { + + List keyStores = new ArrayList<>(); + + try (Connection connection = DatabaseUtil.getDBConnection(this.dataSource)) { + try (NamedPreparedStatement statement = new NamedPreparedStatement(connection, + KeyStoreDAOConstants.SqlQueries.GET_KEY_STORES)) { + statement.setString(KeyStoreTableColumns.TENANT_UUID, tenantUUID); + try (ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) { + keyStores.add(mapResultToKeyStoreModel(resultSet)); + } + } + } catch (SQLException e) { + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages.ERROR_GET_KEY_STORES, e); + } + } catch (SQLException e) { + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages.ERROR_CANNOT_RETRIEVE_DB_CONN, e); + } + + return keyStores; + } + + /** + * {@inheritDoc} + */ + @Override + public Optional getKeyStore(String tenantUUID, String fileName) throws KeyStoreManagementException { + + try (Connection connection = DatabaseUtil.getDBConnection(this.dataSource)) { + try (NamedPreparedStatement statement = new NamedPreparedStatement(connection, + KeyStoreDAOConstants.SqlQueries.GET_KEY_STORE_BY_FILE_NAME)) { + statement.setString(KeyStoreTableColumns.FILE_NAME, fileName); + statement.setString(KeyStoreTableColumns.TENANT_UUID, tenantUUID); + try (ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + return Optional.of(mapResultToKeyStoreModel(resultSet)); + } + } + } catch (SQLException e) { + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages.ERROR_GET_KEY_STORE, e); + } + } catch (SQLException e) { + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages.ERROR_CANNOT_RETRIEVE_DB_CONN, e); + } + return Optional.empty(); + } + + /** + * {@inheritDoc} + */ + @Override + public void deleteKeyStore(String tenantUUID, String fileName) throws KeyStoreManagementException { + + try (Connection connection = DatabaseUtil.getDBConnection(this.dataSource)) { + try (NamedPreparedStatement statement = new NamedPreparedStatement(connection, + KeyStoreDAOConstants.SqlQueries.DELETE_KEY_STORE_BY_FILE_NAME)) { + statement.setString(KeyStoreTableColumns.FILE_NAME, fileName); + statement.setString(KeyStoreTableColumns.TENANT_UUID, tenantUUID); + statement.executeUpdate(); + + connection.commit(); + } catch (SQLException e) { + connection.rollback(); + throw new KeyStoreManagementException( + KeyStoreDAOConstants.ErrorMessages.ERROR_DELETE_KEY_STORE_BY_FILE_NAME, e); + } + } catch (SQLException e) { + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages.ERROR_CANNOT_RETRIEVE_DB_CONN, e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void updateKeyStore(String tenantUUID, KeyStoreModel keyStoreModel) throws KeyStoreManagementException { + + try (Connection connection = DatabaseUtil.getDBConnection(this.dataSource)) { + try { + processUpdateKeyStore(connection, keyStoreModel, tenantUUID); + connection.commit(); + } catch (SQLException e) { + connection.rollback(); + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages.ERROR_UPDATE_KEY_STORE, e); + } + } catch (SQLException e) { + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages.ERROR_CANNOT_RETRIEVE_DB_CONN, e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addPubCertIdToKeyStore(String tenantUUID, String fileName, String pubCertId) + throws KeyStoreManagementException { + + try (Connection connection = DatabaseUtil.getDBConnection(this.dataSource)) { + try (NamedPreparedStatement statement = new NamedPreparedStatement(connection, + KeyStoreDAOConstants.SqlQueries.ADD_PUB_CERT_ID_TO_KEY_STORE)) { + statement.setString(KeyStoreTableColumns.PUB_CERT_ID, pubCertId); + statement.setTimeStamp(KeyStoreTableColumns.LAST_UPDATED, new Timestamp(new Date().getTime()), + CALENDAR); + statement.setString(KeyStoreTableColumns.FILE_NAME, fileName); + statement.setString(KeyStoreTableColumns.TENANT_UUID, tenantUUID); + statement.executeUpdate(); + + connection.commit(); + } catch (SQLException e) { + connection.rollback(); + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages + .ERROR_LINK_PUB_CERT_TO_KEY_STORE, e); + } + } catch (SQLException e) { + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages.ERROR_CANNOT_RETRIEVE_DB_CONN, e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Optional getPubCertIdFromKeyStore(String tenantUUID, String fileName) + throws KeyStoreManagementException { + + try (Connection connection = DatabaseUtil.getDBConnection(this.dataSource)) { + try (NamedPreparedStatement statement = new NamedPreparedStatement(connection, + KeyStoreDAOConstants.SqlQueries.GET_PUB_CERT_ID_OF_KEY_STORE)) { + statement.setString(KeyStoreTableColumns.FILE_NAME, fileName); + statement.setString(KeyStoreTableColumns.TENANT_UUID, tenantUUID); + try (ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + return Optional.ofNullable(resultSet.getString(KeyStoreTableColumns.PUB_CERT_ID)); + } + } + } catch (SQLException e) { + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages + .ERROR_GET_PUB_CERT_OF_KEY_STORE, e); + } + } catch (SQLException e) { + throw new KeyStoreManagementException(KeyStoreDAOConstants.ErrorMessages.ERROR_CANNOT_RETRIEVE_DB_CONN, e); + } + return Optional.empty(); + } + + private KeyStoreModel mapResultToKeyStoreModel(ResultSet resultSet) throws SQLException { + + return new KeyStoreModel.KeyStoreModelBuilder() + .type(resultSet.getString(KeyStoreTableColumns.TYPE)) + .provider(resultSet.getString(KeyStoreTableColumns.PROVIDER)) + .fileName(resultSet.getString(KeyStoreTableColumns.FILE_NAME)) + .password(resultSet.getString(KeyStoreTableColumns.PASSWORD).toCharArray()) + .privateKeyAlias(resultSet.getString(KeyStoreTableColumns.PRIVATE_KEY_ALIAS)) + .privateKeyPass(resultSet.getString(KeyStoreTableColumns.PRIVATE_KEY_PASS).toCharArray()) + .content(resultSet.getBytes(KeyStoreTableColumns.CONTENT)) + .lastUpdated(resultSet.getTimestamp(KeyStoreTableColumns.LAST_UPDATED)) + .build(); + } + + private void processAddKeyStore(Connection connection, KeyStoreModel keyStoreModel, String tenantUUID) + throws SQLException { + + try (NamedPreparedStatement statement = new NamedPreparedStatement(connection, + KeyStoreDAOConstants.SqlQueries.ADD_KEY_STORE)) { + statement.setString(KeyStoreTableColumns.ID, UUID.randomUUID().toString()); + statement.setString(KeyStoreTableColumns.FILE_NAME, keyStoreModel.getFileName()); + statement.setString(KeyStoreTableColumns.TYPE, keyStoreModel.getType()); + statement.setString(KeyStoreTableColumns.PROVIDER, keyStoreModel.getProvider()); + statement.setString(KeyStoreTableColumns.PASSWORD, String.valueOf(keyStoreModel.getPassword())); + // todo: check whether are we storing a null or an empty string when the field is not set? + statement.setString(KeyStoreTableColumns.PRIVATE_KEY_ALIAS, keyStoreModel.getPrivateKeyAlias()); + statement.setString(KeyStoreTableColumns.PRIVATE_KEY_PASS, + String.valueOf(keyStoreModel.getPrivateKeyPass())); + statement.setString(KeyStoreTableColumns.TENANT_UUID, tenantUUID); + statement.setTimeStamp(KeyStoreTableColumns.LAST_UPDATED, new Timestamp(new Date().getTime()), CALENDAR); + statement.setBytes(10, keyStoreModel.getContent()); + statement.executeUpdate(); + } + } + + private void processUpdateKeyStore(Connection connection, KeyStoreModel keyStoreModel, String tenantUUID) + throws SQLException { + + try (NamedPreparedStatement statement = new NamedPreparedStatement(connection, + KeyStoreDAOConstants.SqlQueries.UPDATE_KEY_STORE_BY_FILE_NAME)) { + statement.setString(KeyStoreTableColumns.TYPE, keyStoreModel.getType()); + statement.setString(KeyStoreTableColumns.PROVIDER, keyStoreModel.getProvider()); + statement.setString(KeyStoreTableColumns.PASSWORD, String.valueOf(keyStoreModel.getPassword())); + statement.setString(KeyStoreTableColumns.PRIVATE_KEY_ALIAS, keyStoreModel.getPrivateKeyAlias()); + statement.setString(KeyStoreTableColumns.PRIVATE_KEY_PASS, + String.valueOf(keyStoreModel.getPrivateKeyPass())); + statement.setTimeStamp(KeyStoreTableColumns.LAST_UPDATED, new Timestamp(new Date().getTime()), CALENDAR); + statement.setBytes(7, keyStoreModel.getContent()); + statement.setString(KeyStoreTableColumns.FILE_NAME, keyStoreModel.getFileName()); + statement.setString(KeyStoreTableColumns.TENANT_UUID, tenantUUID); + statement.executeUpdate(); + } + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/impl/PubCertDAOImpl.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/impl/PubCertDAOImpl.java new file mode 100755 index 00000000000..f16f4e644c1 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/dao/impl/PubCertDAOImpl.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.dao.impl; + +import org.wso2.carbon.core.internal.KeyStoreMgtDataHolder; +import org.wso2.carbon.database.utils.jdbc.NamedPreparedStatement; +import org.wso2.carbon.core.keystore.KeyStoreManagementException; +import org.wso2.carbon.core.keystore.dao.PubCertDAO; +import org.wso2.carbon.core.keystore.dao.constants.PubCertDAOConstants; +import org.wso2.carbon.core.keystore.dao.constants.PubCertDAOConstants.PubCertTableColumns; +import org.wso2.carbon.core.keystore.model.PubCertModel; +import org.wso2.carbon.user.core.util.DatabaseUtil; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Optional; +import java.util.UUID; + +import javax.sql.DataSource; + +/** + * This class provides the implementation of the PubCertDAO interface. + */ +public class PubCertDAOImpl implements PubCertDAO { + + private final DataSource dataSource; + + public PubCertDAOImpl() { + + this.dataSource = KeyStoreMgtDataHolder.getDataSource(); + } + + /** + * {@inheritDoc} + */ + @Override + public String addPubCert(PubCertModel pubCertModel) throws KeyStoreManagementException { + + try (Connection connection = DatabaseUtil.getDBConnection(this.dataSource)) { + try { + String uuid = processAddPubCert(connection, pubCertModel); + connection.commit(); + return uuid; + } catch (SQLException e) { + connection.rollback(); + throw new KeyStoreManagementException(PubCertDAOConstants.ErrorMessages.ERROR_MESSAGE_ADDING_PUB_CERT, + e); + } + } catch (SQLException e) { + throw new KeyStoreManagementException(PubCertDAOConstants.ErrorMessages.DB_CONN_RETRIEVAL_ERROR_MSG, e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Optional getPubCert(String uuid) throws KeyStoreManagementException { + + PubCertModel pubCertModel = null; + + try (Connection connection = DatabaseUtil.getDBConnection(this.dataSource)) { + try (NamedPreparedStatement statement = new NamedPreparedStatement(connection, + PubCertDAOConstants.SQLQueries.GET_PUB_CERT)) { + statement.setString(PubCertTableColumns.ID, uuid); + statement.setMaxRows(1); + try (ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + pubCertModel = new PubCertModel(); + pubCertModel.setFileNameAppender(resultSet.getString(PubCertTableColumns.FILE_NAME_APPENDER)); + pubCertModel.setContent(resultSet.getBytes(PubCertTableColumns.CONTENT)); + } + } + } catch (SQLException e) { + throw new KeyStoreManagementException(PubCertDAOConstants.ErrorMessages + .ERROR_MESSAGE_RETRIEVING_PUB_CERT, e); + } + } catch (SQLException e) { + throw new KeyStoreManagementException(PubCertDAOConstants.ErrorMessages.DB_CONN_RETRIEVAL_ERROR_MSG, e); + } + return Optional.ofNullable(pubCertModel); + } + + private String processAddPubCert(Connection connection, PubCertModel pubCertModel) + throws SQLException { + + String id = UUID.randomUUID().toString(); + + try (NamedPreparedStatement statement = new NamedPreparedStatement(connection, + PubCertDAOConstants.SQLQueries.ADD_PUB_CERT)) { + statement.setString(PubCertTableColumns.ID, id); + statement.setString(PubCertTableColumns.FILE_NAME_APPENDER, pubCertModel.getFileNameAppender()); + statement.setBytes(3, pubCertModel.getContent()); + statement.executeUpdate(); + } + return id; + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/model/KeyStoreModel.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/model/KeyStoreModel.java new file mode 100755 index 00000000000..ffd0c018152 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/model/KeyStoreModel.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.model; + +import java.util.Date; + +/** + * Model class for KeyStore. + */ +public class KeyStoreModel { + + // TODO: check whether we need the uuid. + private final String id; + private final String fileName; + private final String type; + private final String provider; + private final char[] password; + private final String privateKeyAlias; + private final char[] privateKeyPass; + private final Date lastUpdated; + private final byte[] content; + + public KeyStoreModel(KeyStoreModelBuilder builder) { + + this.id = builder.id; + this.fileName = builder.fileName; + this.type = builder.type; + this.provider = builder.provider; + this.password = builder.password; + this.privateKeyAlias = builder.privateKeyAlias; + this.privateKeyPass = builder.privateKeyPass; + this.lastUpdated = builder.lastUpdated; + this.content = builder.content; + } + + public String getId() { + + return id; + } + + public String getFileName() { + + return fileName; + } + + public String getType() { + + return type; + } + + public String getProvider() { + + return provider; + } + + public char[] getPassword() { + + return password; + } + + public String getPrivateKeyAlias() { + + return privateKeyAlias; + } + + public char[] getPrivateKeyPass() { + + return privateKeyPass; + } + + public byte[] getContent() { + + return content; + } + + public Date getLastUpdated() { + + return lastUpdated; + } + + public static class KeyStoreModelBuilder { + + private String id; + private String fileName; + private String type; + private String provider; + private char[] password; + private String privateKeyAlias; + private char[] privateKeyPass; + private Date lastUpdated; + private byte[] content; + + public KeyStoreModelBuilder() { + // Default constructor. + } + + public KeyStoreModelBuilder id(String id) { + + this.id = id; + return this; + } + + public KeyStoreModelBuilder fileName(String fileName) { + + this.fileName = fileName; + return this; + } + + public KeyStoreModelBuilder type(String type) { + + this.type = type; + return this; + } + + public KeyStoreModelBuilder provider(String provider) { + + this.provider = provider; + return this; + } + + public KeyStoreModelBuilder password(char[] password) { + + this.password = password; + return this; + } + + public KeyStoreModelBuilder privateKeyAlias(String privateKeyAlias) { + + this.privateKeyAlias = privateKeyAlias; + return this; + } + + public KeyStoreModelBuilder privateKeyPass(char[] privateKeyPass) { + + this.privateKeyPass = privateKeyPass; + return this; + } + + public KeyStoreModelBuilder lastUpdated(Date lastUpdated) { + + this.lastUpdated = lastUpdated; + return this; + } + + public KeyStoreModelBuilder content(byte[] content) { + + this.content = content; + return this; + } + + public KeyStoreModel build() { + + return new KeyStoreModel(this); + } + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/model/PubCertModel.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/model/PubCertModel.java new file mode 100755 index 00000000000..ce69dd25945 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/model/PubCertModel.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.model; + +/** + * Model class for Public Certificate. + */ +public class PubCertModel { + + private String fileNameAppender; + + private byte[] content; + + public PubCertModel() { + // Empty constructor for default initialization + } + + public String getFileNameAppender() { + + return fileNameAppender; + } + + public void setFileNameAppender(String fileNameAppender) { + + this.fileNameAppender = fileNameAppender; + } + + public byte[] getContent() { + + return content; + } + + public void setContent(byte[] content) { + + this.content = content; + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/CertData.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/CertData.java new file mode 100755 index 00000000000..a18429aaea3 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/CertData.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2005, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.service; + +import java.math.BigInteger; + +/** + * Model class for certificate data. + */ +public class CertData { + + private String alias; + private String subjectDN; + private String issuerDN; + private BigInteger serialNumber; + private int version; + private String notBefore; + private String notAfter; + private String publicKey; + + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public String getSubjectDN() { + return subjectDN; + } + + public void setSubjectDN(String subjectDN) { + this.subjectDN = subjectDN; + } + + public String getIssuerDN() { + return issuerDN; + } + + public void setIssuerDN(String issuerDN) { + this.issuerDN = issuerDN; + } + + public BigInteger getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(BigInteger serialNumber) { + this.serialNumber = serialNumber; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public String getNotBefore() { + return notBefore; + } + + public void setNotBefore(String notBefore) { + this.notBefore = notBefore; + } + + public String getNotAfter() { + return notAfter; + } + + public void setNotAfter(String notAfter) { + this.notAfter = notAfter; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/CertDataDetail.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/CertDataDetail.java new file mode 100755 index 00000000000..c882c0c0201 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/CertDataDetail.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2010, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.service; + +import java.security.cert.X509Certificate; + +/** + * Model class for certificate data with detail. + */ +public class CertDataDetail extends CertData { + private X509Certificate certificate; + + public X509Certificate getCertificate() { + return certificate; + } + + public void setCertificate(X509Certificate certificate) { + this.certificate = certificate; + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/KeyStoreData.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/KeyStoreData.java new file mode 100755 index 00000000000..2fb6cda220f --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/KeyStoreData.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2010, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.service; + +/** + * Model class for key store data. + */ +public class KeyStoreData { + + private CertData[] certs; + private CertData key; + private String keyValue; + private String keyStoreName = null; + private String keyStoreType = null; + private String provider = null; + private String pubKeyFilePath = null; + private boolean isPrivateStore = false; + + public String getKeyStoreName() { + return keyStoreName; + } + + public void setKeyStoreName(String keyStoreName) { + this.keyStoreName = keyStoreName; + } + + public String getKeyStoreType() { + return keyStoreType; + } + + public void setKeyStoreType(String keyStoreType) { + this.keyStoreType = keyStoreType; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public boolean getPrivateStore() { + return isPrivateStore; + } + + public void setPrivateStore(boolean isPrivateStore) { + this.isPrivateStore = isPrivateStore; + } + + public CertData[] getCerts() { + return certs; + } + + public void setCerts(CertData[] certs) { + this.certs = certs; + } + + public CertData getKey() { + return key; + } + + public void setKey(CertData key) { + this.key = key; + } + + public String getKeyValue() { + return keyValue; + } + + public void setKeyValue(String keyValue) { + this.keyValue = keyValue; + } + + public String getPubKeyFilePath() { + return pubKeyFilePath; + } + + public void setPubKeyFilePath(String pubKeyFilePath) { + this.pubKeyFilePath = pubKeyFilePath; + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/PaginatedCertData.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/PaginatedCertData.java new file mode 100755 index 00000000000..aaf4f85d146 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/PaginatedCertData.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.service; + +import java.util.Arrays; + +/** + * Model class for paginated cert data. + */ +public class PaginatedCertData { + private CertData[] certDataSet; + + private int numberOfPages; + + public CertData[] getCertDataSet() { + return Arrays.copyOf(certDataSet, certDataSet.length); + } + + public void setCertDataSet(CertData[] certDataSet) { + this.certDataSet = Arrays.copyOf(certDataSet, certDataSet.length); + } + + public int getNumberOfPages() { + return numberOfPages; + } + + public void setNumberOfPages(int numberOfPages) { + this.numberOfPages = numberOfPages; + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/PaginatedKeyStoreData.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/PaginatedKeyStoreData.java new file mode 100755 index 00000000000..6c27a8d21d0 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/service/PaginatedKeyStoreData.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2010, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.service; + +/** + * Model class for paginated key store data. + */ +public class PaginatedKeyStoreData { + + private CertData key; + private String keyValue; + private String keyStoreName = null; + private String keyStoreType = null; + private String provider = null; + private String pubKeyFilePath = null; + private boolean isPrivateStore = false; + private PaginatedCertData paginatedCertData; + private PaginatedCertData paginatedKeyData; + + public PaginatedCertData getPaginatedKeyData() { + return paginatedKeyData; + } + + public void setPaginatedKeyData(PaginatedCertData paginatedKeyData) { + this.paginatedKeyData = paginatedKeyData; + } + + public String getKeyStoreName() { + return keyStoreName; + } + + public void setKeyStoreName(String keyStoreName) { + this.keyStoreName = keyStoreName; + } + + public String getKeyStoreType() { + return keyStoreType; + } + + public void setKeyStoreType(String keyStoreType) { + this.keyStoreType = keyStoreType; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public boolean getPrivateStore() { + return isPrivateStore; + } + + public void setPrivateStore(boolean isPrivateStore) { + this.isPrivateStore = isPrivateStore; + } + + public CertData getKey() { + return key; + } + + public void setKey(CertData key) { + this.key = key; + } + + public String getKeyValue() { + return keyValue; + } + + public void setKeyValue(String keyValue) { + this.keyValue = keyValue; + } + + public String getPubKeyFilePath() { + return pubKeyFilePath; + } + + public void setPubKeyFilePath(String pubKeyFilePath) { + this.pubKeyFilePath = pubKeyFilePath; + } + + public PaginatedCertData getPaginatedCertData() { + return paginatedCertData; + } + + public void setPaginatedCertData(PaginatedCertData paginatedCertData) { + this.paginatedCertData = paginatedCertData; + } + +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/util/KeyStoreIOStreamUtils.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/util/KeyStoreIOStreamUtils.java new file mode 100644 index 00000000000..57df95783fd --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/util/KeyStoreIOStreamUtils.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; + +/** + * Utility class for IO streams. + */ +public class KeyStoreIOStreamUtils { + + private static final Log log = LogFactory.getLog(KeyStoreIOStreamUtils.class); + + public static void closeAllStreams(InputStream input, OutputStream output){ + closeInputStream(input); + closeOutputStream(output); + } + + public static void closeInputStream(InputStream input) { + try { + if (input != null) { + input.close(); + } + } catch (IOException ioe) { + log.error("Error occurred while closing Input stream", ioe); + } + } + + public static void closeOutputStream(OutputStream output) { + try { + if (output != null) { + output.close(); + } + } catch (IOException ioe) { + log.error("Error occurred while closing Output stream", ioe); + } + } + + public static void flushOutputStream(OutputStream output) { + try { + if (output != null) { + output.flush(); + } + } catch (IOException ioe) { + log.error("Error occurred while flushing Output stream", ioe); + } + } + + public static void closeReader(Reader reader) { + try { + if (reader != null) { + reader.close(); + } + } catch (IOException ioe) { + log.error("Error occurred while closing Reader", ioe); + } + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/util/KeyStoreMgtUtil.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/util/KeyStoreMgtUtil.java new file mode 100755 index 00000000000..7efde7e89a5 --- /dev/null +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/keystore/util/KeyStoreMgtUtil.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2010, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 org.wso2.carbon.core.keystore.util; + +import org.apache.axis2.context.ConfigurationContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.core.internal.KeyStoreMgtDataHolder; +import org.wso2.carbon.core.keystore.KeyStoreManagementException; +import org.wso2.carbon.user.api.Tenant; +import org.wso2.carbon.user.api.TenantManager; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.utils.ServerConstants; +import org.wso2.carbon.utils.WSO2Constants; +import org.wso2.carbon.utils.multitenancy.MultitenantConstants; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.Hashtable; +import java.util.Map; + +/** + * This class includes Key Store management utility functions. + */ +public class KeyStoreMgtUtil { + + private static final Log log = LogFactory.getLog(KeyStoreMgtUtil.class); + + private KeyStoreMgtUtil(){} + + /** + * Dumping the generated pub. cert to a file + * + * @param configurationContext + * @param cert content of the certificate + * @param fileName file name + * @return file system location of the pub. cert + */ + public static String dumpCert(ConfigurationContext configurationContext, byte[] cert, + String fileName) { + if (!verifyCertExistence(fileName, configurationContext)) { + String workDir = (String) configurationContext.getProperty(ServerConstants.WORK_DIR); + File pubCert = new File(workDir + File.separator + "pub_certs"); + + if (fileName == null) { + fileName = String.valueOf(System.currentTimeMillis() + new SecureRandom().nextDouble()) + ".cert"; + } + if (!pubCert.exists()) { + pubCert.mkdirs(); + } + + String filePath = workDir + File.separator + "pub_certs" + File.separator + fileName; + OutputStream outStream = null; + try { + outStream = new FileOutputStream(filePath); + outStream.write(cert); + } catch (Exception e) { + String msg = "Error when writing the public certificate to a file"; + log.error(msg); + throw new SecurityException("msg", e); + } finally { + KeyStoreIOStreamUtils.flushOutputStream(outStream); + KeyStoreIOStreamUtils.closeOutputStream(outStream); + } + + Map fileResourcesMap = (Map) configurationContext.getProperty(WSO2Constants.FILE_RESOURCE_MAP); + if (fileResourcesMap == null) { + fileResourcesMap = new Hashtable(); + configurationContext.setProperty(WSO2Constants.FILE_RESOURCE_MAP, fileResourcesMap); + } + + fileResourcesMap.put(fileName, filePath); + } + return WSO2Constants.ContextPaths.DOWNLOAD_PATH + "?id=" + fileName; + } + + /** + * Check whether the certificate is available in the file system + * + * @param fileName file name + * @param configurationContext configuration context of the current message + */ + private static boolean verifyCertExistence(String fileName, ConfigurationContext configurationContext) { + String workDir = (String) configurationContext.getProperty(ServerConstants.WORK_DIR); + String filePath = workDir + File.separator + "pub_certs" + File.separator + fileName; + File pubCert = new File(workDir + File.separator + "pub_certs" + File.separator + fileName); + + //if cert is still available then exit + if (pubCert.exists()) { + Map fileResourcesMap = (Map) configurationContext.getProperty(WSO2Constants.FILE_RESOURCE_MAP); + if (fileResourcesMap == null) { + fileResourcesMap = new Hashtable(); + configurationContext.setProperty(WSO2Constants.FILE_RESOURCE_MAP, fileResourcesMap); + } + if (fileResourcesMap.get(fileName) == null) { + fileResourcesMap.put(fileName, filePath); + } + return true; + } + return false; + } + + /** + * Get the tenant UUID for the given tenant ID. + * @param tenantId Tenant ID + * @return Tenant UUID + * @throws KeyStoreManagementException If an error occurs while getting the tenant UUID. + * @throws UserStoreException If an error occurs while getting the tenant UUID. + */ + public static String getTenantUUID(int tenantId) throws KeyStoreManagementException, UserStoreException { + + // Super tenant does not have a tenant UUID. Therefore, set a hard coded value. + if (tenantId == MultitenantConstants.SUPER_TENANT_ID) { + // Set a hard length of 36 characters for super tenant ID. + // This is to avoid the database column length constraint violation. + return String.format("%1$-36d", tenantId); + } + + if (tenantId != MultitenantConstants.INVALID_TENANT_ID) { + TenantManager tenantManager = KeyStoreMgtDataHolder.getRealmService().getTenantManager(); + Tenant tenant = tenantManager.getTenant(tenantId); + return tenant.getTenantUniqueID(); + } + + throw new KeyStoreManagementException("Invalid tenant id: " + tenantId); + } +} diff --git a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/util/KeyStoreManager.java b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/util/KeyStoreManager.java index 1cb55cecb84..05c433ddb9b 100644 --- a/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/util/KeyStoreManager.java +++ b/core/org.wso2.carbon.core/src/main/java/org/wso2/carbon/core/util/KeyStoreManager.java @@ -23,10 +23,14 @@ import org.wso2.carbon.base.api.ServerConfigurationService; import org.wso2.carbon.core.RegistryResources; import org.wso2.carbon.core.internal.CarbonCoreDataHolder; -import org.wso2.carbon.registry.api.Registry; +import org.wso2.carbon.core.keystore.KeyStoreManagementException; +import org.wso2.carbon.core.keystore.dao.KeyStoreDAO; +import org.wso2.carbon.core.keystore.dao.impl.KeyStoreDAOImpl; +import org.wso2.carbon.core.keystore.model.KeyStoreModel; +import org.wso2.carbon.core.keystore.util.KeyStoreMgtUtil; import org.wso2.carbon.registry.core.Resource; -import org.wso2.carbon.registry.core.exceptions.RegistryException; import org.wso2.carbon.registry.core.service.RegistryService; +import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.utils.CarbonUtils; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; @@ -36,7 +40,9 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.Date; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; /** @@ -49,17 +55,16 @@ public class KeyStoreManager { private KeyStore primaryKeyStore = null; private KeyStore registryKeyStore = null; private KeyStore internalKeyStore = null; - private static ConcurrentHashMap mtKeyStoreManagers = - new ConcurrentHashMap(); - private static Log log = LogFactory.getLog(KeyStoreManager.class); + private static final ConcurrentHashMap mtKeyStoreManagers = new ConcurrentHashMap<>(); + private static final Log LOG = LogFactory.getLog(KeyStoreManager.class); - private Registry registry = null; - private ConcurrentHashMap loadedKeyStores = null; - private int tenantId = MultitenantConstants.SUPER_TENANT_ID; + private KeyStoreDAO keyStoreDAO = null; - private ServerConfigurationService serverConfigService; + private final ConcurrentHashMap loadedKeyStores; + private final int tenantId; + private String tenantUUID = null; - private RegistryService registryService; + private final ServerConfigurationService serverConfigService; /** * Private Constructor of the KeyStoreManager @@ -71,26 +76,21 @@ public class KeyStoreManager { private KeyStoreManager(int tenantId, ServerConfigurationService serverConfigService, RegistryService registryService) { this.serverConfigService = serverConfigService; - this.registryService = registryService; - loadedKeyStores = new ConcurrentHashMap(); + loadedKeyStores = new ConcurrentHashMap<>(); this.tenantId = tenantId; try { - registry = registryService.getGovernanceSystemRegistry(tenantId); - } catch (RegistryException e) { - String message = "Error when retrieving the system governance registry"; - log.error(message, e); - throw new SecurityException(message, e); + this.tenantUUID = KeyStoreMgtUtil.getTenantUUID(tenantId); + } catch (KeyStoreManagementException | UserStoreException e) { + LOG.error("Error while getting the tenant UUID for tenant ID : " + tenantId, e); } + + keyStoreDAO = new KeyStoreDAOImpl(); } public ServerConfigurationService getServerConfigService() { return serverConfigService; } - public RegistryService getRegistryService() { - return registryService; - } - /** * Get a KeyStoreManager instance for that tenant. This method will return an KeyStoreManager * instance if exists, or creates a new one. Only use this at runtime, or else, @@ -133,20 +133,20 @@ public KeyStore getKeyStore(String keyStoreName) throws Exception { return loadedKeyStores.get(keyStoreName).getKeyStore(); } - String path = RegistryResources.SecurityManagement.KEY_STORES + "/" + keyStoreName; - if (registry.resourceExists(path)) { - org.wso2.carbon.registry.api.Resource resource = registry.get(path); - byte[] bytes = (byte[]) resource.getContent(); - KeyStore keyStore = KeyStore.getInstance(resource - .getProperty(RegistryResources.SecurityManagement.PROP_TYPE)); + Optional optionalKeyStoreModel = keyStoreDAO.getKeyStore(this.tenantUUID, keyStoreName); + if (optionalKeyStoreModel.isPresent()) { + KeyStoreModel keyStoreModel = optionalKeyStoreModel.get(); + + byte[] bytes = keyStoreModel.getContent(); + KeyStore keyStore = KeyStore.getInstance(keyStoreModel.getType()); CryptoUtil cryptoUtil = CryptoUtil.getDefaultCryptoUtil(); - String encryptedPassword = resource - .getProperty(RegistryResources.SecurityManagement.PROP_PASSWORD); - String password = new String(cryptoUtil.base64DecodeAndDecrypt(encryptedPassword)); + char[] encryptedPassword = keyStoreModel.getPassword(); + char[] password = new String(cryptoUtil.base64DecodeAndDecrypt(Arrays.toString(encryptedPassword))) + .toCharArray(); ByteArrayInputStream stream = new ByteArrayInputStream(bytes); - keyStore.load(stream, password.toCharArray()); - KeyStoreBean keyStoreBean = new KeyStoreBean(keyStore, resource.getLastModified()); - resource.discard(); + keyStore.load(stream, password); + + KeyStoreBean keyStoreBean = new KeyStoreBean(keyStore, keyStoreModel.getLastUpdated()); if (loadedKeyStores.containsKey(keyStoreName)) { loadedKeyStores.replace(keyStoreName, keyStoreBean); @@ -172,60 +172,42 @@ public Key getPrivateKey(String keyStoreName, String alias) { return getDefaultPrivateKey(); } - String path = RegistryResources.SecurityManagement.KEY_STORES + "/" + keyStoreName; - org.wso2.carbon.registry.api.Resource resource; + KeyStoreModel keyStoreModel; KeyStore keyStore; - if (registry.resourceExists(path)) { - resource = registry.get(path); + Optional optionalKeyStoreModel = keyStoreDAO.getKeyStore(this.tenantUUID, keyStoreName); + if (optionalKeyStoreModel.isPresent()) { + keyStoreModel = optionalKeyStoreModel.get(); } else { - throw new SecurityException("Given Key store is not available in registry : " + keyStoreName); + throw new SecurityException("Given Key store is not available in Database : " + keyStoreName); } CryptoUtil cryptoUtil = CryptoUtil.getDefaultCryptoUtil(); - String encryptedPassword = resource - .getProperty(RegistryResources.SecurityManagement.PROP_PRIVATE_KEY_PASS); - String privateKeyPasswd = new String(cryptoUtil.base64DecodeAndDecrypt(encryptedPassword)); + char[] encryptedPassword = keyStoreModel.getPrivateKeyPass(); + char[] privateKeyPasswd = new String(cryptoUtil.base64DecodeAndDecrypt(Arrays.toString(encryptedPassword))) + .toCharArray(); if (isCachedKeyStoreValid(keyStoreName)) { keyStore = loadedKeyStores.get(keyStoreName).getKeyStore(); - return keyStore.getKey(alias, privateKeyPasswd.toCharArray()); - } else { - byte[] bytes = (byte[]) resource.getContent(); - String keyStorePassword = new String(cryptoUtil.base64DecodeAndDecrypt(resource.getProperty( - RegistryResources.SecurityManagement.PROP_PASSWORD))); - keyStore = KeyStore.getInstance(resource - .getProperty(RegistryResources.SecurityManagement.PROP_TYPE)); - ByteArrayInputStream stream = new ByteArrayInputStream(bytes); - keyStore.load(stream, keyStorePassword.toCharArray()); - - KeyStoreBean keyStoreBean = new KeyStoreBean(keyStore, resource.getLastModified()); - updateKeyStoreCache(keyStoreName, keyStoreBean); - return keyStore.getKey(alias, privateKeyPasswd.toCharArray()); + return keyStore.getKey(alias, privateKeyPasswd); } + byte[] bytes = keyStoreModel.getContent(); + char[] keyStorePassword = new String(cryptoUtil.base64DecodeAndDecrypt( + Arrays.toString(keyStoreModel.getPassword()))).toCharArray(); + keyStore = KeyStore.getInstance(keyStoreModel.getType()); + ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + keyStore.load(stream, keyStorePassword); + + KeyStoreBean keyStoreBean = new KeyStoreBean(keyStore, keyStoreModel.getLastUpdated()); + updateKeyStoreCache(keyStoreName, keyStoreBean); + return keyStore.getKey(alias, privateKeyPasswd); } catch (Exception e) { - log.error("Error loading the private key from the key store : " + keyStoreName); + LOG.error("Error loading the private key from the key store : " + keyStoreName); throw new SecurityException("Error loading the private key from the key store : " + keyStoreName, e); } } - /** - * Get the key store password of the given key store resource - * - * @param resource key store resource - * @return password of the key store - * @throws Exception Error when reading the registry resource of decrypting the password - */ - public String getPassword(Resource resource) throws Exception { - CryptoUtil cryptoUtil = CryptoUtil.getDefaultCryptoUtil(); - String encryptedPassword = resource - .getProperty(RegistryResources.SecurityManagement.PROP_PRIVATE_KEY_PASS); - return new String(cryptoUtil.base64DecodeAndDecrypt(encryptedPassword)); - - } - - /** * Get the key store password for the given key store name. * Note: Caching has been not implemented for this method @@ -234,16 +216,15 @@ public String getPassword(Resource resource) throws Exception { * @return KeyStore object * @throws Exception If there is not a key store with the given name */ - public String getKeyStorePassword(String keyStoreName) throws Exception { + public String getKeyStorePassword(String keyStoreName) throws KeyStoreManagementException, CryptoException { - String path = RegistryResources.SecurityManagement.KEY_STORES + "/" + keyStoreName; - if (registry.resourceExists(path)) { - org.wso2.carbon.registry.api.Resource resource = registry.get(path); + Optional optionalKeyStoreModel = keyStoreDAO.getKeyStore(this.tenantUUID, keyStoreName); + if (optionalKeyStoreModel.isPresent()) { + KeyStoreModel keyStoreModel = optionalKeyStoreModel.get(); CryptoUtil cryptoUtil = CryptoUtil.getDefaultCryptoUtil(); - String encryptedPassword = resource - .getProperty(RegistryResources.SecurityManagement.PROP_PASSWORD); + char[] encryptedPassword = keyStoreModel.getPassword(); if(encryptedPassword != null){ - return new String(cryptoUtil.base64DecodeAndDecrypt(encryptedPassword)); + return new String(cryptoUtil.base64DecodeAndDecrypt(Arrays.toString(encryptedPassword))); } else { throw new SecurityException("Key Store Password of " + keyStoreName + " does not exist."); } @@ -267,38 +248,43 @@ public void updateKeyStore(String name, KeyStore keyStore) throws Exception { config .getFirstProperty(RegistryResources.SecurityManagement.SERVER_PRIMARY_KEYSTORE_FILE)) .getAbsolutePath(); - FileOutputStream out = null; - try { - out = new FileOutputStream(file); + try (FileOutputStream out = new FileOutputStream(file)) { String password = config .getFirstProperty(RegistryResources.SecurityManagement.SERVER_PRIMARY_KEYSTORE_PASSWORD); keyStore.store(out, password.toCharArray()); - } finally { - if (out != null) { - out.close(); - } } - return; } - String path = RegistryResources.SecurityManagement.KEY_STORES + "/" + name; - - org.wso2.carbon.registry.api.Resource resource = registry.get(path); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - CryptoUtil cryptoUtil = CryptoUtil.getDefaultCryptoUtil(); - String encryptedPassword = resource - .getProperty(RegistryResources.SecurityManagement.PROP_PASSWORD); - String password = new String(cryptoUtil.base64DecodeAndDecrypt(encryptedPassword)); - keyStore.store(outputStream, password.toCharArray()); - outputStream.flush(); - outputStream.close(); - - resource.setContent(outputStream.toByteArray()); - - registry.put(path, resource); - resource.discard(); - updateKeyStoreCache(name, new KeyStoreBean(keyStore, new Date())); + Optional optionalKeyStoreModel = keyStoreDAO.getKeyStore(this.tenantUUID, name); + if (optionalKeyStoreModel.isPresent()) { + KeyStoreModel keyStoreModel = optionalKeyStoreModel.get(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + CryptoUtil cryptoUtil = CryptoUtil.getDefaultCryptoUtil(); + char[] encryptedPassword = keyStoreModel.getPassword(); + char[] password = new String(cryptoUtil.base64DecodeAndDecrypt(Arrays.toString(encryptedPassword))) + .toCharArray(); + keyStore.store(outputStream, password); + outputStream.flush(); + outputStream.close(); + + KeyStoreModel updatedKeyStoreModel = new KeyStoreModel.KeyStoreModelBuilder() + .id(keyStoreModel.getId()) + .fileName(keyStoreModel.getFileName()) + .type(keyStoreModel.getType()) + .provider(keyStoreModel.getProvider()) + .password(keyStoreModel.getPassword()) + .privateKeyAlias(keyStoreModel.getPrivateKeyAlias()) + .privateKeyPass(keyStoreModel.getPrivateKeyPass()) + .content(outputStream.toByteArray()) + .build(); + + keyStoreDAO.updateKeyStore(this.tenantUUID, updatedKeyStoreModel); + + // TODO: is it correct to use new Date() here? The value stored in DB might be different. + updateKeyStoreCache(name, new KeyStoreBean(keyStore, new Date())); + } else { + throw new SecurityException("Key Store with a name : " + name + " does not exist."); + } } /** @@ -487,19 +473,21 @@ public X509Certificate getDefaultPrimaryCertificate() throws Exception { } private boolean isCachedKeyStoreValid(String keyStoreName) { - String path = RegistryResources.SecurityManagement.KEY_STORES + "/" + keyStoreName; boolean cachedKeyStoreValid = false; try { if (loadedKeyStores.containsKey(keyStoreName)) { - org.wso2.carbon.registry.api.Resource metaDataResource = registry.get(path); - KeyStoreBean keyStoreBean = loadedKeyStores.get(keyStoreName); - if (keyStoreBean.getLastModifiedDate().equals(metaDataResource.getLastModified())) { - cachedKeyStoreValid = true; + Optional optionalKeyStoreModel = keyStoreDAO.getKeyStore(this.tenantUUID, keyStoreName); + if (optionalKeyStoreModel.isPresent()) { + KeyStoreModel keyStoreModel = optionalKeyStoreModel.get(); + KeyStoreBean keyStoreBean = loadedKeyStores.get(keyStoreName); + if (keyStoreBean.getLastModifiedDate().equals(keyStoreModel.getLastUpdated())) { + cachedKeyStoreValid = true; + } } } - } catch (org.wso2.carbon.registry.api.RegistryException e) { - String errorMsg = "Error reading key store meta data from registry."; - log.error(errorMsg, e); + } catch (KeyStoreManagementException e) { + String errorMsg = "Error reading key store meta data from Database."; + LOG.error(errorMsg, e); throw new SecurityException(errorMsg, e); } return cachedKeyStoreValid; @@ -524,7 +512,7 @@ public KeyStore loadKeyStoreFromFileSystem(String keyStorePath, String password, return store; } catch (Exception e) { String errorMsg = "Error loading the key store from the given location."; - log.error(errorMsg); + LOG.error(errorMsg); throw new SecurityException(errorMsg, e); } finally { try { @@ -532,7 +520,7 @@ public KeyStore loadKeyStoreFromFileSystem(String keyStorePath, String password, inputStream.close(); } } catch (IOException e) { - log.warn("Error when closing the input stream.", e); + LOG.warn("Error when closing the input stream.", e); } } } diff --git a/distribution/kernel/carbon-home/repository/resources/conf/default.json b/distribution/kernel/carbon-home/repository/resources/conf/default.json index 9db941841cd..69bf6cf4859 100644 --- a/distribution/kernel/carbon-home/repository/resources/conf/default.json +++ b/distribution/kernel/carbon-home/repository/resources/conf/default.json @@ -221,6 +221,6 @@ "tenant_mgt.enable_tenant_theme_mgt" : true, "tenant_mgt.enable_tenant_validation_for_nonsaas_username" : true, "jce_provider.provider_name" : "BC", - "signature_util.enable_sha256_algo" : true - + "signature_util.enable_sha256_algo" : true, + "keystore_data_persistence_manager.datasource.name" : "jdbc/SHARED_DB" } diff --git a/distribution/kernel/carbon-home/repository/resources/conf/templates/repository/conf/carbon.xml.j2 b/distribution/kernel/carbon-home/repository/resources/conf/templates/repository/conf/carbon.xml.j2 index 8c03c1b8460..22cde73233e 100644 --- a/distribution/kernel/carbon-home/repository/resources/conf/templates/repository/conf/carbon.xml.j2 +++ b/distribution/kernel/carbon-home/repository/resources/conf/templates/repository/conf/carbon.xml.j2 @@ -805,4 +805,13 @@ {{signature_util.enable_sha256_algo}} + + + + + + {{keystore_data_persistence_manager.datasource.name}} + + diff --git a/parent/pom.xml b/parent/pom.xml index 89a7af1418d..6ee86d3f66f 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -305,6 +305,10 @@ ${carbon.kernel.version} [${carbon.kernel.version}, 4.10.0) + + 2.1.3 + [2.0.0,2.2.0) + 1.1.200.v20160504-1450 3.8.0.v20160509-1230 @@ -1743,6 +1747,11 @@ org.wso2.carbon.integration.test.common.admin.clients ${carbon.kernel.version} + + org.wso2.carbon.utils + org.wso2.carbon.database.utils + ${org.wso2.carbon.database.utils.version} + org.apache.woden woden-api