2.3 IRM Java API

Applications that want to process sealed content locally rather than sending the content to a server can use the IRM Java API. When using this API the sealed content cryptography is performed in the same process as the calling application. Cryptography keys are shipped from the IRM J2EE application and used in the process using the API.

Cryptography Keys:

An application using the IRM Java API has the ability to obtain the sealed content cryptography keys. The IRM Java API stores these keys in the memory of the calling process whilst they are being used. Debugging the IRM Java API, or doing a memory dump could compromise the key material. Do not use the IRM Java API if the environment in which the application is running is insecure or untrusted (for example, a laptop).

The typical use for the IRM Java API is for trusted integrations that want to seal, unseal or examine sealed content where network latency or performance may be an issue.

The API implementation makes use of Java Cryptography Extension (JCE) to perform cryptography operations. If use of AES 256 cryptography is required, the unlimited strength policy JAR files must also be installed. For the latest information about supported JDK/JREs, see the System Requirements and Supported Platforms for Oracle Fusion Middleware page on Oracle Technology Network at http://www.oracle.com/technology/software/products/ias/files/fusion_certification.html.

When using the IRM Java API, the following jar file should be added to the compile time or runtime classpath:

  • irm-api.jar

This irm-api.jar has a manifest entry that adds the following jars files to the classpath. These jar files need to be distributed with the irm-api.jar and placed in the same folder as the irm-api.jar file.

  • irm-common.jar

  • irm-engine.jar

  • irm-client.jar

  • irm-ws.jar

These jar files can be obtained from the IRM server installation, under the folder ECM_ORACLE_HOME/irm/api/lib. These libraries are thread safe.

2.3.1 Runtime Configuration

Configuration settings for the IRM Java API are read from an XML configuration file. The configuration settings tell the IRM Java API where to store the offline cache of licenses, classification and cryptography key information, and the device identity, as well as where to find the key store containing the cryptography key used to protect sealed content cryptography key material.

<?xml version="1.0" encoding="UTF-8" ?>
<core:DesktopConfiguration xmlns:core="http://xmlns.oracle.com/irm/core">
    <storageFolder>/home/irm</storageFolder>
    <device>6f750242-c35e-4992-bb37-e160dba87779</device>
    <application>
        <name>sample-application</name>
    </application>
    <keyStore>
        <type>JCEKS</type>
        <path>/home/irm/irm_api.jceks</path>
    </keyStore>
</core:DesktopConfiguration>

This XML configuration file can be stored in any location accessible to the process running the IRM Java API. The location is specified by setting the oracle.irm.client.configuration system property. If this property is not set, the IRM Java API will throw an exception, informing the caller than the configuration settings have not been specified. Here is an example of a Java program running with the system property set to /home/irm/irm-configuration.xml:

java -Doracle.irm.client.configuration=/home/irm/irm-configuration.xml MyProgram

2.3.1.1 Storage Folder

When the IRM Java API is used to seal, unseal, reseal and reclassify content, license and cryptography key details are requested from the IRM J2EE application for the authenticated user. Depending on the license rules these details may be stored in memory or on the file system and used for future requests to seal, unseal, reseal, or reclassify content. The storage folder is where this data is stored. This data is stored in encrypted form and is locked to the machine that requested the information.

Authentication:

Once license and key material has been obtained from the IRM J2EE application, the process using the IRM Java API will be able to process sealed content, regardless of the user used to authenticate the original request. Unlike the sealing server, these details are shared per process rather than per user.

The following two files will be created the first time an operation is performed that needs to store license related details:

  • irm-store/irm-desktop-store.xml

  • irm-key-sets/irm-key-set-48ff59ab-be7a-4a2a-a093-65286f949909.xml

    The UUID value 48ff59ab-be7a-4a2a-a093-65286f949909 will vary from installation to installation.

Backups:

If the contents of the storage folder are deleted, the next time the IRM Java API saves information to the file system these files will be recreated. There is no need to back up the files generated by the IRM Java API.

2.3.1.2 Device

