18 Developing with the Credential Store Framework

This chapter explains how to use the Credential Store Framework in your applications and describes guidelines for the credential store configuration.

This chapter includes the following topics:

18.1 About the Credential Store Framework API

You use the CFS APIs to access, retrieve, and manage credentials kept in the credential store. This APIs allow you to:

  • Check whether a credential map or a map and key is stored in the credential store.

  • Obtain credentials associated within a map or a map and key.

  • Assign credentials to a a map or to a map and key.

  • Delete credentials within a map or a map and key.

Operations on the credential store are secured by the CredentialAccessPermission class, a class implementing the fine-grained control used by the credential framework.

18.2 Guidelines for Using the Credential Store Framework API

When you develope applications that use the Credential Store Framework API, make sure that you:

  • Provision security policies that enable applications access to credentials.

  • Determine appropriate map and key names to use, specially in environments where multiple applications use the same credential store.

  • Make sure that a credential store instance is defined and properly configured in the jps-config.xml file.

18.3 About Map and Key Names

Each application must have a unique map name associated with it in the credential store. This guarantees that no conflicts will arise between the various map and key names in the store, and that the map name identifies the application unambiguously. Within a given map, an application can store multiple keys each of which also has a unique name, so that the pair map name/key name identifies a single key in the credential store.

18.4 Provisioning Access Permissions

The credential framework secures access to maps, all keys within a map, and to specific keys within a map. To use the Credential Store Framework API you must specify access permissions that allow your application to use the API. Moreover, any code calling this API also requires a codesource permission, but these permissions are typically restricted to specific jars only. It is not recommended that you define access permissions to all maps and keys.

The following sections illustrate access permissions:

18.4.1 Permission to Access a Key Example

The following example shows an access permission to a source code to perform any action on a specific key within a map:

<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=myKey</name>
              <actions>*</actions>
            </permission>
        </permissions>
    </grant>
</jazn-policy>

18.4.2 Permission to Access a Map Example

The following example shows an access permission to a source code to perform specific actions to a map and all keys in that map:

<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>
              <actions>read,write,update,delete</actions>
        </permission>
        </permissions>
    </grant>
</jazn-policy>

18.5 Using the Credential Store Framework API

The following sections explain how to use this framework in Java SE and Java EE applications:

18.5.1 Using the Credential Store Framework API in Java SE Applications

To use the Credential Store Framework API in Java SE applications:

  1. Ensure that the jps-manifest.jar file is in your class path.

  2. Provide permissions to access Credential Store Framework APIs.

  3. Set Java Virtual Machine (JVM) options as appropriate. Options include the following:

    • -Doracle.security.jps.config
      

      specifies the full path to the jps-config-jse.xml file, if different from the default location ($DOMAIN_HOME/config/fmwconfig/jps-config-jse.xml).

    • -Djava.security.policy
      

      specifies the location of the weblogic.policy file, if different from the default location ($WL_HOME/server/lib).

    • -Dcommon.components.home
      

      specifies the location of the oracle_common directory under middleware home.

    • -Dopss.version
      

      specifies the version used in the environment.

18.5.2 Using the Credential Store Framework API in Java EE Applications

To use the Credential Store Framework API in a Java EE application, provide the access permissions necessary for your application to work before deploying it to Oracle WebLogic Server.

18.6 Credential Store Framework API Examples

The following examples illustrate how credential store operations use the required access permissions:

18.6.1 Credential Store Framework Operations Example

The following example illustrates Credential Store Framework API operations that are used in by other examples:

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 {
                                                    if (pc instanceof PasswordCredential){
                      System.out.print("Found ");
                    } else {
                      System.out.println("Unexpected credential type found");
                }
 
                System.out.println("password credential: Name=" + pc.getName() +
                                   ",Password=" +
                                   new String(pc.getPassword()));
 
            } catch (CredentialAlreadyExistsException e) {
                // ignore because 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 because 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. 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();
    }
    
    /*
     * because the following performs a privileged operation, only
     * jar containing this class needs CredentialAccessPermission
     */
    public void doPrivilegedCredOperation() {
        AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    doOperation();
                    return "done";
                }
        };
    }
}

