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:
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.
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.
See Also:
Defining the Keystore Service 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 take in setting up the environment.
The steps are different for stand-alone applications and those that operate in an Oracle WebLogic Server environment.
The Oracle Platform Security Services keystore provider is set when the server is started. When the provider is file-based, the data is stored in system-jazn-data.xml
.
Keystore Service supports securing keys:
at the application stripe level,
at the keystore level, or
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
.
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.
The Keystore Service stores objects in a hierarchy:
application stripe -> keystore(s) -> key(s)/certificate(s)
See Also:
Section 11.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.
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> <!-- Certain actions are explicitly specified --> <!-- Compare to wild-card grant in previous example --> <actions>read,write,update,delete</actions> </permission> </permissions> </grant> </jazn-policy>
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>
You need to define the Keystore Service instance in a configuration file which contains information about the location of the keystore 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
You can use the Keystore Service within Oracle WebLogic Server or in a standalone environment.
To use the keystore service API in Java SE applications, proceed as follows:
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".
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 27.3, "Setting the Java Security Policy Permission".
Set JVM command-options. See details below.
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
To use the API in Java EE applications, proceed as follows:
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.
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 27.3, "Setting the Java Security Policy Permission".
Start Oracle WebLogic Server.
Deploy and test the application.
This section provides an example of using the Keystore Service APIs. It contains these topics:
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(); } } }
See Also:
Keystore API documentation at http://docs.oracle.com/javase/6/docs/api/index.html
This section explains ways an application can access keystore artifacts at runtime. It contains these topics:
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:
Obtain the handle by explicitly specifying the application stripe and keystore name as separate parameters
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.
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.
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.
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>
Here is a sample configuration 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 at $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>
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);
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.