Applications that perform sealing operations, such as Oracle IRM Desktop, sealing server and the IRM Java API, provide details about the where the operation is being performed. This information is provided in the form of a device. Each device has a unique value that is used to identify the location of an Oracle IRM client to the IRM J2EE application. This value is in the form of a UUID and is called the device UUID. When using the IRM Java API, a new UUID value should be generated and specified in the configuration file. It is valid to allow logically identical devices to share the same device UUID value. For example, a cluster of sealing services shares the same device UUID, so every member of the cluster can obtain the same licenses and the cluster is seen as one logical location.

The device UUID is used internally by IRM to track the location of licenses. When a license is used, it is typically checked out to a device (depending on the license criteria). This prevents the license being used on multiple devices at the same time. Sharing the same device UUID is however a simple way to relax this restriction when scaling or clustering an application that uses the IRM Java API.

2.3.1.3 Application

The application name is a human-readable label for the application that is processing sealed content. The application name is part of the information provided to the IRM J2EE application in a content-related audit record.

2.3.1.4 Key Store

When the IRM Java API obtains key material from the IRM J2EE application, the key material is encrypted using a key dedicated to the device. To be able to use the key material, the IRM Java API must have access to the appropriate cryptography key so that the key material can be decrypted and used to process sealed content. The key store type and location are specified in the configuration file. Passwords for the key store and key are not stored in the configuration file. Passwords will be prompted for on the command line when the IRM Java API first accesses the key store.

2.3.2 Enabling the IRM Java API

By default, the Oracle IRM J2EE application will only respond to requests from the Oracle IRM Desktop and the sealing server. To use the IRM Java API, additional configuration steps are required to add the application using the IRM Java API as a trusted application. Processing sealed content locally using the IRM Java API requires local access to the content cryptography keys. These keys are requested from the Oracle IRM J2EE application, which will respond with key material together with licenses. These licenses control what operations the IRM Java API is allowed to perform.

Without enabling the IRM Java API, requests to process seal content will result in the following error:

oracle.irm.engine.core.desktop.RepudiateException: IRM-01023: The desktop is repudiated

Peeking:

If an application is only using the peek operation to extract sealed content metadata, there is no need to set up a trust relationship between the application using the IRM Java API and the IRM J2EE application. Peeking does not use sealed content cryptography keys while peeking content.

2.3.2.1 Generating a Key

The first step in enabling the IRM Java API to use an IRM J2EE application is to generate a new AES symmetric key or RSA asymmetric key pair. This can be done with the Java keytool command line tool. This AES symmetric key or RSA asymmetric key pair are used as a trust mechanism. The IRM J2EE application will encrypt data using a key, and the IRM Java API will decrypt data using the corresponding key.

Key Algorithm Choice:

On AIX platforms using the IBMJCE cryptography provider, the AES key wrap algorithm is not supported, so a RSA 2048 bit trust key must be used. If the IRM J2EE application is using a JKS key store type, the IRM Java API is also limited to using a RSA key. In all other cases, generate an AES key which is at least the same size as the sealed content key sizes. For example, when using the AES128 cryptography schema, generate a 128 or 256 bit AES key for the IRM Java API.

The following is an example of generating a 128 bit AES key and storing it in a JCEKS file based key store. The key alias should be the device UUID specified in the IRM Java API configuration. In the examples shown, the device UUID is b2e1ea48-cc3e-44bc-ad9b-a214c62fd410 and the key store is called irm_api.jks/irm_api.jceks:

keytool -keystore irm_api.jceks -storetype JCEKS -genseckey -alias b2e1ea48-cc3e-44bc-ad9b-a214c62fd410 -keysize 128 -keyalg AES

The following is an example of generating a 2048 bit RSA key and storing it in a JKS file based key store:

keytool -keystore irm_api.jks -keysize 2048 -genkeypair -alias b2e1ea48-cc3e-44bc-ad9b-a214c62fd410 -keyalg RSA

These commands all create the key store, generate a key, and prompt the user for a key store and key password. This key store is the one that should be used in the IRM Java API configuration.

Certificate:

When generating the RSA key, keytool will prompt for certificate details. The certificate details are not used by the IRM Java API or IRM J2EE application, so enter blank values for the certificate questions.

2.3.2.2 Adding the Key to the Server

