Thursday, January 30, 2014

Secure custom properties file using secure vault.






The WSO2 secure vault is an implementation of synapse secure vault (modified version), it  is used to secure plain text password which is contains in configuration  files of WSO2 products.  Please go through the below link to get more information[1].

[1]http://docs.wso2.org/display/Carbon420/WSO2+Carbon+Secure+Vault

Here I'm going to show you, how can we secure the password in configuration property  file. This is very useful when we write custom extensions for WSO2 products.

Lets assume that our new custom module has flowing properties  file(myconf-test.properties) and need to secure the plain text password using the WSO2 secure vault.

myconf.module.serverUrl=http://localhost:9443/servies
myconf.module.remote=true
myconf.module.username=admin
myconf.module.password=admin

1.) Create file myconf-test.properties inside the [product_home]/repository/conf/ and add the above content.

2) If you haven't done before,  run the following command at least one time inside the
[product_home]/bin to generate the secret-conf.properties file with default values,  at the sametime cipher-text.properties and other WSO2 config files(according to the default settings in cipher-tool.properties)  also will be updated.  If you have already execute this command you can directly start with step 2.
sh ciphertool.sh -Dconfigure
i). Now you have to provide the answers for the following questions ?
[Please Enter Primary KeyStore Password of Carbon Server : ]

Answer: wso2carbon

ii) If you check the secret-conf.properties, you should see that file has updated with following settings.
   Note:~ Content of the file will be changed depending on the product.


keystore.identity.location=/home/ajith/wso2/product/wso2greg-4.6.0/repository/resources/security/wso2carbon.jks
keystore.identity.type=JKS
keystore.identity.store.password=identity.store.password
keystore.identity.store.secretProvider=org.wso2.carbon.securevault.DefaultSecretCallbackHandler
secretRepositories.file.provider=org.wso2.securevault.secret.repository.FileBaseSecretRepositoryProvider
secretRepositories.file.location=repository/conf/security/cipher-text.properties
secretRepositories=file
keystore.identity.key.password=identity.key.password
carbon.secretProvider=org.wso2.securevault.secret.handler.SecretManagerSecretCallbackHandler
keystore.identity.key.secretProvider=org.wso2.carbon.securevault.DefaultSecretCallbackHandler
keystore.identity.alias=wso2carbon


2.) Go to the [product_home]/bin and execute the following command to generate the encrypted value for the clear text  password.


sh ciphertool.sh 

i) It will prompt following  console for input value.  Answer: wso2carbon


[Please Enter Primary KeyStore Password of Carbon Server : ]

ii)Then it will appear second console for  following input value.
     (Answer: According to our property file, the plain text password is "admin".)

[Enter Plain text value :]
[Please Enter value Again :]

iii) Now you should see the following output on the console.

Encryption is done Successfully


Encrypted value is : 
MpfXhKP+iJSImA/KNa+DoOXCPQAyF3JLhlFNAdG6F3naWK+N1/WEWOJkFx4kK34i1VtkywNN9SiC
MRQGTFw+nqzK5/INgcFFdoxv49lM/FJw8CyXKQ0JWdxw5QJPtrjJzvGp6Rj6xt4ysb6HdG5uNG+a
1E0lqdmzGUYQ6oejIlk=

3). Open the cipher-text.properties file, which is under [product_home]/repository/conf/security and add the following entry.

myconf.module.password=MpfXhKP+iJSImA/KNa+DoOXCPQAyF3JLhlFNAdG6F3naWK+N1/WEWOJkFx4kK34i1VtkywNN9SiC
MRQGTFw+nqzK5/INgcFFdoxv49lM/FJw8CyXKQ0JWdxw5QJPtrjJzvGp6Rj6xt4ysb6HdG5uNG+a
1E0lqdmzGUYQ6oejIlk=

4). Open the myconf-test.properties file and update the key/value of password field.

myconf.module.serverUrl=http://localhost:9443/servies
myconf.module.remote=true
myconf.module.username=admin
myconf.module.password=secretAlias:myconf.module.password

5). You can use the following Java class to resolve the the password at your custom extension code.

(  Note: If you open from IDE , please point the [product_home]/repository/components/plugins
             to class path.)

ModuleProperties.java

