Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move keystore persistence manager implementations to org.wso2.carbon.utils component #4143

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/org.wso2.carbon.core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@
<Bundle-SymbolicName>org.wso2.carbon.core</Bundle-SymbolicName>
<Bundle-Activator>org.wso2.carbon.core.internal.CarbonCoreActivator</Bundle-Activator>
<Private-Package>
org.wso2.carbon.core.internal, org.wso2.carbon.core.encryption, org.wso2.carbon.core.keystore.persistence
org.wso2.carbon.core.internal, org.wso2.carbon.core.encryption
</Private-Package>
<Export-Package>
!org.wso2.carbon.core.internal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,41 @@

import org.apache.axiom.om.OMElement;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.CarbonException;
import org.wso2.carbon.base.ServerConfiguration;
import org.wso2.carbon.base.api.ServerConfigurationService;
import org.wso2.carbon.core.RegistryResources;
import org.wso2.carbon.core.internal.CarbonCoreDataHolder;
import org.wso2.carbon.core.keystore.persistence.RegistryKeyStorePersistenceManager;
import org.wso2.carbon.core.security.KeyStoreMetadata;
import org.wso2.carbon.keystore.persistence.RegistryKeyStorePersistenceManager;
import org.wso2.carbon.keystore.persistence.model.KeyStoreModel;
import org.wso2.carbon.registry.core.service.RegistryService;
import org.wso2.carbon.utils.CarbonUtils;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.utils.security.KeystoreUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
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.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
Expand Down Expand Up @@ -215,8 +222,38 @@ public void addKeyStore(byte[] keystoreContent, String filename, char[] password
if (KeyStoreUtil.isPrimaryStore(filename) || KeyStoreUtil.isTrustStore(filename)) {
throw new SecurityException("Key store " + filename + " already available");
}
registryKeyStorePersistenceManager.addKeystore(filename, keystoreContent, provider, type, passwordChar,
privateKeyPasswordChar, tenantId);

KeyStoreModel keyStoreModel = new KeyStoreModel();
keyStoreModel.setName(filename);
keyStoreModel.setContent(keystoreContent);
keyStoreModel.setType(type);
keyStoreModel.setProvider(provider);
keyStoreModel.setTenantId(tenantId);

try (InputStream inputStream = new ByteArrayInputStream(keystoreContent)) {
KeyStore keyStore = KeystoreUtils.getKeystoreInstance(type);
keyStore.load(inputStream, passwordChar);
String pvtKeyAlias = KeyStoreUtil.getPrivateKeyAlias(keyStore);
if (StringUtils.isNotBlank(pvtKeyAlias)) {
keyStoreModel.setPrivateKeyAlias(pvtKeyAlias);
byte[] publicCert = keyStore.getCertificate(pvtKeyAlias).getEncoded();

if (publicCert != null) {
keyStoreModel.setPublicCert(publicCert);
}
if (ArrayUtils.isNotEmpty(privateKeyPasswordChar)) {
// Check weather private key password is correct.
keyStore.getKey(pvtKeyAlias, privateKeyPasswordChar);
keyStoreModel.setEncryptedPrivateKeyPass(encryptPassword(privateKeyPasswordChar));
}
}
} catch (IOException | KeyStoreException | NoSuchAlgorithmException | NoSuchProviderException |
UnrecoverableKeyException | CertificateException e) {
throw new SecurityException("Error adding key store " + filename, e);
}

keyStoreModel.setEncryptedPassword(encryptPassword(passwordChar));
registryKeyStorePersistenceManager.addKeystore(keyStoreModel);
}

