22 Developing with the Keystore Service

This chapter explains how to utilize the Keystore Service when developing applications.

Beginning with a survey of the Keystore Service API, the chapter describes what you need to know when invoking the service in your applications; explains how to configure the service; and presents examples of common keystore operations and ways to retrieve keys at runtime.

This chapter contains the following topics:

22.1 About the Keystore Service API

A keystore is used for secure storage of and access to keys and certificates. The Keystore Service API is used to access and perform operations on the keystores.

The Keystore Service:

  • enables you to manage keys and certificates securely

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

  • supports file-, database-, LDAP-based keystore management

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

  • creating keystores

  • deleting keystores

  • obtaining a handle to the domain trust store

  • obtaining a handle to a keystore

  • obtaining the configured properties for a keystore

  • obtaining a list of the keystores within an application stripe

Operations on a KeyStore are secured by KeyStoreAccessPermission, which implements the fine-grained access control model utilized by the Keystore Service.

22.2 Overview of Application Development with the Keystore Service

Knowledge of the following areas is helpful in getting your applications to work with the Keystore Service:

  • Determining appropriate application stripe and keystore names to use.

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

  • Defining the Keystore Service instance in jps-config.xml. Here is an example:

    <serviceInstancelocation="" name="keystore" provider="keystore.provider">
    <description>Default JPS Keystore Service</description>
    <property name="keystore.provider.type" value="file"/>
    <property name="keystore.file.path" value="./"/>
    <property name="keystore.type" value="KSS"/>
    <property name="keystore.csf.map" value="oracle.wsm.security"/>
    <property name="keystore.sig.csf.key" value="<alias>"/>
    <property name="keystore.enc.csf.key" value="<alias>"/>
    </serviceInstance>
    

    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 9.6, "Migrating the OPSS Security Store".
  • Steps to take in setting up the environment.

    The steps are different for stand-alone applications and those that operate in an Oracle WebLogic Server environment.

22.3 Setting the Java Security Policy Permission

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

Keystore Service supports securing keys in the following ways:

  • at the application stripe level

  • at the keystore level

  • with finer granularity for specific <application stripe, keystore, key>

Notes:

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

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

This section provides guidelines for permission grants to keystore objects, along with several examples:

Note:

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

22.3.1 Guidelines for Granting Permissions

The Keystore Service relies on Java permissions to grant permissions to keystore or key 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 application stripes and/or keystores.

22.3.2 Permissions Grant Example 1

The Keystore Service stores objects in a hierarchy:

application stripe -> keystore(s) -> key(s)/certificate(s)

See Also:

Section 12.1.1 for details about the object hierarchy in the Keystore Service.

This example grants permissions for a specific application stripe and a specific keystore name within that stripe.

<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.keystore.KeyStoreAccessPermission</class>
               <name>stripeName=keystoreapp,keystoreName=ks1,alias=*</name>
              <!-- All actions are granted -->
              <actions>*</actions>
            </permission>
        </permissions>
    </grant>
</jazn-policy>

where:

  • stripeName is the name of the application stripe (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).

  • keystoreName is the keystore name in use.

  • alias indicates the key alias within the keystore.

    Note:

    The wildcard indicates the application is granted permission for all aliases.

22.3.3 Permissions Grant Example 2

In this example, permissions are granted for a specific application stripe name and all its keystores.

<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.keystore.KeyStoreAccessPermission</class>
              <name>stripeName=keystoreapp,keystoreName=*,alias=*</name>
              <actions>read,write,update,delete</actions>
        </permission>
        </permissions>
    </grant>
</jazn-policy>

22.3.4 Permissions Grant Example 3

In this example, read permissions are granted for a specific key alias within an application stripe name and a keystore.

<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.keystore.KeyStoreAccessPermission</class>
              <name>stripeName=keystoreapp,keystoreName=ks1,alias=orakey</name>
              <actions>read</actions>
        </permission>
        </permissions>
    </grant>
</jazn-policy>

22.4 Configuring the Keystore Service

Pre-defined configuration files containing a Keystore Service instance with the location of the keystore and the provider classes are available in:

$DOMAIN_HOME/config/fmwconfig

The configuration files are ready to use and are named as follows:

  • jps-config.xml used by Java EE applications

  • jps-config-jse.xml used by Java SE applications

22.5 Using the Keystore Service API

You can use the Keystore Service within Oracle WebLogic Server or in a standalone environment.

22.5.1 Using the Keystore Service API in Java SE Applications

