[ Documentation Index ]

WSO2 Enterprise Service Bus (ESB), Securing Confidential Information

This guide describes how confidential information can be handled in a secured manner in the ESB environment. From the context of the ESB confidential information refers mainly to passwords. But confidential information could be anything and hence instructions given in this guide applies to all such confidential information elements.

Secret Providers

The word 'secret' is used to refer to anything which needs to be kept as a secret. Eventhough the current usage is limited to passwords, API supports any type of data. Secret retrieving is done through 'SecretCallbackHandler' implementations. Currently there are a few implementations and more implementations can be added as needed.

  • JBossEncryptionSecretCallbackHandler

This is the default implementation used within the JBoss Application Server to secure database passwords. It uses a hardcoded pass phrase (Need to use same pass phrase that have been used for encryption). Admin can encrypt passwords using a pass phrase and hard code that in the 'JBossEncryptionSecretCallbackHandler' and make the source code closed.

  • JlineSecretCallbackHandler

This is a command line based approach. Jline provides a way of reading passwords without echoing text - similar to jdk 6 scanners.

  • JMXSecretCallbackHandler

This is only supported in Synapse. With this we must use a JMX Console to provide secrets. This may be useful for any users that manage servers in a production environment using JMX.

  • HardCodedSecretCallbackHandler

Admin needs to hard code password and make source code closed. This can be used to just provide keystore passwords and other secrets can be retrieved using 'SecretManagerSecretCallbackHandler' (described next)

  • SecretManagerSecretCallbackHandler

All secrets are managed using the Secret Manager. Secret Manager can keep any number of secret repositories. Those are arranged in a cascading manner. All secrets are stored in one place - secret repositories. Secrets can be accessed by providing aliases for them (no need to put encrypted values inline in application configurations). Key stores required by the Secret Manager and secret repositories are configurable. It can be done through the 'secret-conf.properties'. Currently, there is only one secret repository and it is called the FileBaseSecretRepository. It uses cipher-text.properties to keep secrets. It keeps aliases vs. its actual secret in encrypted format (encrypted by a key in keystore)

Example

secret-conf.properties

############################################################################
# Secret Securing Configuration
############################################################################
# The following property specifies a global password provider implementation
# which will be used globally if not overriden in specific configurations
#carbon.secretProvider=<any implementation of org.apache.synapse.commons.security.secret.SecretCallbackHandler>
#Examples:
#carbon.secretProvider=org.apache.synapse.commons.security.secret.handler.SecretManagerSecretCallbackHandler
#carbon.secretProvider=org.apache.synapse.commons.security.secret.handler.JMXSecretCallbackHandler
#carbon.secretProvider=org.apache.synapse.commons.security.secret.handler.JlineSecretCallbackHandler
#carbon.secretProvider=org.apache.synapse.commons.security.secret.handler.JBossEncryptionSecretCallbackHandler

#Secret Repositories configuration

secretRepositories=file
secretRepositories.file.provider=org.apache.synapse.commons.security.secret.repository.filebased.FileBaseSecretRepositoryProvider
secretRepositories.file.location=cipher-text.properties

#KeyStores configurations

keystore.identity.location=resources/security/wso2carbon.jks
keystore.identity.type=JKS
keystore.identity.alias=wso2carbon
keystore.identity.store.password=wso2carbon
#keystore.identity.store.secretProvider=<any implementation of org.apache.synapse.commons.security.secret.SecretCallbackHandler>
keystore.identity.key.password=wso2carbon
#keystore.identity.key.secretProvider=<any implementation of org.apache.synapse.commons.security.secret.SecretCallbackHandler>
#keystore.identity.parameters=enableHostnameVerifier=false;keyStoreCertificateFilePath=/home/esb.cer

keystore.trust.location=resources/security/client-truststore.jks
keystore.trust.type=JKS
keystore.trust.alias=wso2carbon
keystore.trust.store.password=wso2carbon
#keystore.trust.store.secretProvider=<any implementation of org.apache.synapse.commons.security.secret.SecretCallbackHandler>

cipher-text.properties

aliases=esb

# configuration  per each plaintext
esb.secret=M6U74dMVvRm4XFMczki2qZ6CsTvnUuRTjSditlACR5vTISSMI7F/mCTVJGOGdKJjij+VWVhBtmAOkElyvR9TwlUECnZ1o5DNsTK6l8je+9amc/ziTQLP3Q1tzm/Ex1pzHsG6jPGGrv3O0B9pZTfYFqRvlcNhM7Ve3WvA3ibs4Yk=
esb.secret.alias=wso2carbon
esb.secret.keystore=identity

For security operations (encrypt / decrypt), Secret Manager uses a KeyStore and securing the keystore passwords can be done by any of above mentioned ways. In the current implementation, there is only a one Secret Repository - A file based Secret Repository. It is possible to implements secret repositories based on database, directory servers, registry etc.

General API for Retrieving Secrets

Consider that an application has a configuration for passwords as

  <datasource>
      <password>pass</password>
      <secretProvider>any implementation of SecretCallbackHandler- class name</secretProvider>
  </datasource> 

For JBossEncryptionSecretCallbackHandler, 'password' should contain an encrypted value. In any other case, it is just an alias- can keep any human readable text. For example in Secret Manger, actual password is in secret repositories. For file based repository, it is something like bellow.

  
  aliases=pass
  pass.secret=EsY65tztE9R5b9pErVxLp8Br5d3ol6vRdWAkYHdc7XkZteGf37VJ+iNlCenqxYSEto0vcjpcmmzwf7K2wd9u3KQtVGKEoNLSe2LYZtrm3tKmGd6PX9YpdN72ml3JISNXPJ69yybFi6DVUIJfE5MFOd7gswWfCnkmZ3eJ6M1nuiI=