/**
Expand Down Expand Up @@ -274,7 +311,12 @@ public KeyStore getKeyStore(String keyStoreName) throws Exception {
public KeyStoreMetadata[] getKeyStoresMetadata(boolean isSuperTenant) throws SecurityException {

CarbonUtils.checkSecurity();
List<KeyStoreMetadata> metadataList = registryKeyStorePersistenceManager.listKeyStores(tenantId);
List<KeyStoreMetadata> metadataList = new ArrayList<>();
List<KeyStoreModel> keyStoreList = registryKeyStorePersistenceManager.listKeyStores(tenantId);
for (KeyStoreModel keyStoreModel : keyStoreList) {
metadataList.add(getKeyStoreMetaDataFromKeyStoreModel(keyStoreModel));
}

if (isSuperTenant) {
metadataList.add(getPrimaryKeyStoreMetadata());
}
Expand Down Expand Up @@ -383,11 +425,17 @@ public String getPassword(org.wso2.carbon.registry.core.Resource resource) throw
*
* @param keyStoreName key store name
* @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) {

return String.valueOf(registryKeyStorePersistenceManager.getKeyStorePassword(keyStoreName, tenantId));
return String.valueOf(getKeyStorePasswordAsCharArray(keyStoreName));
}

private char[] getKeyStorePasswordAsCharArray(String keyStoreName) {

String encryptedPassword =
registryKeyStorePersistenceManager.getEncryptedKeyStorePassword(keyStoreName, tenantId);
return decryptPassword(encryptedPassword);
}

/**
Expand Down Expand Up @@ -447,8 +495,22 @@ private void updateTrustStore(KeyStore keyStore) {

private void updateTenantKeyStore(String keyStoreName, KeyStore keyStore) {

registryKeyStorePersistenceManager.updateKeyStore(keyStoreName, keyStore, tenantId);
updateTenantKeyStoreCache(keyStoreName, new KeyStoreBean(keyStore, new Date()));
char[] passwordChar = new char[0];
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
KeyStoreModel keyStoreModel = new KeyStoreModel();
passwordChar = getKeyStorePasswordAsCharArray(keyStoreName);
keyStore.store(outputStream, passwordChar);
outputStream.flush();
keyStoreModel.setName(keyStoreName);
keyStoreModel.setContent(outputStream.toByteArray());
keyStoreModel.setTenantId(tenantId);
registryKeyStorePersistenceManager.updateKeyStore(keyStoreModel);
updateTenantKeyStoreCache(keyStoreName, new KeyStoreBean(keyStore, new Date()));
} catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException e) {
throw new SecurityException("Error updating tenanted key store: " + keyStoreName, e);
} finally {
clearPasswordCharArray(passwordChar);
}
}

/**
Expand Down Expand Up @@ -502,11 +564,25 @@ private KeyStore getTenantKeyStore(String keyStoreName) throws Exception {
return tenantKeyStores.get(keyStoreName).getKeyStore();
}

KeyStore keyStore = registryKeyStorePersistenceManager.getKeyStore(keyStoreName, tenantId);
KeyStoreBean keyStoreBean = new KeyStoreBean(keyStore,
registryKeyStorePersistenceManager.getKeyStoreLastModifiedDate(keyStoreName, tenantId));
updateTenantKeyStoreCache(keyStoreName, keyStoreBean);
return keyStore;
KeyStoreModel keyStoreModel = registryKeyStorePersistenceManager.getKeyStore(keyStoreName, tenantId);

char[] passwordChar = new char[0];
try {
byte[] bytes = keyStoreModel.getContent();
KeyStore keyStore = KeystoreUtils.getKeystoreInstance(keyStoreModel.getType());
passwordChar = decryptPassword(keyStoreModel.getEncryptedPassword());
ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
keyStore.load(stream, passwordChar);
KeyStoreBean keyStoreBean = new KeyStoreBean(keyStore,
registryKeyStorePersistenceManager.getKeyStoreLastModifiedDate(keyStoreName, tenantId));
updateTenantKeyStoreCache(keyStoreName, keyStoreBean);
return keyStore;
} catch (KeyStoreException | NoSuchProviderException | IOException | CertificateException |
NoSuchAlgorithmException e) {
throw new SecurityException("Error getting key store: " + keyStoreName, e);
} finally {
clearPasswordCharArray(passwordChar);
}
}

/**
Expand Down Expand Up @@ -688,7 +764,9 @@ private PrivateKey getTenantPrivateKey(String keyStoreName, String alias) throws
char[] privateKeyPasswordChar = new char[0];
try {
KeyStore keyStore = getTenantKeyStore(keyStoreName);
privateKeyPasswordChar = registryKeyStorePersistenceManager.getPrivateKeyPassword(keyStoreName, tenantId);
String encryptedPrivateKeyPassword =
registryKeyStorePersistenceManager.getEncryptedPrivateKeyPassword(keyStoreName, tenantId);
privateKeyPasswordChar = decryptPassword(encryptedPrivateKeyPassword);
return (PrivateKey) keyStore.getKey(alias, privateKeyPasswordChar);
} finally {
Arrays.fill(privateKeyPasswordChar, '\0');
Expand Down Expand Up @@ -845,4 +923,56 @@ public KeyStore loadKeyStoreFromFileSystem(String keyStorePath, String password,
}
}
}

private KeyStoreMetadata getKeyStoreMetaDataFromKeyStoreModel(KeyStoreModel keyStoreModel) {

KeyStoreMetadata metadata = new KeyStoreMetadata();
metadata.setKeyStoreName(keyStoreModel.getName());
metadata.setProvider(keyStoreModel.getProvider());
metadata.setKeyStoreType(keyStoreModel.getType());
metadata.setPrivateStore(keyStoreModel.getPrivateKeyAlias() != null);
String publicCertId = keyStoreModel.getPublicCertId();

if (StringUtils.isNotBlank(publicCertId)) {
metadata.setPublicCertId(keyStoreModel.getPublicCertId());
metadata.setPublicCert(keyStoreModel.getPublicCert());
}
return metadata;
}

/**
* This method encrypts the given passwordChar using the default crypto util.
*
* @param passwordChar Password to be encrypted as a character array.
* @return encrypted password.
*/
private String encryptPassword(char[] passwordChar) {

try {
return CryptoUtil.getDefaultCryptoUtil().encryptAndBase64Encode(String.valueOf(passwordChar).getBytes());
} catch (CryptoException e) {
throw new SecurityException("Error encrypting the passwordChar", e);
}
}