To use the keystore service 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 Keystore Service APIs, you need to configure the access permissions in the reference policy store. For examples, see Section 22.3, "Setting the Java Security Policy Permission".

  3. Set JVM command-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-jse.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.

  • -Dopss.version
    

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

  • -Djava.security.debug=all
    

    is helpful for debugging purposes. Refer to the Java SE documentation on Oracle Technology Network at http://www.oracle.com/technetwork for details about this property.

22.5.2 Using the Keystore Service API in Java EE Applications

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

  1. Out-of-the-box, the keystore service provider section of the jps-config.xml file is configured in the following directory:

    $DOMAIN_HOME/config/fmwconfig
    

    If needed, reassociate to an LDAP or database store.

  2. Set up the policy. To provide access to the Keystore Service APIs, you need to configure the access permissions in the reference policy store. For examples, see Section 22.3, "Setting the Java Security Policy Permission".

  3. Start Oracle WebLogic Server.

  4. Deploy and test the application.

22.6 Example of Keystore Service API Usage

This section provides an example of using the Keystore Service APIs. It contains these topics:

22.6.1 Java Program for Keystore Service Management Operations

The following Java code demonstrates common Keystore Service operations:

import oracle.security.jps.JpsContext;
import oracle.security.jps.JpsContextFactory;
import oracle.security.jps.JpsException;
import oracle.security.jps.service.keystore.KeyStoreProperties;
import oracle.security.jps.service.keystore.KeyStoreService;
import oracle.security.jps.service.keystore.KeyStoreServiceException;
 
import java.security.AccessController;
import java.security.PrivilegedAction;
 
public class KeyStoreTest {
 
    private static KeyStoreService ks = null;
 
 
    public KeyStoreTest() {
        super();
    }
 
    /*
     * This method performs a non-privileged operation. Either all code
     * in the call stack must have KeyStoreAccessPermission
     * OR
     * the caller must have the KeyStoreAccessPermission only and
     * invoke this operation in doPrivileged block
     */
    public static void doKeyStoreOperation() {
        doOperation();
    }

     /*
     * Since this method performs a privileged operation, only current class or
     * jar containing this class needs KeyStoreAccessPermission
     */
    public static void doPrivilegedKeyStoreOperation() {
        AccessController.doPrivileged(new PrivilegedAction<String>() {
            public String run() {
                doOperation();
                return "done";
            }
        });
    }
 
    private static void doOperation() {
        try {
            ks.deleteKeyStore("keystoreapp", "ks1", null);
        } catch(KeyStoreServiceException e) {
            e.printStackTrace();
        }

 public static void main(String args[]) throws Exception {
 
        try {
            new JpsStartup().start();
            JpsContext ctx = JpsContextFactory.getContextFactory().getContext();
            ks = ctx.getServiceInstance(KeyStoreService.class);
            
            // #1 - this call is in a doPrivileged block
            // #1 - this should succeed.
            doPrivilegedKeyStoreOperation();
 
            // #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 KeyStoreAccessPermission
            // wasn't granted to this class.
 
            /*
            doKeyStoreOperation();
            */
            
        } catch (JpsException je) {
            je.printStackTrace();
        }
 
    }
}

22.6.2 Reading Keys at Runtime

This section explains ways in which an application can access keystore artifacts at runtime, in the following topics:

22.6.2.1 Getting the Keystore Handle

To access a keystore at runtime and retrieve keys and certificates, an application must first get the keystore handle from the Keystore Service.

There are two approaches to this task:

  1. Obtain the handle by explicitly specifying the application stripe and keystore name as separate parameters

