Skip Headers
Oracle® Fusion Middleware Application Security Guide
11g Release 1 (11.1.1)

Part Number E10043-12
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
PDF · Mobi · ePub

24 Developing with the Credential Store Framework

This chapter describes how to work with the Credential Store Framework (CSF) APIs in the following sections:

24.1 About the Credential Store Framework API

A credential store is used for secure storage of credentials. The credential store framework (CSF) API is used to access and perform operations on the credential store.

The Credential Store Framework:

Critical (create, update, delete) functions provided by the CSF API include:

Operations on CredentialStore are secured by CredentialAccessPermission, which implements the fine-grained access control model utilized by CSF.

24.2 Overview of Application Development with CSF

Knowledge of the following areas is helpful in getting your applications to work with the credential store framework:

Subsequent sections provide details about each of these tasks.

24.3 Setting the Java Security Policy Permissions

The Oracle Platform Security Services policy provider is set when the server is started. When the provider is file-based, the policy data is stored in system-jazn-data.xml.

CSF supports securing credentials:

Notes:

  • To properly access the CSF APIs, you need to grant Java permissions in the policy store.

  • The code invoking CSF APIs needs code source permission. The permissions are typically for specific code jars and not for the complete application.

24.3.1 Guidelines for Granting Permissions

The Credential Store Framework relies on Java permissions to grant permissions to credential store objects.

It is highly recommended that only the requisite permissions be granted, and no more.

WARNING:

It is risky and inadvisable to grant unnecessary permissions, particularly permissions to all maps and/or keys.

24.3.2 Permissions Grant Example 1

Note:

In the examples, the application jar file name is AppName.jar.

The CredentialStore maintains mappings between map names and credential maps. Each map name is mapped to a CredentialMap, which is a secure map of keys to Credential objects.

This example grants permissions for a specific map name and a specific key name of that map.

<jazn-policy>
    <grant>
        <grantee>
            <principals>...</principals>
            <!-- This is the location of the jar -->
            <!-- as loaded with the run-time -->
            <codesource>
      <url>file:${oracle.deployed.app.dir}/<MyApp>${oracle.deployed.app.ext}</url>
            </codesource>
        </grantee>
        <permissions>
            <permission>
               <class>oracle.security.jps.service.credstore.
                      CredentialAccessPermission</class>
               <name>context=SYSTEM,mapName=myMap,keyName=myKey</name>
              <!-- All actions are granted -->
              <actions>*</actions>
            </permission>
        </permissions>
    </grant>
</jazn-policy>

where:

  • MapName is the name of the map (typically the name of the application) for which you want to grant these permissions (read, write, update, and delete permissions denoted by the wildcarded actions).

  • KeyName is the key name in use.

24.3.3 Permissions Grant Example 2

In this example permissions are granted for a specific map name and all its key names.

<jazn-policy>
    <grant>
        <grantee>
            <principals>...</principals>
            <codesource>
      <url>file:${oracle.deployed.app.dir}/<MyApp>${oracle.deployed.app.ext}</url>
            </codesource>
        </grantee>
        <permissions>
           <permission>
              <class>oracle.security.jps.service.credstore.
                     CredentialAccessPermission</class>
              <name>context=SYSTEM,mapName=myMap,keyName=*</name>
              <!-- Certain actions are explicitly specified -->
              <!-- Compare to wild-card grant in previous example -->
              <actions>read,write,update,delete</actions>
        </permission>
        </permissions>
    </grant>
</jazn-policy>

24.4 Guidelines for the Map Name

When the domain-level credential store is used, name conflicts can arise with the various map names in the store for different applications. To avoid this, each application must have a unique map name in the store.

To achieve this, it is recommended that the map name you use uniquely identify the application.

Within a given map name, an application can store multiple credentials each of which is identifiable by a key. The map name and the key together constitute a primary key within a given credential store.

If there is a requirement that an application use more than one map name, then uniqueness continues to be maintained.

For example, consider three applications:

For RCU, a map name of RCU is chosen and the keys for three credentials are (say) Key1, Key2, and Key3:

Note:

The map names and key names used here are arbitrary and chosen for illustration only. Your application can use altogether different map names and/or keynames.