/**
* This method decrypts the given encrypted password using the default crypto util.
*
* @param encryptedPassword Encrypted password.
* @return decrypted password as a character array.
*/
private char[] decryptPassword(String encryptedPassword) {

try {
CryptoUtil cryptoUtil = CryptoUtil.getDefaultCryptoUtil();
return new String(cryptoUtil.base64DecodeAndDecrypt(encryptedPassword)).toCharArray();
} catch (CryptoException e) {
throw new SecurityException("Error decrypting the password", e);
}
}

private static void clearPasswordCharArray(char[] passwordChar) {

Arrays.fill(passwordChar, '\0');
}
}
1 change: 1 addition & 0 deletions core/org.wso2.carbon.utils/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@
<Export-Package>
!org.wso2.carbon.utils.resolver,
org.wso2.carbon,
org.wso2.carbon.keystore.persistence.*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets make only the interfaces as exposed, keeping the Registry impl internal.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will change this with the next PR when introducing KeyStorePersistenceManagerFactory

org.wso2.carbon.utils.*,
org.wso2.carbon.context,
!org.wso2.carbon.context.internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
* under the License.
*/

package org.wso2.carbon.core.keystore.persistence;
package org.wso2.carbon.keystore.persistence;

import org.wso2.carbon.core.security.KeyStoreMetadata;
import org.wso2.carbon.keystore.persistence.model.KeyStoreModel;

import java.security.KeyStore;
import java.util.Date;
import java.util.List;