/*
*  Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
*  WSO2 Inc. 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.sampl.rxt.populator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.governance.generic.stub.beans.xsd.ContentArtifactsBean;
import org.wso2.carbon.governance.generic.ui.clients.ManageGenericArtifactServiceClient;
import org.wso2.carbon.governance.generic.ui.utils.DropDownDataPopulator;

import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;

public class WSDLPopulator implements DropDownDataPopulator {

    private static final Log log = LogFactory.getLog(WSDLPopulator.class);
    String[] out = new String[1];

    @Override
    public String[] getList(HttpServletRequest httpServletRequest, ServletConfig servletConfig) {

        try {
            ManageGenericArtifactServiceClient client =
                    new ManageGenericArtifactServiceClient(servletConfig, httpServletRequest.getSession());

            ContentArtifactsBean artifactsBean = client.listContentArtifacts("application/wsdl+xml");
            if (artifactsBean.getName() != null && artifactsBean.getName().length > 0) {
                return artifactsBean.getName();
            } else {
                out[0] = "None";
            }

        } catch (Exception e) {
            log.error("An error occurred while obtaining the WSDL list", e);
        }
        return out;
    }
}

6). You can checkout the complete sample from the following SVN location[1].

 [i].Open the build.xml and change the value of the "product.home" property to your  product home directory.

Execute the ant command inside the project home (SecureVaultSample) to build the jar file.

[i]https://svn.wso2.org/repos/wso2/people/ajith/blog/SecureVaultSample/

7). Put the jar file which is under target to [product_home]/repository/components/lib.

8). Start the server . (sh wso2server.sh)

9).  When starting the server, it will prompt the following command input to enter the key store password. The answer is "wso2carbon". Then  server will start successfully.


[Enter KeyStore and Private Key Password :]

10). If you planing to  start the server as  background process , then you need to do the following steps before start the server.

i. Create a file named "password-tmp.txt" in <PRODUCT_HOME>/ directory. Add "wso2carbon" (the primary keystore password) to this file and save. By default, the password provider assumes that both private key and keystore passwords are the same. If not, the private key password must be entered in the second line of the file.

ii. Keystore password will be picked up from the "password-tmp.txt" file. Once the server starts, this file is automatically deleted from the file system. Make sure to add this temporary file back whenever you start the sever as a background process. If you name of the password file "password-persist.txt" instead of "password-tmp.txt", then the file will not be deleted after the server starts. Therefore, it will not be required to provide the password in subsequent startups.

11. Finally, if you initialize the ModuleProperties class in your custom extension code (new ModuleProperties() ) you should be able to get the resolved password from that Object.

The summary of the process showing in following diagram.



Wednesday, January 29, 2014

Secure custom configuration file(XML) using WSO2 secure vault.


The WSO2 secure vault is an implementation of synapse secure vault (modified version), it  is used to secure plain text password which is contains in configuration  files of WSO2 products.  Please go through the below link to get more information[1].

[1]http://docs.wso2.org/display/Carbon420/WSO2+Carbon+Secure+Vault

Here I'm going to show you, how can we secure the plain text password which contains in our own configuration file(XML type). This is very useful when we write custom extensions for WSO2 products.

Lets assume that our new custom module has flowing configuration file(myconf-test.xml) and need to secured the plain text password using the WSO2 secure vault.

<myconf>
   <module serverURL="local://services/" remote="false">
       <username>admin</username>
       <password>admin</password>
   </module>
 </myconf>

1). Open the cipher-tool.properties file which is under <product_home>/repository/conf/security and add the following entry.

myconf.module.password=myconf-test.xml//myconf/module/password,true

2). Open the cipher-text.properties which is under <product_home>/repository/conf/security
and add the following entry.

myconf.module.password=[admin]

3). Go to the <product_home>/bin and execute the following command.

sh ciphertool.sh  -Dconfigure

4).  Now you have to provide the answers for the following questions ?

[Please Enter Primary KeyStore Password of Carbon Server : ]

 Answer is : wso2carbon

5). After enter the keystore  password , you should see the success message in the console output.

Protected Token [myconf.module.password] is updated in myconf-test.xml successfully

6). If you look at the myconf-test.xml again, you should see the password element has been modified as bellow.

<password svns:secretAlias="myconf.module.password">password</password>

7). If you look at the cipher-text.properties file again, you should see the following like entry has been update.

myconf.module.password=Tihb7iTeIrcfWXI6i0m+tMw7OUZKFbSSfl+ngNVS8usB0Q2igFZfBAEFvlq4vPJhZc+b59kuVFBl\nwmfVEWHUDmw3+Nz1JJJl1yds8RA5MV+YrMTuQpl0az2/suW7qJnLvO/fMDTPICQBF8TMxdr3KG5G\nsBym+1MW4B3zuwEtR5g\=

7). Following java class  used to read the configuration file from the distribution and resolved the
     secured password.

    (  Note: If you open from IDE , please point the <product_home>/repository/components/plugins
       to class path.)

ModuleConfig.java


/*
 * Copyright (c) 2006, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * Licensed 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.sample.securevault;


import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.utils.CarbonUtils;
import org.wso2.securevault.SecretResolver;
import org.wso2.securevault.SecretResolverFactory;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;


// Following sample contains the password element, that is one going to be secure using WSO2 secure vault.

/**
 * 
 * 
 * admin
 * admin
 * 
 * 
 */
public class ModuleConf {