MapName -> RCU, Key -> Key1 and Credential -> PasswordCredential1
MapName -> RCU, Key -> Key2 and Credential -> PasswordCredential2
MapName -> RCU, Key -> Key3 and Credential -> GenericCredential1

For Oracle WebCenter, the map name is Web and the key for a single credential is Key1:

MapName -> Web, Key -> Key1 and Credential -> PasswordCredential3

For Fusion Middleware Control, the map name is denoted by EM and the keys for two credentials are Key1 and Key2 respectively:

MapName -> EM, Key -> Key1 and Credential -> PasswordCredential4
MapName -> EM, Key -> Key2 and Credential -> GenericCredential2

Note that the map name and key name are just two arbitrary strings and can have any valid string values in practice. However, implementing this way makes map names easier to manage.

24.5 Configuring the Credential Store

The administrator needs to define the credential store instance in a configuration file which contains information about the location of the credential store and the provider classes. Configuration files are located in:

$DOMAIN_HOME/config/fmwconfig

and are named as follows:

For details, see Chapter 10, "Managing the Credential Store".

24.6 Steps for Using the API

You can use the credential store framework within Oracle WebLogic Server or in a standalone environment.

24.6.1 Using the CSF API in a Standalone Environment

The steps for using the API in a standalone environment are:

  1. Set up the classpath. Ensure that the jps-manifest.jar file is in your classpath. For details, see Required JAR in Classpath in Section 1.5.3, "Scenario 3: Securing a Java SE Application".

  2. Set up the policy; to provide access to the CSF APIs, you need to configure the access permissions in the reference policy store. For examples, see Section 24.3, "Setting the Java Security Policy Permissions".

  3. Run the application.

Command-line options include:

  • -Doracle.security.jps.config
    

    specifies the full path to the configuration file

  • -Djava.security.policy
    

    specifies the location of the OPSS/Oracle WebLogic Server policy file

  • -Djava.security.debug=all
    

    is helpful for debugging purposes

24.6.2 Using the CSF API in Oracle WebLogic Server

The steps for using the API in an Oracle WebLogic Server environment are:

  1. The credential store service provider section of the jps-config.xml file is configured out-of-the-box in the following directory:

    $DOMAIN_HOME/config/fmwconfig
    

    If needed, reassociate to an LDAP credential store.

  2. Set up the policy; to provide access to the CSF APIs, you need to configure the access permissions in the reference policy store. For examples, see Section 24.3, "Setting the Java Security Policy Permissions".

  3. Start Oracle WebLogic Server.

  4. Deploy and test the application.

24.7 Examples

This section provides several examples of using the credential store framework APIs. It shows:

In each example, the test code is set up to show how the credential store operations are affected by the permissions. For each example the policy file, the test code, and the configuration file are provided to demonstrate how the provider information must be specified, and to enable you to compare the defined permissions on the map/key with the operation attempted in the code.

The section is structured as follows:

24.7.1 Code for CSF Operations

The following common "utility" program performs the CSF API operations. It is called by the example programs.

package demo.util;
 
import java.security.AccessController;
import java.security.PrivilegedAction;
 
import oracle.security.jps.JpsException;
import oracle.security.jps.service.credstore.Credential;
import oracle.security.jps.service.credstore.CredentialAlreadyExistsException;
import oracle.security.jps.service.credstore.CredentialFactory;
import oracle.security.jps.service.credstore.CredentialStore;
import oracle.security.jps.service.credstore.PasswordCredential;
 
public class CsfUtil {
    final CredentialStore store;
    public CsfUtil(CredentialStore store) {
        super();
        this.store = store;
    }
    