To retrieve secret, it is needed to use following API and follow steps.

Retrieving API

Read the configuration parameter value of 'secret Provider' - say 'secretProvider'

// Create SecretCallbackHandler
 SecretCallbackHandler secretCallbackHanlder SecretCallbackHandlerFactory.createSecretCallbackHandler(secretProvider);

// Setup SecretLoadingModule with 
CallbackHandlersSecretLoadingModule secretLoadingModule = new SecretLoadingModule();
secretLoadingModule.init(new SecretCallbackHandler[]{secretCallbackHanlder});

// Create SecretCallbacks
SingleSecretCallback secretCallback = new SingleSecretCallback(aliases); // for above example 'aliases' is 'pass'

secretLoadingModule.load(new SecretCallback[]{secretCallback});

String actualPassword= secretCallback.getSecret();

If you want to load multiple secrets at once you can use 'MultiSecretCallback'; or array of SingleSecretCallback s.

There is a simple way to avoid code duplication.

SecretInformation secretInformation = SecretInformationFactory.createSecretInformation(secretProvider, aliasPassword, passwordPrompt);

String actualPassword = secretInformation.getResolvedSecret();

Adapting in OSGI Environment

All 'SecretCallbackHandlers' other than the 'SecretManagerSecretCallbackHandler' are stateless enabling to be used without any additional code. With 'SecretManagerSecretCallbackHandler', it is best if there is only one 'SecretManager' instance. More precisely, a single 'SecretManagerSecretCallbackHandler' instance. For this, there is a carbon module called 'secretvault'. It just reads 'secret-conf.properties' and initiates a new 'SecretCallbackHandler' instance. This can be a 'SecretManagerSecretCallbackHandler' or any other 'SecretCallbackHandlers' instance that needs to be shared globally. To acccess this instance one must follow the instuctions given below.

Step 1 - Adding Dependencies

<dependency>
    <groupId>org.apache.synapse</groupId>
    <artifactId>synapse-commons</artifactId>
    <version>1.10.0-SNAPSHOT</version>
</dependency> 

<dependency>
     <groupId>org.wso2.carbon</groupId>
     <artifactId>org.wso2.carbon.securevault</artifactId>
     <version>${carbon.version}</version>
</dependency>

Setp 2 - Listening to SecretCallbackHandlerService

/** @scr.reference name="secret.callback.handler.service"
* interface="org.wso2.carbon.securevault.SecretCallbackHandlerService"
* cardinality="1..1" policy="dynamic"
* bind="setSecretCallbackHandlerService" unbind="unsetSecretCallbackHandlerService
*/

protected void setSecretCallbackHandlerService( SecretCallbackHandlerService secretCallbackHandlerService) {
            Logic on Set
}

protected void unsetSecretCallbackHandlerService(SecretCallbackHandlerService secretCallbackHandlerService) {
           Logic on Unset
}

The annotations at the top are required and the Maven SCR plugin must be used to build the code. Please refer Maven 2 documentation on how to use the Maven SCR plugin. SCR plugin generates OSGi declarative service descriptors by parsing the scr annotations stated in the source files.

Logic on Set

If the secret that needs to be resolved can be accessed directlym, for example passing as an argument, you can use the following logic.

  SecretCallbackHandler secretProvider = secretCallbackHandlerService.getSecretCallbackHandler());
  SecretInformation secretInformation = SecretInformationFactory.createSecretInformation(secretProvider, aliasPassword, passwordPrompt);
  String actualPassword = secretInformation.getResolvedSecret();

Now you can pass either an actual password or a SecretProvider or SecretInformation to the code location that uses the password. But if the code that performs secret resolution can not be accessed, for example, when using a configuration such as follows - you cannot pass resolved password directly.

  <datasource>
      <password>pass</password>
      <secretProvider>any implementation of SecretCallbackHandler- class name</secretProvider>
  </datasource>

You have to do following if the scenario is similar to the above.

  if (secretCallbackHandlerService != null) {
      SecretCallbackHandler secretCallbackHandler = secretCallbackHandlerService.getSecretCallbackHandler();
      SharedSecretCallbackHandlerCache.getInstance().setSecretCallbackHandler(secretCallbackHandler);
  }

Then you have to specify secretProvider in the configuration as org.apache.synapse.commons.security.secret.handler.SharedSecretCallbackHandler. This is another SecretCallbackHandler that uses the handler set in the SharedSecretCallbackHandlerCache. You need to use Retrieving API. What we do here is using OSGI services in order to share across any component and use the Singleton pattern to share across classes in a single component and enable using the Retrieving API as it is.

Ciphertool

This is a simple tool to encrypt and decrypt simple texts.There are scripts named ciphertool.sh & ciphertool.bat available in the bin directory to launch the tool. The arguments accepted by this tool with their meanings are shown bellow.

  • keystore - If keys are in a store , its location
  • storepass - Password to access keyStore
  • keypass - To get private key
  • alias - Alias to identify key owner
  • storetype - Type of keyStore, Default is JKS
  • keyfile - If key is in a file
  • opmode - encrypt or decrypt , Default is encrypt
  • algorithm - encrypt or decrypt algorithm , Default is RSA
  • source - Either cipher or plain text as an in-lined form
  • outencode - Currently base64 and used for encoding the result
  • inencode - Currently base64 and used to decode input
  • trusted - Is KeyStore a trusted store? If this argument is provided, consider as a trusted store
  • passphrase - if a simple symmetric encryption using a pass phrase shall be used

An example

ciphertool.bat -source testpass -keystore resources/security/client-truststore.jks -storepass wso2carbon -alias wo2carbon -outencode base64 -trusted