Chapter 1: Introduction

AKM SDK for Java

The AKM SDK for Java provides an API for client applications to access AKM services. AKM services are segmented into functional areas for administration, key retrieval and remote encryption services.

This SDK supports the key retrieval and remote encryption roles. Keys may be retrieved from AKM to use in cryptographic operations on the client, or a key may be used to encrypt or decrypt data on the AKM server, without the client retrieving the key.

Together, key retrieval and remote encryption services are referred to as the key client API, distributed as a Java archive file: “akmkeys.jar”. Audit and error logging are implemented using the Apache Log4j library, which is included with the SDK, along with other resources.

Unless otherwise indicated, references in this guide to “the API” refer to the key client API, and “the SDK” refers to the entire distribution, including documentation and samples.

Who is this for?

This guide is for Java application developers who want to develop client-side applications for AKM’s key management and encryption services. See the AKM User Guide to review introductory AKM concepts.

An XML configuration file can be used to set server addresses and other options. Familiarity with basic XML terminology is also assumed.

AKM uses digital certificates to secure communication with clients. Developers should be familiar with certificates, Java keystores, and the Java keytool utility.

Other resources

Technical documentation and other resources for AKM installation, configuration and usage are bundled in an archive called the “AKM Supplemental”.

The AKM Supplemental also includes tools, applications, code samples, and libraries to support client development, including this AKM SDK for Java.

Notices

This product and documentation is covered by U.S. and International copyright law. This product may incorporate software licensed under one or more open source license agreements. Government users please note that this product is provided under restricted government use license controls. Please refer to the Alliance Key Manager End User License Agreement for more information.

The AKM Java client SDK uses Java SE cryptography APIs. By default, AES key strength is limited to 128 bits. Subject to applicable laws, an “Unlimited Strength Policy” jar file may be downloaded from Oracle to enable AES 192 and 256 bit operations. See Appendix B for more details.

Oracle and Java are registered trademarks of Oracle and/or its affiliates. Apache Log4j is a registered trademark of The Apache Software Foundation. Other names may be trademarks of their respective owners.

Change log

The following table provides information on the changes to this documentation:

Version Date Description
0.01 12/23/2008 Initial draft.
0.02 12/29/2008 Add additional documentation from Thomas.
0.03 12/30/2008 Updates from Thomas.
1.00 1/6/2009 Initial release.
2.1.13.001 10/23/2013 New manual format.
3.0.0.001 11/6/2014 Update for AKM 3.0.0 and JALLKeyRtv v. 2.2.
3.0.3.001 12/18/2014 Update for AKM 3.0.3 and the new version of AKM for VMware.
3.0.3.002 6/11/2015 Update for the new release of the AKM SDK for Java. Rename to “AKM Guide for Java Developers v2”.
3.0.3.003 11/4/2015 Add section on key Instance considerations using encryption service API calls.
3.0.3.004 11/20/2015 Organizational updates.
4.0.0.001 5/24/2016 Update preparation chapter for AKM 4.0.
4.5.0.001 10/21/2016 Update for asymmetric RSA key support and AKM SDK for Java version 2. Name change to “AKM Guide for Java Developers.”

Chapter 2: Preparation

This guide assumes a licensed and operational AKM server; see Appendix A: Related Documentation.

Prerequisites

The AKM SDK for Java is supported for Java 7 and higher. Applications using the SDK can be compiled and run with either Oracle JDK 7 or 8, and OpenJDK.

Note that Oracle Java 7 is out of public support, but not yet in End Of Life status, and security updates are now on a paid basis: http://www.oracle.com/technetwork/java/eol-135779.html.

Although Java 7 can be used, the use of Java 8 is encouraged, and the jar file as distributed is built with Java 8. A jar file build with Java 7 is also available for developers needing to compile with Java 7 applications.

The SDK relies on the SUN JCE providers for certain features. If running under the IBM JDK, the SUN providers will have to be installed. See the “Installing Providers” section of: http://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/CryptoSpec.html.

Server information

The following server information is required for configuration:

  • The network address of the primary AKM server and any secondary AKM servers

  • The port number that AKM has been configured to use for key retrieval (the default is 6000) or remote encryption (the default is 6003)

AKM functions use several network ports. By default, key administration access is on port 6001, key retrieval on port 6000, and remote encryption services on port 6003. These port assignments can be changed by the server administrator. AKM also exposes a KMIP interface on port 5696; however, the AKM SDK for Java does not currently support KMIP services.

Certificates

The AKM server and clients use certificates and private keys for mutual authentication over a TLS connection. See Appendix C for an overview of AKM certificate usage specific to the key client API.

The AKM SDK for Java uses certificates stored in the native Java keystore (JKS) format, with client certificates and private keys in a keystore file, and trusted certificate authority (CA) certificates in a truststore file.

These files are generated automatically during server initialization. See the AKM platform specific deployment guides for instructions on downloading these files. Note that AKM users are not restricted to the initial certificates; the client application developer should coordinate certificate usage with the AKM system administrator.

NOTE: The standard configuration for AKM mutual client and server authentication is to install AKM’s CA certificate along with a client certificate/private key on the client. See Appendix E for information on using AKM server certificates on the client for stricter server authentication via “certificate pinning”.

Encryption keys

AKM setup and initialization includes the option to generate an initial set of encryption keys. Details are documented in specific user guides for each deployment platform.

Encryption keys can also be created using the AKM Administrative Console. See the AKM Administrative Console Guide for more information.

Checklist

  • Initialize AKM

  • Oracle Java SE version 7 or 8, or OpenJDK 1.7 or higher

  • AKM Server address (as resolvable hostname or dotted IPv4 format), key retrieval services port number (default port 6000) or encryption services port number (default port 6003)

  • Authentication certificates, including the Certificate Authority and client certificates

  • Encryption keys on the AKM server

Chapter 3: Installation

Installation

There is no installer for the SDK; akmkeys.jar can be deployed to any location. The java classpath must refer to the akm and Log4J jar files, and a Log4j configuration file must be present in the runtime classpath.

Another section of this guide describes compiling and running an example program, including classpath settings.

AKM SDK for Java components

The AKM SDK for Java distribution includes the following components:

  • The “akmkeys.jar” file

  • A redistributed copy of “log4j-1.2.17.jar”, renamed as “log4j.jar”

  • Javadoc files

  • Example programs

  • An Apache Ant script to build and run sample applications that use the API

  • A text file that shows how to compile and run applications on the command line

akmkeys.jar

The key client API is implemented in akmkeys.jar; the only external dependency is Log4j (see next section). Applications can retrieve encryption keys from the server for client-side encryption tasks, or use the API to submit data to be encrypted or decrypted by AKM remote encryption services.

Apache log4j.jar

Audit and error logging use the Apache Log4j library. The Apache distribution file “log4j-1.2.17.jar” has been renamed as “log4j.jar” and bundled with the AKM SDK for Java, as a convenience, but is otherwise unmodified.