    private void doOperation() {
        try {
            PasswordCredential pc = null;
            try {
                // this call requires read privilege
                pc = (PasswordCredential)store.getCredential("pc_map", "pc_key");
                if (pc == null) {
                    // key not found, create one
                    pc = CredentialFactory.newPasswordCredential("jdoe",
                          "password".toCharArray());
                    // this call requires write privilege
                    store.setCredential("pc_map", "pc_key", pc);
                    System.out.print("Created ");
                }
                else {
                    System.out.print("Found ");
                }
 
                System.out.println("password credential: Name=" + pc.getName() +
                                   ",Password=" +
                                   new String(pc.getPassword()));
 
            } catch (CredentialAlreadyExistsException e) {
                // ignore since credential already exists.
                System.out.println("Credential already exists for 
                <pc_map, pc_key>: " + pc.getName() + ":" +
                new String(pc.getPassword()));
            }
 
            try {
                // permission corresponding to 
                // "context=SYSTEM,mapName=gc_map,keyName=gc_key"
                byte[] secret =
                    new byte[] { 0x7e, 0x7f, 0x3d, 0x4f, 0x10,
                                 0x20, 0x30 };
                Credential gc =
                    CredentialFactory.newGenericCredential(secret);
                store.setCredential("gc_map", "gc_key", gc);
                System.out.println("Created generic credential");
            } catch (CredentialAlreadyExistsException e) {
                // ignore since credential already exists.
                System.out.println("Generic credential already exists 
                  for <gc_map,gc_key>");
            }
 
            try {
                //no permission for pc_map2 & pc_key2 to perform 
                //operation on store
                Credential pc2 =
                    CredentialFactory.newPasswordCredential("pc_jode2",
                    "pc_password".toCharArray());
                store.setCredential("pc_map2", "pc_key2", pc2);
 
            } catch (Exception expected) {
                //CredentialAccess Exception expected here. Not enough permission
                System.out.println("This is expected :" +
                                   expected.getLocalizedMessage());
            }
 
        } catch (JpsException e) {
            e.printStackTrace();
        }
        
    }
    
    /*
     * This method performs a non-privileged operation. Either all code 
     * in the call stack must have CredentialAccessPermission  
     * OR
     * the caller must have the CredentialAccessPermission only and 
     * invoke this operation in doPrivileged block
     */
    public void doCredOperation() {
        doOperation();
    }
    
    /*
     * Since this method performs a privileged operation, only current class or
     * jar containing this class needs CredentialAccessPermission
     */
    public void doPrivilegedCredOperation() {
        AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    doOperation();
                    return "done";
                }
            });
    }
}

24.7.2 Example 1: Java SE Application with Wallet Store

This example shows a sample Java SE application using wallet credentials, that is, a file-based provider.

The example illustrates:

  • how the permissions are set in an xml-based policy store (jazn-data-xml)

  • how the configuration file is set up

  • the Java SE code

jazn-data.xml File

For illustration, the example uses an xml-based policy store file which has the appropriate permissions needed to access the given credential from the store. The file defines the permissions for different combinations of map name (alias) and key. Other combinations, or attempts to access the store beyond the permissions defined here, will be disallowed.

Note:

The default policy store to which this grant is added is $DOMAIN_HOME/config/fmwconfig/system-jazn-data.xml.

Here the system property projectsrc.home is set to point to the directory containing the Java SE application, and clientApp.jar is the application jar file which is present in sub-directory dist.

The corresponding policy grant looks like this:

<grant>
   <grantee>
      <codesource>
         <url>file:${projectsrc.home}/dist/clientApp.jar</url>
      </codesource>
   </grantee>
   <permissions>
      <permission>
         <class>oracle.security.jps.service.credstore.CredentialAccessPermission
         </class>
         <name>context=SYSTEM,mapName=pc_map,keyName=*</name>
         <actions>read,write</actions>
      </permission>
      <permission>
         <class>oracle.security.jps.service.credstore.CredentialAccessPermission
         </class>
         <name>context=SYSTEM,mapName=gc_map,keyName=gc_key</name>
         <actions>write</actions>
      </permission>                          
   </permissions>
</grant>

Note that no permission has been granted to mapName=pc_map2,keyName=pc_key2, hence the setCredential call for this map and key combination in Section 24.7.1, "Code for CSF Operations" is expected to fail.

jps-config-jse.xml File

Note:

For the complete configuration file see the default file shipped with the distribution at $DOMAIN_HOME/config/fmwconfig/jps-config-jse.xml.

The location property of the credential store service shows the directory containing the wallet file:

<jpsConfig>
 ...
    <serviceInstances>
        <serviceInstance name="credstore_file_instance"
                         provider="credstore_file_provider">
            <property name="location" value="store" />
        </serviceInstance>
    </serviceInstances>
 ...
</jpsConfig>

Note:

The default value of location is "./", that is, the current directory relative to the location of jps-config-jse.xml. To use a different path, be sure to specify the full path.

The wallet name is always cwallet.sso which is the default file-based Oracle wallet.