  2. Obtain the handle by specifying the application stripe and the keystore name as one parameter

Applications are free to use either method. Both methods work exactly in the same way for fetching keys and certificates, the only difference is the way the key store is loaded. Method 1 uses OPSS APIs while method 2 uses JDK KeyStore APIs.

22.6.2.2 Accessing Keystore Artifacts - Method 1

This example demonstrates the first method, in which the application stripe and keystore name are separate parameters.

// First get the KeyStoreService handle from JpsContext

JpsContext ctx = JpsContextFactory.getContextFactory().getContext();
KeyStoreService kss = ctx.getServiceInstance(KeyStoreService.class);
 
// Now get the keystore handle from KeyStoreService. There are two ways to 
// do this:
// Method 1: Explicitly specify application stripe and keystore name as
// separate parameters. In this example, the last parameter is null since
// we are demonstrating a permission-protected keystore. 

java.security.KeyStore keyStore = kss.getKeyStore("keystoreapp", "ks1", null);

// If this keystore is password-protected, provide the keystore password as the
// last parameter.
// java.security.KeyStore.ProtectionParameter pwd = new

java.security.KeyStore.PasswordProtection("password".toCharArray());

// java.security.KeyStore keyStore = kss.getKeyStore("keystoreapp", "ks1", pwd);
 
 
// Method 2: Specify application stripe and keystore name as one parameter
// using KSS URI. The last parameter is the keystore password, which is
// set to null in this example due to permission protected keystore.
// For password-protected keystore, set it to the keystore password.
// Since we are demonstrating Method 1 in this
// example, method 2 is commented out
 
// java.security.KeyStore keyStore kss.getKeyStore("kss://keystoreapp/ks1", null);
 
// Now you have a handle to the JDK KeyStore object. Use this handle to get
// key(s) and/or certificate(s)
 
// Get the private key or secret key associated with this alias. Pass the
// key password as the second parameter. In this case, it is null due to
// permission protected keystore.

Key key = keyStore.getKey("key-alias", null);
 
// Get the certificate associated with this alias

Certificate cert = keyStore.getCertificate("cert-alias");

Note:

Although this code fragment does not show this, calls to load, getKey, getCert are all privileged operations, and they should therefore be wrapped in a doPrivileged() block.

22.6.2.3 Accessing Keystore Artifacts - Method 2

This example demonstrates a method in which the application stripe and keystore name are provided as one parameter using the JDK Keystore URI.

// Create an object with parameters indicating the keystore to be loaded
FarmKeyStoreLoadStoreParameter param = new FarmKeyStoreLoadStoreParameter();
param.setAppStripeName("keystoreapp");
param.setKeyStoreName("ks1");
// The password is set to null in this example since it assumes this is a 
// permission protected keystore.
param.setProtectionParameter(null);
// If the keystore is password protected, use the following lines instead
// java.security.KeyStore.ProtectionParameter pwd = new java.security.KeyStore.PasswordProtection("password".toCharArray());
// param.setProtectionParameter(pwd);
 
// Initialize the keystore object by setting keystore type and provider
java.security.KeyStore keyStore = KeyStore.getInstance("KSS", new FarmKeyStoreProvider());
 
// Load the keystore. There are two ways to do this:
// Method 1: Explicitly specify application stripe and keystore name as
// separate parameters in param object
// keyStore.load(param);
 
// Method 2: Specify application stripe and keystore name as one parameter
// using KSS URI. Since we demonstrate method 2, in this
// example, method 1 is commented out
 
FarmKeyStoreLoadStoreParameter param = new FarmKeyStoreLoadStoreParameter();
param.setKssUri("kss://keystoreapp/ks1");
param.setProtectionParameter(null);
keyStore.load(param);
 
// Now you have a handle to JDK KeyStore object. Use this handle to get
// key(s) and/or certificate(s)
 
// Get the private key or secret key associated with this alias
Key key = keyStore.getKey("key-alias", null);
// Get the certificate associated with this alias
Certificate cert = keyStore.getCertificate("cert-alias");

Note:

Although this code fragment does not show it, calls to load, getKey, getCert are all privileged operations, and they should therefore be wrapped in a doPrivileged() block.

22.6.3 Policy Store Setup

For illustration, the example uses an XML-based policy store file (system-jazn-data.xml) which has the appropriate permissions needed to access the given keystore from the store. The file defines the permissions for different combinations of application stripe and keystore name. Other combinations, or attempts to access the store beyond the permissions defined here, will be disallowed.

Note:

This grant is added to the default policy store in
$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.keystore.KeyStoreAccessPermission
         </class>
         <name>stripeName=keystoreapp,keystoreName=ks1,alias=*</name>
         <actions>*</actions>
      </permission>
   </permissions>
</grant>

22.6.4 Configuration File

Here is a sample configuration of the keystore in the file (jps-config-jse.xml). The keystore.file.path property of the keystore service shows the directory containing the keystores.xml file:

Note:

For the complete configuration file see the default file shipped with the distribution in the directory $DOMAIN_HOME/config/fmwconfig/jps-config-jse.xml.
<jpsConfig>
 ...
    <serviceInstances>
        <serviceInstance name="keystore_file_instance"
                         provider="keystore_file_provider">
            <property name="keystore.file.path" value="store" />
            <property name="keystore.provider.type" value="file" />
        </serviceInstance>
    </serviceInstances>
 ...
</jpsConfig>

22.6.5 About Using the Keystore Service in the Java SE Environment

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

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

and:

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

22.7 Best Practices

In a clustered environment and when the store is file-based, use the Keystore Service Mbean API over the Keystore Service API to create, retrieve, update, and delete keys for an application.

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