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:

  • enables you to manage credentials securely

  • provides an API for storage, retrieval, and maintenance of credentials in different back-end repositories

  • supports file-based (Oracle wallet) and LDAP-based credential management

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

  • verifying if a credential map, or a credential with a given key, exists in the store

  • returning credentials associated with <mapname, key>

  • assigning credentials to <mapname, key>

  • deleting credentials associated with a given map name, or a given map name and key

  • resetting credentials for a specified <mapname, key>

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:

  • Determining appropriate map names and key names to use. This is critical in an environment with multiple applications storing credentials in the common credential store.

  • Provisioning Java security policies.

    Policy permissions are set in the policy store, which can be file-based (system-jazn-data.xml) or LDAP-based. Setting appropriate permissions to enable application usage without compromising the security of your data requires careful consideration of permission settings.

  • How to define the credential store instance in jps-config.xml.

    You will need to define the service instance in jps-config.xml only if manually crafting the configuration file.

    Note:

    The file-based provider is already configured by default, and can be changed to an LDAP-based provider. See Section 8.6, "Migrating the OPSS Security Store".

  • Steps to set up an environment.

    These steps differ according to the type of application: Java SE applications or Java EE applications.

Subsequent sections in this chapter provide detailed settings for each type of application.

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:

  • at the map level, or

  • with finer granularity for specific <mapname, key>

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:

  • a Repository Creation Utility (RCU) based application,

  • a Oracle WebCenter application, and

  • a Fusion Middleware Control application

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:

  • jps-config.xml for Oracle WebLogic Server

  • jps-config-jse.xml for Java SE

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

24.6 Using the CSF 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 Java SE Applications

To use the CSF API in Java SE applications, proceed as follows:

  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. Set JVM command-line options. See details below.

  4. Run the application.

Command-line options include:

  • -Doracle.security.jps.config
    

    specifies the full path to the configuration file jps-config.xml, typically located in the directory domain_home/config/fmwconfig.

  • -Djava.security.policy
    

    specifies the location of the OPSS/Oracle WebLogic Server policy file weblogic.policy, typically located in the directory wl_home/server/lib.

  • -Dcommon.components.home
    

    specifies the location of the oracle_common directory under middleware home.

  • -Djrf.version
    

    specifies the version used in the environment; its value is 11.1.1.

  • -Djava.security.debug=all
    

    is helpful for debugging purposes

24.6.2 Using the CSF API in Java EE Applications

To use the CSF API in Java EE applications, proceed as follows:

  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 presents several code samples illustrating the use of the credential store framework APIs. Each code sample illustrates how credential store operations are affected by permissions, and they include sample configuration files.

The section includes the following samples:

24.7.1 Common Code for CSF Operations

The following "utility" program performs the CSF API operations, and it is called by code in the examples that follow this section.

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

See Also:

Section 22.1, "Using OPSS in Java SE Applications," for details about securing a Java SE applications with OPSS.

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, "Common 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.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);
            // #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 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.

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 OID LDAP Store

This example uses the same Java EE application used in Example 2: Java EE Application with Wallet Store; the only difference is that the credential store in the following example is LDAP-based and instead of file-based (wallet).

The domain's jps-config.xml must configure the following properties:

  • The root name:

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

    <property name="oracle.security.jps.farm.name" value="cn=OracleFarmContainer" />
    
  • The LDAP URL:

    <property name="ldap.url" value="ldap://mynode.us.mycorp.com:1234"/>
    
  • The principal key, which (in conjunction with the above required properties) is used to locate the credentials to access the LDAP store:

    <property name="bootstrap.security.principal.key" value="bootstrap"/>
    

Here is an example of a jps-config.xml file specifying the required properties for a OID LDAP-based store:

<jpsConfig>
    <serviceProviders>
        <serviceProvider name="ldap.credentialstore.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"/>
        </jpsContext>
    </jpsContexts>
</jpsConfig>

24.7.5 Example 4: Java EE Application with Oracle DB Store

This example uses the same Java EE application as in Example 2: Java EE Application with Wallet Store; the only difference is that the credential store in the following example is DB-based instead of file-based (wallet).

The following properties must be configured in the domain's jps-config.xml file:

  • The root name:

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

    <property name="oracle.security.jps.farm.name"
     value="cn=OracleFarmContainer"/>
    
  • The JDBC URL:

    <property name="jdbc.url" value="jdbc:oracle:thin:@<host>:<port>:<service>"/>
    
  • The JDBC driver:

    <property name="jdbc.driver" value="oracle.jdbc.OracleDriver"/>
    
  • The datasource JNDI name:

    <property name="datasource.jndi.name" value="jdbc/OpssDS"/>
    
  • The principal key, which (in conjuntion with the above required properties) is used to locate the credentials to access the DB store:

    <property value="bootstrap" name="bootstrap.security.principal.key"/>
    

Here is an example of a jps-config.xml file specifying the required properties for a DB-based store:

<jpsConfig>
 <serviceProviders>
  <serviceProvider type="CREDENTIAL_STORE" name="db.credentialstore.provider" class="oracle.security.jps.internal.credstore.rdbms.DbmsCredentialStoreProvider"/>
   <description>DB-based 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>
</jpsConfig>

24.8 Best Practices

In a clustered environment and when the store is file-based, 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.