Java Code

Here is the Java SE code that calls the utility program.

package demo;
 
import java.io.ByteArrayInputStream;
 
import java.security.AccessController;
import java.security.PrivilegedAction;
 
import oracle.security.jps.JpsContext;
import oracle.security.jps.JpsContextFactory;
import oracle.security.jps.JpsException;
import oracle.security.jps.internal.policystore.JavaPolicyProvider;
import oracle.security.jps.jaas.JavaPolicy;
import oracle.security.jps.service.credstore.Credential;
import oracle.security.jps.service.credstore.CredentialAlreadyExistsException;
import oracle.security.jps.service.credstore.CredentialFactory;
import oracle.security.jps.service.credstore.CredentialStore;
import oracle.security.jps.service.credstore.PasswordCredential;
import oracle.security.jps.service.policystore.PolicyStore;
import oracle.security.jps.service.policystore.PolicyStoreException;
 
import demo.util.CsfUtil;
 
 
public class CsfApp {
 
 // set the OPSS policy provider explicitly, as required in a Java SE application
    static {
        java.security.Policy.setPolicy(new oracle.security.jps.internal.policystore.JavaProvider());
    }
 
    public CsfApp() {
        super();
    }
 
    public static void main(String[] a) {
        // perform operation as privileged code
        JpsContextFactory ctxFactory;
        try {
            ctxFactory = JpsContextFactory.getContextFactory();
            JpsContext ctx = ctxFactory.getContext();
 
            CredentialStore store =
                ctx.getServiceInstance(CredentialStore.class);
            CsfUtil csf = new CsfUtil(store);
            // #1 - this call is in a doPrivileged block
            // #1 - this should succeed.
            csf.doPrivilegedCredOperation();
 
            // #2 - this will also pass since granted all application
            // code necessary permission
            // NOTE: Since this call is not in a doPrivileged block,
            // this call would have failed if CredentialAccessPermission 
            // wasn't granted to this class.
            /*
            csf.doCredOperation();
            */
        } catch (JpsException e) {
            e.printStackTrace();
        }
 
    }
}

Notes:

  • It is not necessary to replace the JDK-wide policy object. Since the example grant shown conforms to the OPSS XML policy store, it is reasonable to set the policy provider to the OPSS provider.

  • In a Java EE environment for a JRF install for a supported application server, the OPSS policy provider will have been initialized.

24.7.3 Example 2: Java EE Application with Wallet Store

This example shows a sample Java EE application using wallet credentials. A simple servlet calls the CSF API.

The jazn-data.xml File

The jazn-data.xml file for this example defines the appropriate permissions needed to access the given credential from the store. The file defines both the codesource permissions and the permissions for different combinations of map name (alias) and key. Other combinations, or attempts to access the store beyond the permissions defined here, will be disallowed.

A fragment of the policy file showing the corresponding policy grant looks like this:

<grant>
   <grantee>
      <codesource>
      <url>file:${oracle.deployed.app.dir}/<MyApp>${oracle.deployed.app.ext}</url>
      </codesource>
   </grantee>
   <permissions>
      <permission>
         <class>oracle.security.jps.service.credstore.CredentialAccessPermission
         </class>
         <name>context=SYSTEM,mapName=pc_map,keyName=*</name>
         <actions>read,write</actions>
      </permission>
      <permission>
         <class>oracle.security.jps.service.credstore.CredentialAccessPermission
         </class>
         <name>context=SYSTEM,mapName=gc_map,keyName=gc_key</name>
         <actions>write</actions>
      </permission>                 
   </permissions>
</grant> 

Note that the first map and key permissions enable both read and write operations; the second enable write operations but not reads.

jps-config.xml File

A portion of the default configuration file jps-config.xml showing the credential store configuration is as follows:

<jpsConfig>
    <serviceProviders>
        <serviceProvider type="CREDENTIAL_STORE" name="credstoressp"
    class="oracle.security.jps.internal.credstore.ssp.SspCredentialStoreProvider">
            <description>SecretStore-based CSF provider</description>
        </serviceProvider>
    </serviceProviders>
 
    <serviceInstances>
        <serviceInstance name="credstore" provider="credstoressp">
            <property name="location" value="./" />
        </serviceInstance>
    </serviceInstances>
 
    <jpsContexts default="default">
        <jpsContext name="default">
        ...
            <serviceInstanceRef ref="credstore"/>
        ...
        </jpsContext>
    </jpsContexts>