18.6.2 Java SE Application with File Credentials Example

The example in this section illustrates a Java SE application that uses a file credential store represented by the $DOMAIN_HOME/config/fmwconfig/system-jazn-data.xml file.

In the example, the projectsrc.home system property points to the directory containing the Java SE application, and clientApp.jar is the application JAR file which is present in the dist directory.

The following grant illustrates access permissions:

<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>

Because no permission is granted to mapName=pc_map2,keyName=pc_key2, the call to setCredential for that map and key will fail.

The credential store used by the application is specified in jps-config-jse.xml:

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

Here is the Java SE code that calls the program.

package demo;
import java.io.ByteArrayInputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import oracle.security.jps.JpsContext;
import oracle.security.jps.JpsStartup;
import oracle.security.jps.JpsContextFactory;
import oracle.security.jps.JpsException;
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 {
    public CsfApp() {
        super();
    }
    public static void main(String[] a) {
        // perform operation as privileged code
        JpsContextFactory ctxFactory;
        try {
              new JpsStartup().start();
            ctxFactory = JpsContextFactory.getContextFactory();
            JpsContext ctx = ctxFactory.getContext();
            CredentialStore store =
                ctx.getServiceInstance(CredentialStore.class);
            CsfUtil csf = new CsfUtil(store);
            // next call is in a doPrivileged block and should succeed
            csf.doPrivilegedCredOperation();
 
            // because next call is not in a doPrivileged block,
            // it fails if CredentialAccessPermission is not granted to this class 
            csf.doCredOperation();
        } catch (JpsException e) {
            e.printStackTrace();
        }
   }
}

18.6.3 Java EE Application with File Credentials Example

This example shows a Java EE application using file credentials that calls the Credential Store Framework API. The jazn-data.xml file defines the appropriate access permissions, the codesource permissions, the permissions required for different combinations of map and key.

The following grant illustrates access permissions:

<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> 

The credential store used by the application is specified in the jps-config.xml file:

    <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>

The location property specifies the location of the cwallet.sso file.

Here is the example using these configurations:

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>");
 
            //get hold of app-level CSF service store
            //Outside app context, it returns the domain CSF store
            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 create operation is implemented inside a privileged block. Note that in a Java SE environment, the following two calls are equivalent:

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

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

18.6.4 Java EE Application with LDAP Store Example

The following example uses the same application used in Java EE Application with File Credentials Example, but the credential store is now LDAP instead of a file.

Here is an example of an LDAP store configuration:

    <serviceProviders>
        <serviceProvider name="ldap.credentialstore.provider"
 class="oracle.security.jps.internal.credstore.ldap.LdapCredentialStoreProvider">
            <description>Prototype LDAP 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"/>
        </jpsContext>
    </jpsContexts>

18.6.5 Java EE Application with DB Store Example

The following example uses the same application used in Java EE Application with File Credentials Example, but the credential store is now a database instead of a file.

Here is a example of a DB store configuration:

 <serviceProviders>
  <serviceProvider type="CREDENTIAL_STORE" name="db.credentialstore.provider" class="oracle.security.jps.internal.credstore.rdbms.DbmsCredentialStoreProvider"/>
   <description>DB CSF provider</description>
   </serviceProvider>
 </serviceProviders>
 
 <serviceInstances>
  <serviceInstance provider="db.credentialstore.provider" 
   name="credstore.db">
   <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 name="jdbc.url" value="jdbc:oracle:thin:@localhost:5521:ldapoid"/>
   <property name="jdbc.driver" value="oracle.jdbc.OracleDriver"/>
   <property name="datasource.jndi.name" value="jdbc/OpssDS"/>
  </serviceInstance>
 </serviceInstances>
 
 <jpsContexts default="appdefault">
   <jpsContext name="appdefault">
     <serviceInstanceRef ref="credstore.db"/>
   </jpsContext>
 </jpsContexts>