The symmetric key or asymmetric public key generated with keytool must be added to the IRM J2EE application key store, using the device UUID value as the key alias. The IRM J2EE application then uses this key to encrypt key material before sending it to the IRM Java API. Without the key being added to the IRM J2EE application key store, the IRM Java API will not be able to process sealed content.

Location:

The IRM J2EE application key store is typically located under the folder DOMAIN_HOME/config/fwmconfig.

2.3.2.3 AES Keys

If the IRM Java API key is an AES key then the keytool -importkeystore feature can be used to import the key into the server key store. In this example, the IRM Java API key store is called irm_api.jceks and the IRM J2EE application key store is called irm.jceks:

keytool -importkeystore
  -srckeystore irm_api.jceks
  -srcstoretype JCEKS
  -srcalias b2e1ea48-cc3e-44bc-ad9b-a214c62fd410
  -destkeystore irm.jceks
  -deststoretype JCEKS
  -destalias b2e1ea48-cc3e-44bc-ad9b-a214c62fd410

When specifying the key store file name, the key store path may also be required, depending on whether the keytool command is run in the same folder as the key store file.

2.3.2.4 RSA Keys

The first step is to export the public key part of the asymmetric key pair from the IRM Java API key store. In this example, the IRM Java API key store is called irm_api.jks and the IRM J2EE application key store is called irm.jks.

keytool -exportcert -keystore irm_api.jks -alias b2e1ea48-cc3e-44bc-ad9b-a214c62fd410 -file b2e1ea48-cc3e-44bc-ad9b-a214c62fd410.cer

The next step is to import this public key into the IRM J2EE application key store.

keytool -importcert -alias b2e1ea48-cc3e-44bc-ad9b-a214c62fd410 -file b2e1ea48-cc3e-44bc-ad9b-a214c62fd410.cer -keystore irm.jks

When specifying the key store file name, the key store path may also be required, depending on whether the keytool command is run in the same folder as the key store file.

2.3.2.5 Setting the Password

The IRM J2EE application will need to read the imported key and so will need to know the key password for symmetric keys. For asymmetric there is no password associated with the public key, so this step can be skipped. The key password for the IRM J2EE application is stored in the Fusion Middleware Credential Store Framework (CSF). The WLST tool is used to add new passwords. A WLST shell should be launched from ECM_ORACLE_HOME/common/bin. The credential is added using the createCred command. The example below shows connecting to an admin server and setting the password for a key store called irm.jceks to the value password for the key with alias b2e1ea48-cc3e-44bc-ad9b-a214c62fd410.

connect("weblogic", "password", "t3://adminServerHost:adminServerPort")
createCred("IRM", "key:irm.jceks:b2e1ea48-cc3e-44bc-ad9b-a214c62fd410", "dummy", "password")

In this example, the key alias is b2e1ea48-cc3e-44bc-ad9b-a214c62fd410 and the key store file name is irm.jceks. Both these values should be altered to reflect the device UUID used and the actual IRM J2EE application key store file name. As keys do not require a user name, the value dummy is used in place of a user name.

2.3.3 Authentication

A sealed content operation that ends up requesting licenses and keys may involve a request to the IRM J2EE application, which in turn will require authentication. A remote call is made from the IRM Java API if the local memory and file cache of licenses and keys cannot satisfy the request. The peek operation does not require access to licenses and keys, and will never cause a request to the IRM J2EE application.

2.3.3.1 Using a java.net.Authenticator

A java.net.Authenticator can be used to specify the username and password. Any HTTP connection that requires authentication will call back to the authenticator to request credentials.

java.net.Authenticator.setDefault(
    new java.net.Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication("username", "password".toCharArray());
        }
    });

This code sample uses hard-coded passwords and user names. In a production system, these details should be retrieved from a secure location or a user prompt.

2.3.4 Advanced Topics

The IRM Java API provides a plug-in mechanism that allows code to be augmented into internal processes. Two such processes are the code that provides the key store and key password for the wrapping keys, and the code that reads configuration settings. Both processes can be changed to use programmer-provided logic instead of using the default IRM Java API logic.