</jpsConfig>

The location property specifies the wallet location; this specification is essentially the same as in Example 1, except that in this example the wallet is located inside the configuration directory. The wallet name is always cwallet.sso.

Java Code

package demo;
 
import demo.util.CsfUtil;
 
import java.io.IOException;
import java.io.PrintWriter;
 
import java.net.URL;
 
import java.util.Date;
 
import javax.servlet.*;
import javax.servlet.http.*;
 
import oracle.security.jps.JpsException;
import oracle.security.jps.service.JpsServiceLocator;
import oracle.security.jps.service.credstore.CredentialStore;
 
public class CsfDemoServlet extends HttpServlet {
    private static final String CONTENT_TYPE = "text/html; charset=windows-1252";
 
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }
 
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response) throws ServletException,
                                                           IOException {
        response.setContentType(CONTENT_TYPE);
        PrintWriter out = response.getWriter();
        //ServletOutputStream out = response.getOutputStream();
        try {
            response.setContentType("text/html");
            out.println("<html><body bgcolor=\"#FFFFFF\">");
            out.println("<b>Current Time: </b>" + new Date().toString() +
                        "<br><br>");
 
            //This is to get hold of app-level CSF service store
            //Outside app context, this call returns domain-level CSF store
            //This call also works in Java SE env
            final CredentialStore store =
              JpsServiceLocator.getServiceLocator().lookup(CredentialStore.class);
            CsfUtil csf = new CsfUtil(store);
            
            csf.doPrivilegedCredOperation();
            out.println("Credential operations completed using privileged code.");
        } catch (JpsException e) {
            e.printStackTrace(out);
        }
    }
}

The credential create operation is conducted using privileged code. The success of the operation can be verified by using the WLST listCred command:

listCred(map="pc_map", key="pc_key")

Note About Java SE Environment

In the Java SE environment, the following calls are equivalent:

CredentialStore store = JpsServiceLocator.getServiceLocator().lookup(CredentialStore.class);

and:

CredentialStore store = JpsContextFactory.getContextFactory().getContext().getServiceInstance(CredentialStore.class); 

The latter call is shown in Section 24.7.2, "Example 1: Java SE Application with Wallet Store".

24.7.4 Example 3: Java EE Application with LDAP Store

This example uses the same Java EE application used earlier in Example 2. The only difference is that the credential store is LDAP-based and not file (wallet) based.

You need to configure the following properties in the domain-level jps-config.xml file:

  • root name

    <property name="oracle.security.jps.ldap.root.name" value="cn=OracleJpsContainer"/>
    
  • farm name

    <property name="oracle.security.jps.farm.name" value="cn=OracleFarmContainer" />
    

The configuration of the LDAP store in jps-config.xml is as follows:

<jpsConfig>
    <serviceProviders>
        <serviceProvider name="credstore_ldap_provider"
 class="oracle.security.jps.internal.credstore.ldap.LdapCredentialStoreProvider">
            <description>Prototype LDAP-based CSF provider</description>
        </serviceProvider>
    </serviceProviders>
 
    <serviceInstances>
       <serviceInstance provider="ldap.credentialstore.provider" 
          name="credstore.ldap">
          <property value="bootstrap" 
             name="bootstrap.security.principal.key"/>
          <property value="cn=wls-jrfServer" 
             name="oracle.security.jps.farm.name"/>
          <property value="cn=jpsTestNode"
             name="oracle.security.jps.ldap.root.name"/>
          <property value="ldap://mynode.us.mycorp.com:1234" 
             name="ldap.url"/>
       </serviceInstance>
    </serviceInstances>
 
    <jpsContexts default="appdefault">
        <jpsContext name="appdefault">
            <serviceInstanceRef ref="credstore_ldap_instance"/>
        </jpsContext>
    </jpsContexts>
</jpsConfig>

The highlighted lines define the LDAP parameters necessary to locate the credentials.

24.8 Best Practices

In a clustered environment, use the Credential Store Mbean API over the Credential Store Framework API to create, retrieve, update, and delete credentials for an application.

If you are simply reading credentials, however, either API can be used.