Expand All @@ -32,26 +31,20 @@ public interface KeyStorePersistenceManager {
/**
* Add the key store to the data storage.
*
* @param keyStoreName Name of the key store.
* @param keystoreContent Content of the key store.
* @param provider Provider of the key store.
* @param keyStorePasswordChar Key Store Password as a character string.
* @param privateKeyPasswordChar Private key password as a character string.
* @param tenantId Tenant ID.
* @param keyStoreModel Key store model.
* @throws SecurityException If an error occurs while adding the key store.
*/
void addKeystore(String keyStoreName, byte[] keystoreContent, String provider, String type,
char[] keyStorePasswordChar, char[] privateKeyPasswordChar, int tenantId);
void addKeystore(KeyStoreModel keyStoreModel) throws SecurityException;

/**
* Get the key store from the data storage.
*
* @param keyStoreName Name of the key store.
* @param tenantId Tenant Id.
* @return Key store.
* @return Key store model.
* @throws SecurityException If an error occurs while getting the key store.
*/
KeyStore getKeyStore(String keyStoreName, int tenantId) throws SecurityException;
KeyStoreModel getKeyStore(String keyStoreName, int tenantId) throws SecurityException;

/**
* Method to retrieve list of keystore metadata of all the keystores in a tenant.
Expand All @@ -60,16 +53,14 @@ void addKeystore(String keyStoreName, byte[] keystoreContent, String provider, S
* @return List of KeyStoreMetaData objects.
* @throws SecurityException If an error occurs while retrieving the keystore data.
*/
List<KeyStoreMetadata> listKeyStores(int tenantId) throws SecurityException;
List<KeyStoreModel> listKeyStores(int tenantId) throws SecurityException;

/**
* Update the key store in the data storage.
*
* @param keyStoreName Key store name.
* @param keyStore Updated key store.
* @param tenantId Tenant Id.
* @param keyStoreModel Key store model.
*/
void updateKeyStore(String keyStoreName, KeyStore keyStore, int tenantId);
void updateKeyStore(KeyStoreModel keyStoreModel);

/**
* Delete the key store from the data storage.
Expand All @@ -96,7 +87,7 @@ void addKeystore(String keyStoreName, byte[] keystoreContent, String provider, S
* @return KeyStore Password as a character array.
* @throws SecurityException If there is an error while getting the key store password.
*/
char[] getKeyStorePassword(String keyStoreName, int tenantId) throws SecurityException;
String getEncryptedKeyStorePassword(String keyStoreName, int tenantId) throws SecurityException;

/**
* Get the private key password as a character array for the given key store name.
Expand All @@ -106,5 +97,5 @@ void addKeystore(String keyStoreName, byte[] keystoreContent, String provider, S
* @return Private key password as a character array.
* @throws SecurityException If an error occurs while getting the private key password.
*/
char[] getPrivateKeyPassword(String keyStoreName, int tenantId) throws SecurityException;
String getEncryptedPrivateKeyPassword(String keyStoreName, int tenantId) throws SecurityException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://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.keystore.persistence;

/**
* Constants used in the persistence manager.
*/
public class PersistenceManagerConstants {

private PersistenceManagerConstants() {

}

public static class RegistryResources {

private RegistryResources() {

}

public static final String ROOT = "/repository/";
public static final String KEY_STORES = ROOT + "security/key-stores";
public static final String PRIMARY_KEYSTORE_PHANTOM_RESOURCE = RegistryResources.ROOT +
"security/key-stores/carbon-primary-ks";
public static final String PROP_PASSWORD = "password";
public static final String PROP_PROVIDER = "provider";
public static final String PROP_PRIVATE_KEY_ALIAS = "privatekeyAlias";
public static final String PROP_TYPE = "type";
public static final String PROP_PRIVATE_KEY_PASS = "privatekeyPass";
public static final String TENANT_PUBKEY_RESOURCE = ROOT + "security/pub-key";
}

}
Loading