2.3.4.1 Supplying Configuration Settings in Code

The configuration settings can be provided using code rather than using an XML configuration file. If code is used, there is no need to specify a location for the XML configuration file as a system property. The sample code below shows how configuration settings can be provided using code. The settings are hard-coded and are identical to the XML sample configuration file. In a production system this code could read the settings from an alternative location rather than relying on an externally created XML file.

import static oracle.irm.engine.core.desktop.DesktopApplicationFactory.createDesktopApplication;
import static oracle.irm.engine.content.store.KeyStoreSettingsFactory.createKeyStoreSettings;
 
import oracle.irm.engine.core.desktop.DesktopApplication;
import oracle.irm.engine.content.store.KeyStoreSettings;
import oracle.irm.engine.core.config.DesktopConfigurationSlice;
import oracle.irm.engine.util.Aspect;
import oracle.irm.engine.util.Purpose;
import java.io.File;
import java.util.UUID;
 
@Aspect(Purpose.INTERCEPTOR)
public final class DesktopConfigurationDemo extends DesktopConfigurationSlice {
 
    @Override
    public void construct() {
 
        UUID deviceUuid = UUID.fromString("6f750242-c35e-4992-bb37-e160dba87779");
 
        File storageFolder = new File("/home/irm");
 
        KeyStoreSettings keyStore= createKeyStoreSettings("JCEKS", new File("/home/irm/irm_api.jceks"));
 
        DesktopApplication application = createDesktopApplication("demo");
 
        vthis.construct(
            storageFolder,
            deviceUuid,
            application,
            keyStore);
    }
}

2.3.4.2 Specifying Key and Key Store Passwords in Code

The following code sample shows how to specify key and key store passwords. In a production integration, these passwords should be obtained using an appropriate mechanism and passed into the construct method rather than using hard-coded values. The key parameter provides details of the key store and key being requested. This key parameter can be used if the key store path or key alias name are needed to choose an appropriate set of passwords.

import oracle.irm.engine.content.key.WrappingKeySlice;
import oracle.irm.engine.content.store.KeySettings;
import oracle.irm.engine.content.store.KeyStoreAccessException;
import oracle.irm.engine.content.store.UnknownKeyException;
import oracle.irm.engine.util.Aspect;
import oracle.irm.engine.util.Purpose;
 
@Aspect(Purpose.INTERCEPTOR)
public final class WrappingKeyDemo extends WrappingKeySlice {
 
    @Override
    public void construct(KeySettings key) throws KeyStoreAccessException, UnknownKeyException {
 
        vthis.construct(
            key,
            "password".toCharArray(),
            "password".toCharArray());
    }
}

2.3.4.3 Activating Code Plug-ins

To activate code plug-ins, the XML file META-INF/oracle_irm_deployment.xml needs to be added to the classpath. The easiest way to do this is to package this file in the same jar file as the plug-in code and add the jar file to the end of the classpath. This sample file oracle_irm_deployment.xml tells the API to add in the WrappingKeyDemo and DesktopConfigurationDemo classes into the internal logic processes.

<system:Deployment xmlns:system="http://xmlns.oracle.com/irm/system">
    <components>
        <name>demo</name>
        <plugins>
            <interfaceClass>oracle.irm.engine.content.key.WrappingKey</interfaceClass>
            <resourcePath>/oracle/irm/engine/content/key/aspects/classes/WrappingKey.xml</resourcePath>
            <slices>
                <implementationClass>WrappingKeyDemo</implementationClass>
            </slices>
        </plugins>
        <plugins>
            <interfaceClass>oracle.irm.engine.core.config.DesktopConfiguration</interfaceClass>
            <resourcePath>/oracle/irm/engine/core/config/aspects/classes/DesktopConfiguration.xml</resourcePath>
            <slices>
                <implementationClass>DesktopConfigurationDemo</implementationClass>
            </slices>
        </plugins>
    </components>
</system:Deployment>

When adding your own classes, change the contents of the implementationClass element to provide the package and class name of the class created. The name element can be used to give the plug-in a unique name: this could be the name of the company or application providing the plug-in logic. Also ensure the classes that are created are added to the classpath.