    private static final Log log = LogFactory.getLog(ModuleConf.class);

    private String username;
    private String password;
    private String serverURL;
    private String remote;

    public ModuleConf() {
        FileInputStream fileInputStream = null;
        //Assumed that configuration file is under the /repository/conf
        String configPath = CarbonUtils.getCarbonHome() + File.separator + "repository" + File.separator + "conf" +
                File.separator + "myconf-test.xml";

        File registryXML = new File(configPath);
        if (registryXML.exists()) {
            try {
                fileInputStream = new FileInputStream(registryXML);

                StAXOMBuilder builder = new StAXOMBuilder(fileInputStream);
                OMElement configElement = builder.getDocumentElement();
                //Initialize the SecretResolver providing the configuration element.
                SecretResolver secretResolver = SecretResolverFactory.create(configElement, false);
                OMElement module = configElement.getFirstChildWithName(new QName("module"));

                if (module != null) {
                    username = module.getFirstChildWithName(new QName("username")).getText();

                    //same entry used in cipher-text.properties and cipher-tool.properties.
                    String secretAlias = "myconf.module.password";

                    //Resolved the secret password.
                    if (secretResolver != null && secretResolver.isInitialized()) {
                        if (secretResolver.isTokenProtected(secretAlias)) {
                            password = secretResolver.resolve(secretAlias);
                        } else {
                            password = module.getFirstChildWithName(new QName("password")).getText();
                        }
                    }
                    serverURL = module.getAttributeValue(new QName("serverURL"));
                    remote = module.getAttributeValue(new QName("remote"));
                }
            } catch (XMLStreamException e) {
                log.error("Unable to parse myconf-test.xml", e);
            } catch (IOException e) {
                log.error("Unable to read myconf-test.xml", e);
            } finally {
                if (fileInputStream != null) {
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                        log.error("Failed to close the FileInputStream, file : " + configPath);
                    }
                }
            }
        }
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public String getServerURL() {
        return serverURL;
    }

    public boolean isRemote() {
        return Boolean.valueOf(remote);
    }
}


8). You can checkout complete sample project(SecureVaultSample) from the following SVN location.

 [i]. After that open the build.xml and change the value of the "product.home" property to your  product home directory.

Execute the ant command inside the project home to build the jar file.

[i]https://svn.wso2.org/repos/wso2/people/ajith/blog/SecureVaultSample/

9). Put the jar file which is under target to <product_home>/repository/components/lib.

10). Start the server . (sh wso2server.sh)

11).  When starting the server, it will prompt the following command input to enter the key store password. The answer is "wso2carbon". Then  server will start successfully.


[Enter KeyStore and Private Key Password :]

12). If you planing to  start the server as  background process , then you need to do the following steps before start the server.

i. Create a file named "password-tmp.txt" in <PRODUCT_HOME>/ directory. Add "wso2carbon" (the primary keystore password) to this file and save. By default, the password provider assumes that both private key and keystore passwords are the same. If not, the private key password must be entered in the second line of the file.

ii. Keystore password will be picked up from the "password-tmp.txt" file. Once the server starts, this file is automatically deleted from the file system. Make sure to add this temporary file back whenever you start the sever as a background process. If you name of the password file "password-persist.txt" instead of "password-tmp.txt", then the file will not be deleted after the server starts. Therefore, it will not be required to provide the password in subsequent startups.

13. Finally, if you initialize the ModuleConf class in your custom extension code (new ModuleConf() ) you should be able to get the resolved password from that Object.

Note:

The SecretCallbackHandler is defined in the secret-conf.properties file inside the <product_home>/repository/conf/security.

 The default implementation is set to org.wso2.carbon.securevault.DefaultSecretCallbackHandler.
 You can find the source in the SVN location[2]

[2]https://svn.wso2.org/repos/wso2/carbon/kernel/branches/4.2.0/core/org.wso2.carbon.securevault/4.2.0/src/main/java/org/wso2/carbon/securevault/DefaultSecretCallbackHandler.java

If you want , you can write your own implementation as well, for that you should write your own implementation extending the AbstractSecretCallbackHandler.

Eg: MyConfSecretCallbackHandler.java 


* Copyright (c) 2006, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * Licensed 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.sample.securevault;

import org.wso2.securevault.secret.AbstractSecretCallbackHandler;
import org.wso2.securevault.secret.SingleSecretCallback;


public class MyConfSecretCallbackHandler extends AbstractSecretCallbackHandler {
    @Override
    protected void handleSingleSecretCallback(SingleSecretCallback singleSecretCallback) {
        singleSecretCallback.setSecret("wso2carbon");
    }
}

Build the jar which includes  your own SecretCallbackHandler and add the fully qualified class name in secret-conf.properties instead of org.wso2.carbon.securevault.DefaultSecretCallbackHandler.


The summary of the process showing in following diagram.