Applications using Log4j version 2 can be run by including Apache “log4j.1.2.bridge.jar” in the classpath. See the Apache Logging website (https://logging.apache.org/log4j/2.x/) for details.

Javadoc files

The distribution includes a zip file containing Javadoc files, which detail the public methods of the API, in standard Javadoc format.

Samples

Working sample Java programs demonstrate retrieving a key to use on the client for encryption, and using the AKM remote encryption services without retrieving keys.

The “AkmLocalEncrypt.java” sample shows symmetric key retrieval and local encryption using the javax.crypto packages. “AkmRemoteEncrypt.java” shows how to submit a symmetric key identifer and plaintext data to be encrypted by AKM, which returns the resulting ciphertext.

Both samples also decrypt the ciphertext. Similar samples show retrieving asymmetric (RSA) keys, or performing RSA encryption and decryption on the server.

See Chapter 8 for more information about the code samples.

Chapter 4: Configuration

XML configuration

An application using the key client API must be configured to access one or more AKM servers, for one or more client roles. Server configuration includes network and port information; client configuration indicates which servers to contact, and in what order. An AKM configuration also defines certificate keystores used by servers and clients.

An AKM client application can be configured in code, using setter methods, or with a configuration file in XML format. This section describes configuring with XML.

The SDK includes an example XML file, “jakmcfg.xml”, which configures a client to access a primary AKM server and one failover server, for key retrieval and encryption roles. Not all options are shown in the example. The full XML schema is described in Appendix F.

The <akmconfig> element includes child elements for keystores, AKM servers, and client roles, comprising the entire configuration. It can be the document root element, as shown here, or a first level direct child element of the document root.

<?xml version="1.0" encoding="utf-8"?>
<akmconfig>

The <keystores> element is a collection of named <keystore> and <truststore> elements defining Java keystore files and passwords to access them. A truststore is a keystore which holds trusted Certificate Authority certificates.

<keystores>
		<keystore name="akmkeystore" path="AKMClientKeystore.jks"
					 pw="password"/>
		<truststore name="akmtruststore" path="AKMRootCATrustStore.jks"
					 pw="password" />
	</keystores>

The <keyService> includes a list of named server elements. Other attributes of a <server> include the network address, and a truststore attribute referring to a named <truststore> element.

A <server> element can optionally include a trustAlias attribute to identify a specific server certificate. This is certificate pinning as described in Appendix E.

The <keyService> can include optional portrole elements to override the default AKM ports, but that is not shown here. See Appendix F for details.

<keyService>
<servers>			
		<server name="primary" address="10.0.1.1" truststore="akmtruststore"/>
         		<server name=”alt1" address="10.0.1.2" truststore="akmtruststore"/>
         </servers>
</keyService>

The <keyClient> element defines one or more client roles, corresponding to the AKM services exposed on different network ports, and refers to one or more named <server> elements, with attributes for failover order and timeouts.

The <clientrole> name attribute must be one of the values: “keyRetrieval”, “encryption”, or “admin” (note that the “admin” role is not supported by the keyClient API, but this same configuration schema is used by a separate admin client API).

Other <clientrole> attributes identify a named <keystore> element, and an optional alias value to select a specific certificate. Aliases are optional and described in Appendix E.

<!-- keyClient has client roles and akmservers failover order -->
 <keyClient>	
	<clientrole name="keyRetrieval" keystore="akmkeystore" alias=”keyrequestor”/>
	<clientrole name="encryption" keystore="akmkeystore"/>		 
	
	<!-- primary and failover servers -->
  	<akmservers>
		<akmserver name="primary" order = "1" timeout="5000"/>
		<akmserver name="alt1" order = "2" timeout="4000"/>
	</akmservers>
</keyClient>

The closing </akmconfig> tag completes the configuration.

</akmconfig>

Naming individual elements such as <server> allow for configuring multiple servers differently; later these <server> elements are referenced by name in the section that configures client failover. Changing the failover order or timeout values is done in the <client> section.

Similarly, naming individual <keystore> elements allows them to be referenced where needed by server and client configuration elements.

This schema can support any number of servers, using shared or distinct certificates; similarly, different client roles can use shared or distinct certificates.

An .xsd schema file that formally defines the XML format is included in the SDK and described in Appendix F.

SECURITY ALERT: The XML file may contain keystore passwords in the clear. These XML files themselves should be secured, and access to them, including read access, should be tightly managed.

The passwords are not required attributes in the XML. They can be omitted and supplied later to the application by another mechanism, such as interactive user entry at runtime.

As noted above, an AKM configuration can be set from code instead of using an XML file. The AkmConfig and AkmServerConfig objects expose getter and setter methods for the same properties modeled in the XML. See Chapter 6 for more details.

Chapter 5: Programming Model

The key client API exposes public methods for key retrieval and remote encryption services. Network communication and the AKM protocol are encapsulated by the API, allowing application code to focus on the high level programming model described here.

A base class, AkmRequest, handles logic common to all requests, including secure server connections. Applications can only create and use subclasses of AkmRequest; there is no public constructor for AkmRequest.

Key retrieval is supported by the AkmKeyRequest object, and remote encryption services by the AkmEncryptRequest and AkmDecryptRequest objects.

Request objects are created with a specific configuration from an instance of an AkmConfig object. Once created, an AkmConfig object can be reused to create multiple request objects with the same configuration values.

A request object can be created directly from a configuration file, instead of in two steps. The request object can be reused for more than one request, for example, to retrieve different keys, without having to reconfigure for each AKM request.

Similarly, a single encryption request object could be reused to encrypt different data items, rather than creating a new request each time.

Error conditions are reported with checked exceptions specific to the AKM SDK. Application code can catch these exceptions and extract error codes and context for problem diagnosis. Most error conditions are also logged (see the Logging section).

Logging

Logging is supported using the Apache Log4j library. The log files and level of logging detail can be managed with a standard Log4j configuration file without recompiling the application. The Log4j configuration file can be in either XML format or a Java properties format, named either “log4j.xml” or “log4j.properties”.

A sample “log4j.xml” file is included with the SDK, which manages separate logs for audit logging and debug logging. This file is for illustration purposes, and can be modified or extended or replaced entirely.

The AKM SDK for Java creates log entries for info, debug, and trace levels. The log messages contain comma separated key=value items. For example, this excerpt from the debug log shows errors trying to connect to primary and failover AKM servers:

2015-04-10 04:13:41,963 UTC, DEBUG, EXCEPTION=SocketTimeoutException, REMOTE_HOST=192.168.51.36:6000
2015-04-10 04:13:44,974 UTC, DEBUG, EXCEPTION=SocketTimeoutException, REMOTE_HOST=192.168.51.30:6100

A Log4j Logger instance is created for each AkmRequest object instance, with the same logger category name “akmkeys”.

Developers can format and manage the log files using Log4j configuration files. The AKM SDK for Java does not have any built-in log policies; it merely provides the mechanism to log events while an application using the SDK is running.

NOTE The log4j.jar file must be referenced in the classpath to run any application built with the AKM SDK for Java.

Also, either a Log4j xml or properties file must be present in the runtime classpath; if an application chooses not to create logs, the Log4j file would have no Appenders enabled.

Concurrency considerations

The AKM SDK for Java has no built-in synchronization. While no classes contain static members, thread safety is not guaranteed. Multi-threaded applications should explicitly synchronize AkmRequest and AkmConfig objects.

Log4j synchronizes on files being written, if using the Log4j RollingFileAppender, as shown by the sample log4j.xml file included with the SDK. See the Apache Log4j documentation if using other Appenders.

The next sections provide detailed descriptions of the AKM client classes and their usage.

Chapter 6: Using the AkmConfig Object

A client needs to be configured with certificate and server information to connect with AKM. The AkmConfig object can be initialized from an XML file or directly in code.

An AkmConfig instance contains the settings for a client application to use a specific AKM service, or role. A fully initialized AkmConfig object can be created by passing an XML configuration file argument to the static getConfigInstance() method of the AkmConfigParser class.

An uninitialized AkmConfig object can be created with a constructor, and then properties set for server addresses and ports, timeout periods, and keystore and truststore details. The application developer is responsible for creating a logically coherent set of values.

The XML configuration format allows more than one client role to be described. A role can be specified when initializing an AkmConfig object from XML, by passing a string argument along with the XML filename. If no role argument is supplied, the first clientrole element found in the XML file will be used.

Once initialized, an AkmConfig object could be repurposed to a different role using the setRole() and setPort() methods, making sure that the new port corresponds to the new role. If different certificates are in use for different roles, it may be necessary to also reset the client keystore.

However, an AkmConfig object is lightweight, and the simplest way to support two roles in the same application would be to create two AkmConfig objects.

As seen in the next chapter, request objects can be directly created with an XML file instead of first creating a config object. One reason to create a config object separately is if an application needs to create multiple request objects without configuring each one.

 

Chapter 7: Using the AkmRequest Classes

The API is organized into two packages: com.townsendsecurity.akmcore, and com.townsendsecurity.akmkeys.

The akmcore package implements common functionality for configuration, connection management, error and exception handling. The akmkeys package implements key retrieval and remote encryption services.

While akmkeys uses and depends on classes in akmcore, the reverse is not true; the classes in akmcore do not depend on akmkeys.

Several classes must be imported from the com.townsendsecurity.akmcore and com.townsendsecurity.akmkeys packages. Although wildcard import statements could import all classes, this is not a good practice, and can obscure subtle programming errors. Explicit imports are shown here.

All AKM requests may throw AKM Exceptions, which must be imported:

import com.townsendsecurity.akmcore.AkmException;
import com.townsendsecurity.akmcore.AkmConfigException;
import com.townsendsecurity.akmcore.AkmConnectException;

To retrieve keys, an application must import the key retrieval classes:

import com.townsendsecurity.akmkeys.AkmKeyRequest;
import com.townsendsecurity.akmkeys.AkmSymKey;
import com.townsendsecurity.akmkeys.AkmRsaKey;

To create AkmRequest and AkmConfig objects from XML, AkmConfig and AkmConfigParser classes must also be imported:

import com.townsendsecurity.akmcore.AkmConfig;
import com.townsendsecurity.akmcore.AkmConfigParser;

Key retrieval

The simplest operation using the AKM SDK is to retrieve a key, which can then be used to encrypt or decrypt user data.

Key retrieval is implemented by the AkmKeyRequest object, which is a subclass of AkmRequest. An AkmKeyRequest object must be created with a configuration, either by using the AkmKeyRequest constructor with an AkmConfig argument, or by calling a class getInstance() method with an XML configuration filename argument.

For example, if an XML configuration file called “jakmcfg.xml” is located in the same directory as the application’s main class, a key request object can be created in one step:

try {
	AkmKeyRequest keyRQ = AkmKeyRequest.getInstance("jakmcfg.xml");
	// use the key request object... 
}
catch (AkmException ax) {
	// handle the exception
}

The getInstance() method may throw an exception if the configuration file is not found or cannot be opened, or if it is not well-formed. The filename parameter can be an absolute or relative path.

Once the key request object is created, a key can retrieved by specifying either a key name or key instance value, or both. For symmetric keys, if the instance is not specified, the default or current key instance is retrieved. Asymmetric keys have a single instance per keytype (public or private).

AKM returns the key and its associated data as a byte buffer, which must then be parsed for individual fields, based on the AKM protocol. Several helper methods are provided to parse the response.

For most uses, an application can retrieve an object that wraps and manages the key. Symmetric keys can be retrieved as an AkmSymKey object, and RSA keys can be retrieved as AkmRsaPublicKey or AkmRsaPrivateKey objects.

The keyclient API does not cache retrieved keys; applications may use any of a variety of available Java caching libraries to implement caching if desired. Also, the API does not attempt to secure the memory holding the key value. Again, applications may add such functionality.

Symmetric key retrieval and usage are shown here:

try{
	AkmKeyRequest keyRQ = AkmKeyRequest.getInstance(“jakmcfg.xml”);
AkmSymKey symkey = keyRQ.retrieveSymKey(sKey, sInstance);
		
	String sKeyRollover = symkey.getKeyRollover();
	String sKeyExpiration = symkey.getKeyExpiration();

	int keyByteLen = symkey.getKeyByteSize();
	byte[] keybytes = symkey.getKeyBytes();
}
catch (AkmException ax){ 
		// handle exception
}

Possible exceptions may be an AkmConnectException for socket or TLS problems, or a generic AkmException for errors specific to the AKM service and protocol, such as an invalid key or instance is specified, or the key is expired. See Chapter 9: Exceptions and Error Codes for more details.

An example program that retrieves a key and uses it to encrypt and decrypt data is included with the SDK, and shown in Chapter 8: Sample Code.

The model is similar for asymmetric keys;

try{
	AkmKeyRequest keyRQ = AkmKeyRequest.getInstance(“jakmcfg.xml”);
AkmRsaPublicKey akmpubkey = keyRQ.retrieveRsaPublicKey(sKeyName, “”);
int keysize = akmpubkey.getKeyBitSize();  // 1024, 2048, etc
   	PublicKey pubkey = akmpubkey.getKey();
}
catch (AkmException ax){ 
		// handle exception
}

Or

try{
	AkmKeyRequest keyRQ = AkmKeyRequest.getInstance(“jakmcfg.xml”);
AkmRsaPrivateKey akmprivkey = keyRQ.retrieveRsaPrivateKey(sKeyName, “”); 	
int keysize = akmpubkey.getKeyBitSize(); // 1024, 2048, etc
PrivateKey privkey = akmprivkey.getKey();
}
catch (AkmException ax){ 
		// handle exception
}

A notable difference between symmetric and asymmetric key retrieval is that the application will retrieve either a public or private key into different Akm objects.

Another difference is that, while the Akm symmetric key object which wraps a buffer of bytes containing the key value, the Akm asymmetric key objects wrap standard Java PublicKey or PrivateKey objects, Applications can use these objects with standard Java asymmetric encryption APIs. See the related asymmetric samples in Chapter 8: Sample Code.

Remote encryption services

AKM provides remote encryption services to encrypt or decrypt data on the server. With remote encryption services, the key is not sent to the client, and the key does not ever need to be present on the client.

Remote encryption services with symmetric keys operate on up to 16,272 bytes of data per request. To encrypt larger amounts of data, the client application must manage buffering the data.

Remote encryption with RSA keys is also supported, subject to the limitations of RSA keys: the encrypted data is related to the size of the key, and asymmetric encryption is significantly slower than symmetric.

A 1024 bit RSA key can encrypt up to 86 bytes of plaintext; a 2048 bit key: up to 214 bytes; a 3072 bit key: up to 342 bytes, and a 4096 bit key: up to 470 bytes.

When encrypting with RSA public keys, the size of the resulting ciphertext does not depend on the length of the plaintext. The ciphertext size is equal to the size of the RSA key, in bytes (thus 2014 bit key yields 128 bytes of ciphertext, etc).

A common use of RSA encryption is to encrypt a symmetric key, which can be used to encrypt plaintext data of any length.

Remote encryption services involve a tradeoff between performance and other considerations, such as ease of use and security. Applications using the remote encryption services never have to retrieve, store and secure a key on the client, nor include code to do the encryption. However, remote encryption will usually not be as fast as local encryption on the client.

If using the same key frequently, or encrypting large amounts of data, it will generally be more efficient to retrieve the key once and perform cryptographic operations locally. However, if encrypting or decrypting small data items infrequently, remote encryption services may be a better fit.

The AkmEncryptRequest and AkmDecryptRequest objects are used to encrypt or decrypt data using the remote encryption services. Similar to key requests, the encryption service request objects are created directly from an XML configuration file, or using an existing AkmConfig object.

The client role of encryption must be configured to use remote encryption service objects. If using an XML configuration file, setting the clientrole element will cause the correct AKM service port to be used. If configuring in code, the application must set both the the role and the port explicitly.

To use AKM’s remote encryption service, an application must import the applicable classes:

import com.townsendsecurity.akm.AkmEncryptRequest; 
import com.townsendsecurity.akm.AkmDecryptRequest; 

Either type of encryption request object can be created in one step from an XML configuration file, in the same manner as a key request object. For example:

try {
	AkmEncryptRequest encrRQ = AkmEncryptRequest.getInstance("jakmcfg.xml");
	// encrypt some data …
}
catch (AkmException ax) {
	// handle the exception
}

Using symmetric key API calls

Both local and remote encryption with symmetric keys can use either ECB mode or CBC mode. ECB mode is a simple bitwise substitution that does not hide patterns or structure in the data; CBC mode is more secure.

CBC mode requires an additional parameter: an “initialization vector” (IV). The IV does not have to secret, but must be random, and should not be reused.

To encrypt up to 16,272 bytes, using AKM’s remote encryption service:

String sKey = "AESKEY"; 	// name of a key managed by AKM
String sInstance = "";    	//  use the default instance 
byte[] iv;			// must initialize with random value
byte[] plaintext = "what a wonderful world".getBytes();
byte[] ciphertext = encrRQ.encryptCBC(sKey, sInstance, iv, plaintext);	

The ciphertext buffer returned will be padded to a multiple of 16 bytes. To initialize an IV, the Java SecureRandom class can be used:

java.security.SecureRandom rand = new java.security.SecureRandom();
iv  = new byte[16]; 
rand.nextBytes(iv);

The same IV that was used to encrypt data must be used to decrypt it; a common technique prepends the IV to the ciphertext after encryption, then extracts the IV prior to decryption.

For example, assuming the above code was executed:

byte[] cipherdata = new byte[iv.length + ciphertext.length];
System.arraycopy(cipherdata, 0, iv, 0, iv.length);
System.arraycopy(cipherdata, iv.length, ciphertext, 0, ciphertext.length);

The cipherdata buffer will be broken apart before decrypting, as shown below.

To decrypt the data encrypted by the above steps, the first step to create a decrypt request:

try {
	AkmDecryptRequest decrRQ = Request.getInstance("jakmcfg.xml");
	// decrypt data …
}
catch (AkmException ax) {
		// handle the exception
}

Using the same key and instance:

String sKey = "AESKEY"; // name of a key managed by AKM
String sInstance = "";  // will use the default instance 

Extract the IV from the cipherdata:

byte iv[] = new byte[16];
System.arraycopy(iv, 0, cipherdata, 0, iv.length);
byte ciphertext[] = new byte[cipherdata.length - iv.length];
System.arraycopy(ciphertext, 0, cipherdata, iv.length, ciphertext.length);

The decrypt request object’s decryptCBC() method returns a new byte array of plaintext (decrypted) data:

byte[] plaindata = decrRQ.decryptCBC(sKey, sInstance, iv, ciphertext);

Instance considerations with symmetric keys

Symmetric keys in AKM may have multiple instances, which change when a key is rolled over. If no instance value is supplied in a key request, AKM returns a default, or current, instance.

If the encryptECB() or encryptCBC() methods are called with a blank instance parameter, the current instance will be used, and the calling code must keep track of that instance separately, since it could change due to key rollover.

The encryptECB() or encryptCBC() methods can be called with an empty byte buffer parameter that will be populated by the actual instance used; an Applications should store the instance used as well as the keyname for later decryption.

A small code snippet demonstrates encrypting with the default instance and saving the actual instance used, then decrypting:

byte[] iv;   //  …  initilize the iv with random values			
byte[] instanceUsed = newbyte[24]; // akm instances are always 24 bytes 
byte[] ciphertext = encrRQ.encryptCBC("AES128", “”, iv, "what?", instanceUsed);


// convert the returned instance to string; akm instances are base64 ascii
String sInstance = new String(instanceUsed, Charset.forName("“ISO_8859_1"));
byte[] plaindata = decrRQ.decryptCBC(sKey, sInstance, iv, ciphertext);

Complete example programs are shown and described in Chapter 8, and included in the SDK.

 

Chapter 8: Sample Code

Sample programs are shown to demonstrate how to retrieve a key and use it to encrypt data, or encrypt data on the server without needing the key locally.

These samples are included with the SDK as Java source files. These files are for illustration purposes and should not be regarded as production code.

Compiling and running samples

To compile and run the samples, or any application using the AKM SDK for Java, the Java classpath must include akmkeys.jar and log4j.jar.

For example, if the jar files are in the parent directory of the samples directory, relative paths could be specified to compile the “AkmEncryptLocal” sample. On Linux:

javac -cp ../akkmeys.jar:. AkmEncryptLocal.java

To run the sample, both the akmkeys and log4j jar files must be explicitly referenced:

java -cp ../log4j.jar:../akmkeys.jar:. AkmEncryptLocal

On Windows, semicolons would separate the classpath elements instead of colons, and backslashes used within classpath elements, thus, to compile:

javac -cp ..\akmkeys.jar;. AkmEncryptLocal.java

To run the sample, both the akmkeys and log4j jar files must be explicitly referenced:

java -cp ..\log4j.jar;..\akmkeys.jar;. AkmEncryptLocal

If the jar files are in the same directory as the sample programs, the jar files can simply be referenced without any path. To compile on Linux:

javac -cp akmkeys.jar:. AkmEncryptLocal.java

To run:

java -cp log4j.jar:akmkeys.jar;. AkmEncryptLocal

To compile and run on Windows, use semicolons to separate elements in the classpath.

A logging configuration file used by Log4j should also be present on the classpath; this can be either “log4j.xml” or “log4m.properties”.

Note that the current directory is referenced at the end of the classpath.

The SDK includes a file, “samples.build-run.txt”, that details compiling and running application on the command line.

A build file for use with Apache Ant, samples.build.xml, is also included; this Ant script can build the samples and run the local encryption sample. Until certificates and keystores are configured and the configuration file edited accordingly, running the sample will return a connection error. However, it does provide a quick verification that the sample built, and that the classpaths are correct.

Local symmetric encryption sample

The following complete program retrieves a key from AKM and uses it to encrypt, then decrypt some text, using the Java cryptographic API’s.

Command line arguments supply the text to encrypt and decrypt, and a symmetric keyname. The default instance for the supplied key is used. The sample uses a configuration file called “jakmcfg.xml” in the local directory.

Errors reading or parsing the XML file cause an AkmConfigException to be thrown. The sample prints out some diagnostic information captured by the Exception, and quits.

The request object’s primary public method, retrieveSymKey() is called with the key and an empty string, to retrieve the default instance. Any problems establishing a secure connection will throw an AkmConnectException, with diagnostic information captured from the point that the exception occurred.

It is possible to successfully connect to AKM and get a response that indicates an AKM logic error, such as the key is not found or is expired. That will throw an AkmException, with the specific AKM error message.

Once the AkmSymKey object is returned, the actual bytes of the key can be obtained with the getKeyBytes() method. The key bytes are passed directly to the constructor of the helper class EncryptDecryptCBC, which uses standard Java cryptography API’s to encrypt or decrypt data.

The helper class is limited to AES symmetric encryption in CBC mode, with PKCS#5 padding. It is not meant to be a complete production tool, but just to illustrate the basic techniques involved.

There is a comment in the program about key strength. U.S. law places limits on the strength of cryptographic libraries and components for export to certain countries. By default, the Java SE JDK from Oracle limits symmetric key sizes to 128 bits to conform to these limits.

See the links in the comment for the “unlimited strength” downloads for Java 7 and Java 8, available for users in most countries.

/*
* AkmLocalEncrypt: retrieve a symmetric key from AKM and use it to encrypt/decrypt data
* uses default key instance and configuration file "jakmcfg.xml" in current directory
*/


import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
 
import com.townsendsecurity.akmcore.AkmException;
import com.townsendsecurity.akmcore.AkmUtil;
import com.townsendsecurity.akmcore.AkmRequest;


import com.townsendsecurity.akmkeys.AkmKeyRequest;
import com.townsendsecurity.akmkeys.AkmSymKey;


public class AkmLocalEncrypt
{
    static final String AKM_DEFAULT_INSTANCE = "";
    static final String AKM_DEFAULT_CONFIGFILE = "jakmcfg.xml";


    public static void main(String args[])
    {
   	 String sCfgFile = AKM_DEFAULT_CONFIGFILE;
   	 String sInstance=AKM_DEFAULT_INSTANCE;
   	 String sKey = "";
   	 String plaintext = "";


   	 if (args.length < 2)
   	 {
   		 System.out.println("usage: AkmLocalEncrypt plaintext keyname");
   		 return;
   	 }
   	 switch (args.length)
   	 {
   		 default:
   		 System.out.println("ignoring extra arguments");
   		 // fall through


   		 case 2:
   		 sKey = args[1];
   		 // fall through


   		 case 1:
   		 plaintext = args[0];
   		 break;
   	 }


 System.out.println(“AkmRemoteEncrypt: Remote encryption service with symmetric key”); 
   	 System.out.println("plaintext: "" + plaintext);
   	 System.out.println("symmetric keyname: " + sKey);
   
   	 try
   	 {
   		 AkmKeyRequest keyRQ = AkmKeyRequest.getInstance(sCfgFile);
   		 AkmSymKey symkey = keyRQ.retrieveSymKey(sKey, sInstance);


   		 byte[] ciphertext = null;
   	EncryptDecryptCBC cryptor = 
new EncryptDecryptCBC(symkey.getKeyBytes());
   		 ciphertext = cryptor.encryptSymmetric(plaintext.getBytes());


   		 System.out.println("resulting ciphertext as hex:");
   		 System.out.println(AkmUtil.bytesToHex(ciphertext));
   		 System.out.println();


   		 System.out.println("decrypt ciphertext");
   		 byte[] plainbuf = null;
   		 plainbuf = cryptor.decryptSymmetric(ciphertext);
   		 System.out.println("Decrypted plain text:");
   	 System.out.println(new String(plainbuf));
}
   	 catch (AkmException ax)
   	 {
   		 System.out.println("" + ax.getAkmErrMsg());
   		 return;
   	 }
   	 catch (Exception ex)
   	 {
   		 ex.printStackTrace();
   		 return;
   	 }


   	 return;
    }
}




/* helper class for symmetric encryption/decryption CBC mode; not production worthy
 * hardcoded 128 bit symmetric key, initialization vector of zero bytes
 * key size limited to 128 bit unless strong key policy files are installed:
 * http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
 * http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
 */
class EncryptDecryptCBC
{
    static final String ALGORITHM_CBC = "AES/CBC/PKCS5PADDING";
    
    byte[] keyBuf;
    byte[] iv;
    String algorithm;


	
    public EncryptDecryptCBC(byte[] key)
    {
   	keyBuf = key;
   	algorithm = ALGORITHM_CBC;
java.security.SecureRandom rand = 
new java.security.SecureRandom();
   	 iv  = new byte[16];
   	 rand.nextBytes(iv);
    }




    public EncryptDecryptCBC(byte[] key, byte[] ivbytes)
    {
   	 this(key);
   	 iv = ivbytes;
    }


     // create cipher obj for aes/cbc from key and iv; mode is encr/decr
    public Cipher initCipher(byte[] keybytes, byte[] ivbytes, int opmode) throws Exception
    {
   	 Cipher aCipher = null;
   	 IvParameterSpec ips = new IvParameterSpec(ivbytes);
   	 SecretKeySpec keyspec = new SecretKeySpec(keybytes, "AES");
    	 aCipher = Cipher.getInstance(algorithm);
   	 aCipher.init(opmode, keyspec, ips);
   	 
   	 return aCipher;
    }


    // encrypt with symmetric key; returns ciphertext as new byte array
    public byte[] encryptSymmetric(byte[] plainText) throws Exception
    {
   	 byte[] cipherText = null;
   	 Cipher cipher = initCipher(keyBuf, iv, Cipher.ENCRYPT_MODE);
   	 cipherText = new byte[cipher.getOutputSize(plainText.length)];
   	 int ctLength = cipher.update(plainText, 0, plainText.length, cipherText, 0);
   	 ctLength += cipher.doFinal(cipherText, ctLength);


   	 return cipherText;
    }


    // decrypt with symmetric key; returns plaintext as new byte array, or null
    public byte[] decryptSymmetric(byte[] cipherText) throws Exception
    {
   	 byte[] plainText=null;


   	 Cipher cipher = initCipher(keyBuf, iv, Cipher.DECRYPT_MODE);
   	 plainText = new byte[cipher.getOutputSize(cipherText.length)];   	 int ptLength = cipher.update(cipherText, 0, cipherText.length, plainText, 0);


   	 ptLength += cipher.doFinal(plainText, ptLength);
    
   	 return plainText;
    }


    // create cipher obj for aes/cbc from key and iv; mode is encr/decr
    public Cipher initCipher(byte[] keybytes, byte[] ivbytes, int opmode) throws Exception
    {
   	 Cipher aCipher = null;
   	 IvParameterSpec ips = new IvParameterSpec(ivbytes);
   	 SecretKeySpec keyspec = new SecretKeySpec(keybytes, "AES");
    	 aCipher = Cipher.getInstance(algorithm);
   	 aCipher.init(opmode, keyspec, ips);
   	 
   	 return aCipher;
    }
    
}

Remote symmetric encryption sample

The following complete program submits data to AKM’s remote encryption services using symmetric keys.

The program accepts command line arguments for text to encrypt and decrypt, and symmetric keyname. The default instance for the key is used.

The default XML configuration file is the same as the local encryption example, but for a different client role.

Instead of directly creating AkmRequest objects from the XML file, the getConfigInstance() static method of the AkmConfigParser class creates an AkmConfig object to then create both encrypt and decrypt request objects.

Calling the getConfigInstance() method of AkmConfigParser with the xml filename and the client role of “encryption” creates an AkmConfig object.

The AkmEncryptRequest object’s encryptCBC() method sends key and instance values to AKM, along with the data to encrypt, and a random initialization vector. The same initialization vector will be needed to later decrypt the data.

/* example using remote encryption service -- cbc mode */


import java.nio.charset.Charset;


import com.townsendsecurity.akmcore.AkmConfig;
import com.townsendsecurity.akmcore.AkmConfigParser;
import com.townsendsecurity.akmcore.AkmException;
import com.townsendsecurity.akmcore.AkmUtil;


import com.townsendsecurity.akmkeys.AkmEncryptRequest;
import com.townsendsecurity.akmkeys.AkmDecryptRequest;


public class AkmRemoteEncrypt
{
    static final String AKM_DEFAULT_INSTANCE = "";
    static final String AKM_DEFAULT_CONFIGFILE = "jakmcfg.xml";


    public static void main(String args[])
    {
   	 String sKey = "";
   	 String sInstance=AKM_DEFAULT_INSTANCE;
   	 String sCfgFile = AKM_DEFAULT_CONFIGFILE;
   	 String plaintext = "";
   	 


if (args.length < 2)
   	{
   		 System.out.println("usage: AkmRemoteEncrypt plaintext keyname [instance]");
   		 return;
   	}


    	switch (args.length)
   	{
   		 default:
   		 System.out.println("ignoring extra arguments");
   		 // fall through


   		 case 2:
   		 sKey = args[1];
   		 // fall through


   		 case 1:
   		 plaintext = args[0];
   		 break;
   	}


System.out.println(“AkmRemoteEncrypt: Remote encryption with symmetric key”); 	 System.out.println("plaintext: "" + plaintext);
   	System.out.println("symmetric keyname: " + sKey);


try
{   		 
   	 // get an AkmConfig object for encryption service
   	 AkmConfig cfg = AkmConfigParser.getConfigInstance(sCfgFile, 
"encryption");
   	 // construct encrypt request object with config
   	 AkmEncryptRequest encrRQ = new AkmEncryptRequest(cfg);
   		 
   	 // generate a random initialization vector; use same iv to decrypt
   	 java.security.SecureRandom rand = 
new java.security.SecureRandom();
   	 byte[]  iv  = new byte[16];
   	 rand.nextBytes(iv);


   	 byte[] instanceUsed = new byte[24]; // instance always 24 bytes
   	 Byte[] ciphertext = encrRQ.encryptCBC(sKey, sInstance,
 iv, plaintext.getBytes(), instanceUsed);


   	 System.out.println("ciphertext as hex:");
   	 System.out.println(AkmUtil.bytesToHex(ciphertext));
   	 System.out.println();


   	 // create remote decrypt request with same cfg used to encrypt
   	 AkmDecryptRequest decrRQ = new AkmDecryptRequest(cfg);


   	 // convert the used instance to string; akm instances are base64 ascii
   	 String sEncryptInstance = new String(instanceUsed,
 						Charset.forName("ISO_8859_1"));
   	 Byte[] plaindata = decrRQ.decryptCBC(sKey, sEncryptInstance,
 iv, ciphertext);


   	System.out.println("decrypted plaintext:");
   	System.out.println(new String(plaindata));
   }
   catch (AkmException ax) // catch akm exceptions, access akm error methods
   {
   	 System.out.println("akm err" + ax.getAkmErr() + ": "
 + ax. getAkmErrMsg());
   }
   catch (Exception ex) // catch runtime exceptions
   {
  	 ex.printStackTrace();
   }


   return;
    }// end main
}// end class

Local asymmetric encryption sample using RSA keys

Local encryption with RSA (asymmetric) keys is similar to with symmetric keys, but one difference is that two keys are retrieved, a public key to encrypt, and a private key to decrypt. The AkmKeyRequest object has methods to retrieve RSA keys as objects which wrap standard Java RSA key objects.

The usage model for configuration is the same as with symmetric keys, an AkmConfig object is created, then reused to retrieve both RSA key objects. Like the symmetric examples, a local configuration file “jakmcfg.xml” is used; however both the file name and location (path) are arbitrary.

Unlike the symmetric key encryption example, there is no initialization vector to keep track of. Since AKM asymmetric keys only have a single instance per key, there is no default instance to keep track of. While RSA keys can be specified by instance values or names, the examples show names only.

Another difference from symmetric encryption is that the AkmRsaKey objects retrieved do not expose the key value, but instead provide getter methods to use native Java RSA key objects. The application then uses the Java PublicKey and PrivateKey objects as shown.

The padding mode defaults to OAEP, but PKCS1 can be used instead.

/* retrieve rsa keys from AKM and use to encrypt/decrypt data  */
import javax.crypto.Cipher;
import java.security.PublicKey;
import java.security.PrivateKey;


import com.townsendsecurity.akmcore.AkmException;
import com.townsendsecurity.akmcore.AkmUtil;


import com.townsendsecurity.akmkeys.AkmKeyRequest;
import com.townsendsecurity.akmkeys.AkmRsaPublicKey;
import com.townsendsecurity.akmkeys.AkmRsaPrivateKey;


public class AkmLocalEncryptRsa
{
    public static final String PADDINGMODE_OAEP     = "OAEP";
    public static final String PADDINGMODE_PKCK1     = "PCKS1";
    public static final String TRANSFORM_OAEP  = "RSA/ECB/OAEPWithSHA1AndMGF1Padding";   
    public static final String TRANSFORM_PKCS1  = "RSA/ECB/PKCS1Padding";


    public static void main(String args[])
    {
   	 String sPaddingMode = PADDINGMODE_OAEP;  // default oaep padding
   	 String sKey = "";
   	 String sCfgFile = "jakmcfg.xml";
   	 String plaintext = "";


   	 if (args.length < 2)
   	 {
   		 System.out.println("usage: AkmLocalEncryptRsa plaintext keyname [paddingmode]");
   		 return;
   	 }


   	switch (args.length)
    	{
   	 	default:
   	 	System.out.println("ignoring extra arguments");
   	 	// fall through


   	 	case 3:
   	 	sPaddingMode = args[2];
   	 	// fall through


   	 	case 2:
   	 	sKey = args[1];
   	 	// fall through


   	 	case 1:
   	 	plaintext = args[0];
   	 	break;
   	 }


System.out.println(“AkmRemoteEncrypt:Local encryption with retrieved RSA keys”); 	 System.out.println("plaintext: "" + plaintext);
   	System.out.println("symmetric keyname: " + sKey);


try
{
   	AkmKeyRequest keyRQ = AkmKeyRequest.getInstance(sCfgFile);
AkmRsaPublicKey akmpubkey = keyRQ.retrieveRsaPublicKey(sKey,””); PublicKey PublicKey pubkey = akmpubkey.getKey();


   	String sTransform;
   	 if (sPaddingMode.equals(PADDINGMODE_OAEP))
   		 sTransform = TRANSFORM_OAEP;
else
   		 sTransform = TRANSFORM_PKCS1;


   	 Cipher cipher = Cipher.getInstance(sTransform);
   	 cipher.init(Cipher.ENCRYPT_MODE, pubkey);
   	 Byte[] ciphertext = cipher.doFinal(plaintext.getBytes());


   	 System.out.println("ciphertext as hex:");
   	 System.out.println(AkmUtil.bytesToHex(ciphertext));
   	 System.out.println();


   	 AkmRsaPrivateKey akmprivkey = keyRQ.retrieveRsaPrivateKey(sKey, “”);    		 	 PrivateKey privkey = akmprivkey.getKey();


   	 Cipher cipher2 = Cipher.getInstance(sTransform);
   	 cipher2.init(Cipher.DECRYPT_MODE, privkey);
   	 Byte[] plainbuf = cipher2.doFinal(ciphertext);
   	 System.out.println("decrypted plain text:");
   	 System.out.println(new String(plainbuf));
}
 	catch (AkmException ax)
{
System.out.println("" + ax.getAkmErrMsg());
 }
 catch (Exception ex){ ex.printStackTrace();}


 return;
    }// end main


}// end class


Remote RSA encryption sample

This example program submits data to AKM’s remote encryption services using RSA keys.

Like the other examples, the program accepts command line arguments for text to encrypt and decrypt, and a keyname. An optional argument may override the default OAEP padding mode (see next section).

Like the remote encryption with symmetric key example, an AkmConfig object is created from an XML configuration file which sets the client role to the value “encryption” rather than “keyRetrieval”.

An AkmEncryptionRequest object is constructed with the config object, and its encryptWithRsaPublic() method is called with the plaintext to encrypt, returning the encrypted data (ciphertext) as a byte array.

The same config object is used to construct an AkmDecryptionRequest object, and its decryptWithRsaPrivate() method is called to decrypt the ciphertext.

Given the size limitations of using RSA keys discussed in a previous section, and the relatively slow performance compared to symmetric encryption, the RSA encryption tends to be used for small amounts of data, infrequently encrypted and decrypted.

Thus, the simpler programming model for remote encryption may often be preferred over retrieving and encrypting locally for RSA keys.

/* sample demonstrating remote encryption service with rsa keys*/


import com.townsendsecurity.akmcore.AkmConfig;
import com.townsendsecurity.akmcore.AkmConfigParser;
import com.townsendsecurity.akmcore.AkmException;
import com.townsendsecurity.akmcore.AkmUtil;
import com.townsendsecurity.akmcore.AkmRequest;


import com.townsendsecurity.akmkeys.AkmEncryptRequest;
import com.townsendsecurity.akmkeys.AkmDecryptRequest;


public class AkmRemoteEncryptRsa
{
    static final String AKM_DEFAULT_INSTANCE    	 = "";
    static final String AKM_DEFAULT_CONFIGFILE     = "jakmcfg.xml";
    
    static final String PADDINGMODE_OAEP    = "OAEP";
    static final String PADDINGMODE_PKCK1    = "PCKS1";


    public static void main(String args[])
    {
   	 String plaintext     = "";
   	 String sKey    	 = "";
   	 String sPaddingMode = AkmRequest.AKM_PADDING_OAEP;
   	 String sInstance    = AKM_DEFAULT_INSTANCE;
   	 String sCfgFile     = AKM_DEFAULT_CONFIGFILE;
    
   	 if (args.length < 2)
   	 {
   		 System.out.println("usage: AkmRemoteEncryptRsa plaintext keyname [paddingmode]");
   		 return;
   	 }
	
   	 switch (args.length)
   	 {
   	 	default:
   	 	System.out.println("ignoring extra arguments");
   	 	// fall through


 	case 3: // optionally override oaep with pkcs1
if (args[2].equals(PADDINGMODE_PKCK1))
   			sPaddingMode = AkmRequest.AKM_PADDING_PKCS1;
   	 	// fall through


   	 	case 2:
   	 	sKey = args[1];
   	 	// fall through


   	 	case 1:
   	 	plaintext = args[0];
   	 	break;
   	 }


System.out.println(“AkmRemoteEncrypt:Remote encryption with RSA keys”);
System.out.println("plaintext: "" + plaintext);
System.out.println("Rsa keyname: " + sKey);


try
    	{
   		 // get an AkmConfig object for encryption service
   		 AkmConfig cfg = AkmConfigParser.getConfigInstance(sCfgFile, "encryption");
   		 
   		 // construct encrypt request object with config
   		 AkmEncryptRequest encrRQ = new AkmEncryptRequest(cfg);
   		 byte[] ciphertext =  encrRQ.encryptWithRsaPublic(sKey, sInstance,
 					plaintext.getBytes(), sPaddingMode));
   		 System.out.println("ciphertext as hex:");
   		 System.out.println(AkmUtil.bytesToHex(ciphertext));
   		 System.out.println();


   		 // create remote decrypt request with same cfg used to encrypt
   		 AkmDecryptRequest decrRQ = new AkmDecryptRequest(cfg);
   		 byte[] plaindata = decrRQ.decryptWithRsaPrivate(sKey, sInstance,
ciphertext, sPaddingMode);
   		 System.out.println("decrypted plaintext:");
   		 System.out.println(new String(plaindata));
   		 System.out.println();
   	 }
 catch (AkmException ax)
   	 {
   		 System.out.println("akm err" + ax.getAkmErr() + ": " + ax. getAkmErrMsg());
   	 }
catch (Exception ex){ ex.printStackTrace();}


   	return;
    }// end main


}// end class

PKCS padding

Both the local and remote encryption examples use CBC mode with padding.

The AKM remote encryption services use PCKS#7 padding, and will always return ciphertext whose size is a multiple of 16 bytes. If the original plaintext is exactly 16 bytes, an additional 16 bytes of padding is added anyway. The server removes the padding when the entire ciphertext buffer is sent to be decrypted.

The local encryption example shows the value”PKCS5PADDING”, which is defined by the Java cryptographic libraries. Again, if the plaintext to encrypt is exactly 16 bytes, an additional 16 bytes of padding is added, and removed when decrypting.

As a practical matter, the ”PKCS5PADDING” algorithm as implemented in the SUN JCE provider is interoperable with PCKS#7 padding.

 

Chapter 9: Exceptions and Error Codes

Error conditions are reported as Exceptions thrown by the public API methods.

AkmException is a checked exception that may be thrown by the AKM SDK for errors reported by the server, and also incorrect client side usage. The AkmConfigException and AkmConnectException types are subclasses of AkmException and may be thrown for configuration or connection errors.

Applications may choose to catch all AKM exceptions as AkmException or catch the subclasses if more fine-grained handling is desired.

An AkmException contains AKM specific error code and message fields. These two categories of information can be retrieved separately, using the Java base Exception method getMessage() and the AkmException method getAkmErrMsg().

Most unchecked (runtime) exceptions are not caught by the SDK, if they represent unrecoverable situations. Applications using the SDK can report or handle runtime exceptions in their own context.

The following error codes and messages may be reported by the akmkeys API by exceptions and in log files (note that some errors may be reported by different error codes depending on the request being processed; if debug logging is enabled, the request type and other context can be seen in the log):

Code Message Comment
402 “TLS read failed” An error trying to read from the server after initial connected. TLS session may have timed out, or other network problem occurred. Attempt to resubmit the request.
403 “TLS write failed” An error trying to write to the server after TLS connected. TLS session may have timed out, or other network problem occurred. Attempt to resubmit the request.
404 “TLS connect failed” Client connected to server, but the TLS handshake failed. Review certificate and CA details, aliases, if used.
406 “TLS init fail” Could not initialize TLS prior to attempting handshake. Review keystore/truststore paths, pwds match as configured. See also: errs 5706, 5707
407 “TCP connect timeout” The client timed out connecting to a server, before trying to establish a TLS session; if failover servers were configured, this will show up in a log for each server that could not be reached see also: err 5074
420 “unspecified AKM protocol error” The response received from AKM is malformed and violates the AKM protocol. The debug log may contain context information
421 “did not receive full response length header” The response length header was not correctly or fully received; the remainder of the AKM response may not be read. This is a network or connectivity problem. Best action is to resubmit request.
422 “received incomplete response from AKM” The full response was not received from AKM; could be caused by network delay or loss of connectivity. Resubmit request.
423 “one or more required parameters missing” A request was sent without all required parameters.
3002 “invalid Key Instance parameter” The key instance parameter is too long (over 24 chars) or contains invalid (non-base64) characters.
**3113 4130 ** “key not activated” requested key has not been activated and cannot be used.
3114 “key is expired” requested key is expired, AKM does not return expired keys.
3115 4132 “key has been revoked by admin, cannot use key” requested key has been revoked; AKM will not return a revoked key.
3248 “no key found for instance” No key was found on the server matching the instance and key name.
**3275 3440 3608 ** “no key found for keyname” No key on the server matching the name.
3765 “AKM server license expired” The license for the AKM server has expired; the remedy is to apply and new license and restart AKM.
3391 3610 “key access denied” the user or group is not authorized for the key.
3393 “AKM refused TLS connection, bad CN or OU” Network and TLS connection can be made, but AKM could not recognize the CN or OU fields in the client certificate; only supported values for these fields can connect. See also: AKM User Guide
3999 “cannot import clear format key in PCIDSS mode” when AKM is configured for PCIDSS mode, keys must be imported in encrypted format
4475 “plaintext exceeds encryption request maximum (16,272)” A limit of 16,272 bytes can be encrypted in a single request. Applications must manage buffering larger amounts.
4470 “ciphertext exceeds decryption request maximum (16,272)” Only 16,272 bytes or less can be decrypted in a single request. Applications must manage buffering larger amounts.
5005 “invalid KeyName parameter” The keyname argument was not well formed, perhaps too long (over 40 chars)
5007 “invalid cfg file” There was a problem parsing the config file, see the debug log for more detail.
5010 “error opening cfg file” Check config file name, path, permissions
5074 “TCP connect failure” A general failure to connect to one or more AKM servers for any network problem. See also: err 407
5089 “Unable to connect to any server” Connection attempts failed for the primary AKM server and all failover servers. Each failed connection is in a separate log entry. This error indicates a failure to connect to any server, perhaps due to network issues in the client environment.
5706 “could not initialize client keystore” The client keystore could not be initialized; this could be a problem accessing the JKS file, either because it is missing, misspelled, or access is denied or other file error. Other causes include an incorrect password, or missing certificate alias, or if using a JCE provider that does not support the “SunX509” algorithms.
5707 “could not initialize truststore” The truststore JKS file could not be read, or the JCE provider does not support “PKIX”.

 

Appendix A: Related Documentation

The following documents provide additional information on the installation and use of Alliance Key Manager:

General:

Platform specific deployment guides:

Encryption key management:

These and other resources are available on the AKM Supplemental.

 

Appendix B: Unlimited Strength Policy Files

The United States and other countries have legal regulations and limits on the strength of cryptographic components, and their export and import. By default, the Oracle JDK limits the strength of cryptographic algorithms, as documented here:

Subject to applicable laws, users may be able to download “policy” files from Oracle, and install them to their Java Runtime Environment (JRE), to enable unlimited strength algorithms:

The downloads are ZIP files containing “policy” jar files. Extract and copy local_policy.jar and US_export_policy.jar to the $JAVA_HOME/jre/lib/security directory (on Windows: %JAVA_HOME%\ jre7\lib\security).

The location of JAVA_HOME will vary based on where the JDK is installed. It will usually be the root directory of the Java Development Kit installation. A public Java Runtime Engine (JRE) is often installed on end user systems, with a corresponding jre/lib/security directory.

 

Appendix C: Certificate Usage Overview

Configuring AKM Certificates

A key retrieval client must connect to an AKM server with the TLS protocol. The TLS protocol is an internet standard that supersedes SSL and uses digital certificates for authentication and confidentiality.

TLS servers, such as web servers, identify themselves to clients by presenting a server certificate. The AKM server additionally requires that any client identify itself with a certificate.

The AKM client’s certificate must be signed by a Certificate Authority (CA) recognized by the AKM server. Similarly, the server certificate must be signed by a CA recognized by the client.

A CA certificate is either signed by another CA, or may be a self-signed CA, also called a root CA. Well-known commercial root CAs may be referred to a as public CAs, in contrast to a self-signed private CA maintained by an organization for its own use.

NOTE: A CA signs a certificate to indicate that the signed certificate belongs to the identity indicated by its contents. This does not mean that the holder of the signed certificate is necessarily trustworthy, merely that the identity information in the certificate has been verified by the CA that signed the certificate.

A private CA certificate can generally be trusted only by the organization that created and manages it. The certificates automatically created during AKM server initialization constitute a private CA and a private Public Key Infrastructure (PKI). The private CA and subsequent chain of trust should be carefully managed and not used for other purposes outside of AKM.

AKM private CA recommendation

Web browsers using https can connect with any server, whose identity may be established with a certificate signed by a well-known public certificate authority (CA), many of which are preconfigured by browsers to be trusted.

AKM networking takes place in a different context. The server is known in advance, and is under control of the deploying organization. AKM client applications do not communicate with other servers using the same network connections or credentials.

A private CA certificate would not usually be trusted outside of the organization which generated it, and web browsers normally won’t accept it. However, if both the server and all client applications are under one organization’s control, a private CA can be used securely for a dedicated purpose such as AKM communications.

Townsend Security recommends that AKM customers manage a private CA and a private certificate infrastructure. A full set of certificates, including private CA, server, and client certificates, are created during AKM server initialization. However, customers do not have to use the generated certificates, they can configure AKM and clients with certificates of their own choosing.

SECURITY ALERT: Certificate and private key files, regardless of origin, are crucial to securing the AKM key management system, and must be protected during creation, distribution, and storage.

These are sensitive files, and access to them should be controlled with the rights and permissions management capabilities of all platforms in use. All certificate and key files should be transferred with secure methods and tools.

If any certificates or private keys are compromised or lost, they should immediately be replaced, including the certificate authority on the AKM server and all client certificates in that chain of trust. See the AKM Certificate Manager Guide for more information.

Certificate formats and AKM

Certificates and private keys may be stored and transferred in a variety of formats.

The PKCS#12 format holds a certificate and its associated private key. Files in this format are often named with a .p12 or .pfx extension, and commonly used with Microsoft Windows.

Another common format for certificates and private key files is the .pem format, used by OpenSSL and other tools. The PEM file format can hold standalone certificates or keys, while the P12 format bundles certificates with keys in one file.

AKM initialization creates certificates and keys in all of the above formats, including JKS files. These generated JKS files can be used by referring to them in the configuration file as described in the XML Configuration section.

If needed, the AKM Certificate Manager application provided on the AKM Supplemental can be used to create a full set of certificates.

To use legacy certificates in any deployment, the Java keytool utility can be used to manage JKS keystores and truststores, including importing certificates from other formats. See Appendix D for keytool examples.

Certificate management in Java

Java applications use keystores to manage certificates. The native Java keystore format is a JKS file containing x509 certificates and private keys. A truststore is a keystore that holds trusted CA certificates. Both keystore and truststore JKS files are password protected.

A default truststore is installed with Java, with well-known public CA certificates. Although the default truststore can be used, Townsend Security recommends using the private truststore provided by AKM: a JKS file which holds only the CA certificates which are trusted by AKM client programs.

Keystores may contain certificates with private keys and trusted CA certificates, all of which are known as entries. A keystore entry is identified by an “alias”, a name that can be set when creating or importing an entry. Aliases can be viewed or changed with the keytool utility.

NOTE: While not required, aliases may be used by the AKM SDK for Java for explicit control of both client and server certificate selection. The optional use of aliases is described in Appendix E.

Client certificates, server certificates, and CA certificates have associated private keys, and those private keys should be carefully managed. Only CA certs themselves are stored in a Java truststore, but not their corresponding private keys.

While private keys may be distributed with client certificates, server private keys are only used on the server, with limited access, and should never be distributed. CA private keys must also be guarded, since they can be used to sign other certificates.

AKM certificate keystore options and aliases

Certificates can be managed in separate keystores, each containing one client certificate, or a single keystore can hold multiple certificates, with that keystore shared among several users.

The choice of keystore organization will depend upon circumstances, policies and resources of each user or organization. The AKM SDK for Java supports both options.

The AKM SDK for Java supports the use of keystores with more than one client certificate by configuring an alias for a specific client. The alias name configured for the client must match exactly one keystore entry in the keystore file used by that client.

A similar use of aliases supports server certificate pinning, in which the client configures a specific server certificate to trust, rather than trust any certificate signed by CA in the truststore.

Appendix E describes alias usage and certificate pinning.

Importing certificates created outside of AKM

If needed, certificates can be imported from a privately managed CA using standard certificate tools such as OpenSSL and the Java keytool utility.

See the AKM Certificate Guide for OpenSSL for information on creating and managing certificates using the OpenSSL command line tool, from the open source OpenSSL Project (https://openssl.org).

See Appendix D for information on importing certificates using the Java keytool.

 

Appendix D: Using Java Keytool

Java keystores can be managed using the keytool utility, included in the Java Development Kit (JDK) distribution. The complete keytool reference for Oracle Java 8 is online: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html.

Importing certificates to a Java keystore

Most keytool commands that operate on a keystore require a password parameter for that keystore. The password for the keystore can be entered on the command line, or keytool will interactively prompt for it.

A client certificate and its private key can be directly imported into a JKS keystore from a PKCS#12 file, with the following command line syntax:

keytool -importkeystore -srckeystore client.p12 -srcstoretype pkcs12
-destkeystore akmclient.jks  

The filenames “client.p12” and “akmclient.jks” should be changed to match the actual filenames for the PKCS12 bundle with the client certificate and private key, and the Java keystore JKS file used with AKM. Both the P12 file and the JKS file are protected by passwords, which can be entered interactively, or added to the command line:

keytool -importkeystore -srckeystore client.p12 -srcstoretype pkcs12 -srcstorepass "p12 password" -destkeystore akmclient.jks  -deststorepass "jks password"

Substitute the actual passwords for the placeholders shown; passwords containing spaces need to be quoted, as shown.

PKCS#12 keystores often protect individual private key entries with passwords, in addition to the password for the file itself. If that password is different than the p12 file password, then the key password can be supplied with the -srckeypass argument.

As noted earlier, keytool will prompt for passwords as needed, so it is not always necessary to include the password options on the command line. If running keytool commands from a script, passwords may be exposed unless the script and the environment are securely managed.

Sometimes, there is no password for a private key or the keystore file itself; however, keytool expects a password, and will end with an error if none is supplied. An empty password can be given on the command line, for example:

keytool -importkeystore ... -srcstorepass "" -srckeypass ""  ...

A PCKS#12 file can contain more than one certificate. It may contain an entire certificate chain, including signing certificates. Importing a PKCS#12 file to a JKS keystore without specifying a source alias will import all entries.

The contents of any .p12 or .pfx file should be listed first before importing to a JKS file, using the keytool -list command as described in the next section.

If a certificate and its private key are in separate PEM files, then a PKCS12 bundle can be created with the OpenSSL command line tool.

openssl pkcs12 -export -out client.p12 -inkey clientkey.pem -in clientcert.pem

Substitute actual names for the placeholder names client.p12, clientkey.pem and clientcert.pem.

A CA certificate can be directly imported to a truststore JKS file, without a private key. If the CA certificate is in PEM format, the command line syntax is straightforward:

keytool -importcert -file cacert.pem -alias CAAlias -keystore truststore.jks -storepass StorePass 

Another user of the -importcert option would be add a specific server certificate to the truststore, as described in the certificate pinning section of Appendix E.

The syntax would be the same as shown for importing a CA certificate, but a server certificate would be imported instead of a CA certificate:

keytool -importcert -file servercert.pem -alias "trustme" -keystore truststore.jks -storepass "password"

The server certificate being imported should be signed by a CA that is already in the truststore.

 

Appendix E: Using Certificate Aliases

Keystore entries can be identified by an alias when a specific entry is of interest. The AKM SDK for Java can use aliases for fine-grained control of client certificate selection, and for server certificate pinning, which is described in the certificate pinning section of this appendix.

Many keytool commands can include an -alias parameter to identify a specific entry. An alias can be specified by the user when importing creating a keystore entry. If not specified, one will be generated by keytool.

To determine existing alias values, the keystore (or truststore) contents can be listed with:

keytool -list -keystore akmkeystore.jks -v

The first value displayed for each entry is the alias value. The -v (verbose) option is optional, and will display more detail, and clearly indicate the alias value.

Alias names are usually a number or short word, but can contain embedded commas and spaces, and may contain a Distinguished Name comprising certificate field values. Any alias name that is not a single number or word must be quoted on the command line to correctly identify the keystore entry.

Keytool can list the contents of keystores in PKCS#12 (P12) format also with the -storetype option. Keytool will list alias values for the contents of PKCS#12 keystores. OpenSSL uses the term “name” to indicate a specific entry in a file, but the same value is listed by keytool as “alias”. For example:

keytool -list -keystore akmclient.p12 -storetype pkcs12

An existing alias can be changed:

keytool -changealias -alias "oldalias" -destalias "newalias" -keystore "akmkeystore.jks"

Client aliases and server pinning aliases

While an alias can be used for both client certificate selection and for certificate pinning, there is an important difference in the purposes and usage.

In a client keystore, use of an alias selects a particular to present to the AKM server during the TLS handshake. If there is only one certificate in the keystore, an alias is not needed. Certificates can be added to a keystore with the -importkeystore option shown above.

In a truststore, use of an alias restricts which certificate presented by the server will be trusted by the client. A server certificate signed by a trusted CA can be imported to the truststore, and the alias for that server certificate can be configured for the server.

Appendix F on XML configuration describes the optional use of alias values in the client and server elements, to select a client certificate, or for server pinning.

 

Certificate pinning

The standard approach to TLS authentication as described in this document is to install the root CA certificate onto the client in addition to the client certificate/private key. This approach is adequate for most implementations, especially if AKM is deployed entirely within an enterprise’s network, including all clients, and a tightly managed CA infrastructure is used instead of a public CA. However, operational procedures must still be in place to control access to network infrastructure and certificates.

To further increase security and avoid installing the root CA certificate on clients, the AKM SDK for Java also provides an option to identify a specific server certificate that will be trusted, rather than accept any server certificate signed by a trusted CA.

This option is known as certificate pinning, and using it restricts the connection to a specific server by identifying the server’s certificate in the client truststore.

Like other forms of whitelisting, certificate pinning does not scale well to many servers, but an AKM client connects to a small number of known AKM servers, whose certificates are managed by the organization deploying AKM.

Using AKM server certificates as pinned certificates

To pin an AKM server certificate, the AKM server administrator must give the server certificate to the client developer or end user. That server certificate must be imported to the truststore used by the client, using the keytool commands described earlier in this chapter. See Client aliases and server pinning aliases for more information on using aliases in Java JKS files.

The alias for the server certificate must be specified either in an XML configuration file, or in code. Appendix F describes configuration of the key client API in more detail.

 

Appendix F: AKM XML Configuration Schema

The AKM SDK for Java XML configuration is defined by the following schema. This is provided as a reference for the names and types used, and some of their relationships.

The SDK uses standard rules for well formed XML, as implemented by the Java SAX package. Additional validation based on logical relationships between some of the elements is applied after standard XML parsing.

The akmconfig element must be either the document root element, or a direct child of the document root, allowing an AKM configuration within an XML file with a broader purpose.

A naming convention defines a list of elements with a singular name nested under an element with a plural name. For example: the <servers> element defines a list of one or more <server> child elements. The name attribute values of elements within a list element should be unique.

The logical relationships that are enforced constrain the values of some attributes to the name attribute of certain other elements:

  • The keystore or truststore attribute of a server element must match the name attribute of a keystore child element in the keystores element.

  • The keystore or attribute of a clientrole element must match the name attribute of a keystore child element in the keystores element.

  • The name attribute of an akmserver element must match the name attribute of one of the server elements in the servers child element of the keyService element.

Other constraints include:

  • The name attribute of both portrole and clientrole elements must be one of the values: “keyRetrieval”, “encryption”, or “admin”.

Optional elements and usage:

  • A portrole element can appear in the portroles list within the keyService. If no portrole element is present for one of the AKM services, the AKM default port is used.

  • The portroles list element itself is optional. The portrole elements support clients that want to connect to AKM servers on non-default ports; otherwise their use is not required.

  • The AKM default port assignments are: 6000 for key retrieval, 6001 for admin, and 6003 for encryption services.

  • The value of the port attribute should correspond to the port value configured on the AKM server itself for that role or service. If the server is configured with non-default values, they should be set in the portroles list of the key service. If AKM default ports are used for all servers, ` portrole` elements can be omitted.

  • A server element may optionally have child portrole elements, to override port assignments per server.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="akmconfig" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
 <xs:element name="portrole">
	<xs:complexType>
 	<xs:attribute name="name" type="xs:string" />
 	<xs:attribute name="port" type="xs:string" />
	</xs:complexType>
 </xs:element>
 <xs:element name="akmconfig" msdata:IsDataSet="true>
	<xs:complexType>
 	<xs:sequence minOccurs="0" maxOccurs="unbounded">
	
   	<xs:element name="keystores">
     	<xs:complexType>
       	<xs:sequence>
         	<xs:element name="keystore" minOccurs="0" maxOccurs="unbounded">
           	<xs:complexType>
             	<xs:attribute name="name" type="xs:string" />
              	<xs:attribute name="path" type="xs:string" />
             	   <xs:attribute name="pw" type="xs:string" />
           	</xs:complexType>
         	</xs:element>
            <xs:element name="keystore" minOccurs="0" maxOccurs="unbounded">
           	<xs:complexType>
             	<xs:attribute name="name" type="xs:string" />
              	<xs:attribute name="path" type="xs:string" />
             	   <xs:attribute name="pw" type="xs:string" />
           	</xs:complexType>
         	</xs:element>
       	</xs:sequence>
     	</xs:complexType>
   	</xs:element>
  	 
   	<xs:element name="keyService">
     	<xs:complexType>
       	<xs:sequence>
      	 
         	<xs:element name="portroles" minOccurs="0" maxOccurs="1">
           	<xs:complexType>
             	<xs:sequence>
               	<xs:element ref="portrole" minOccurs="1" maxOccurs="unbounded">
             	</xs:sequence>
           	</xs:complexType>
         	</xs:element>
        	 
         	<xs:element name="servers" minOccurs="1" maxOccurs="unbounded">
           	<xs:complexType>
             	<xs:sequence>
              	 
               	<xs:element name="server" minOccurs="1" maxOccurs="unbounded">
                 	<xs:complexType>
                   	<xs:sequence>
                     	<xs:element ref="portrole" minOccurs="0"
 												maxOccurs="unbounded" />
                   	</xs:sequence>
                   	<xs:attribute name="name" type="xs:string" />
                   	<xs:attribute name="address" type="xs:string" />
                   	<xs:attribute name="keystore" type="xs:string" />
                   	<xs:attribute name="trustAlias" type="xs:string" />
                 	</xs:complexType>
             	 
               	</xs:element>
             	</xs:sequence>
           	</xs:complexType>
         	</xs:element>
       	</xs:sequence>
     	</xs:complexType>
   	</xs:element>
  	 
   	<xs:element name="keyClient">
     	<xs:complexType>
       	<xs:sequence>
        	 
         	<xs:element name="clientrole" minOccurs="0" maxOccurs="unbounded">
           	<xs:complexType>
             	<xs:attribute name="name" type="xs:string" />
             	<xs:attribute name="keystore" type="xs:string" />
             	<xs:attribute name="alias" type="xs:string" />
           	</xs:complexType>
         	</xs:element>
        	 
         	<xs:element name="akmservers" minOccurs="0" maxOccurs="unbounded">
           	<xs:complexType>
             	<xs:sequence>
               	<xs:element name="akmserver" minOccurs="1"
 maxOccurs="unbounded">
                 	<xs:complexType>
                   	<xs:attribute name="name" type="xs:string" />
                   	<xs:attribute name="order" type="xs:string" />
                   	<xs:attribute name="timeout" type="xs:string" />
                 	</xs:complexType>
               	</xs:element>
             	</xs:sequence>
           	</xs:complexType>
        	 
         	</xs:element>
       	</xs:sequence>
     	</xs:complexType>
   	</xs:element>
  	 
 	</xs:sequence>
	</xs:complexType>
 </xs:element>
</xs:schema>