ANSI TR-31 key block operations for symmetric transport keys

In a secure environment such as banking, it is important that each symmetric key has a specific set of attributes attached to it, specifying such things as the cryptographic operations for which that key can be used. Different cryptographic architectures implement these attributes in different ways and as a result, the normal approach when exchanging keys with different vendors was to strip the attributes and send just the key material. ANSI TR-31 provides a standardized way to securely exchange key material and its attributes between differing cryptographic systems.

The TR-31 key block has two important features:
  • The key is protected in a way that meets the "key bundling" requirements of various standards that state that the individual 8-byte pieces of a double-length or triple-length triple DES key must be bound in such a way that they cannot be individually manipulated. In TR-31, this result is accomplished mainly by computation of a MAC across the entire structure.
  • The key usage attributes, which define how the key can be used, are securely bound to the key itself. Thus, when a key is sent from one party to another, the attributes are also transferred and cannot be modified to suit the needs of an attacker.

In addition to the encoding defined in ANSI TR-31, the TR-31 key block can have a number of optional data blocks where each has a two character string key and a variable length string value. Any optional data blocks that are included in a TR-31 key block are securely bound to the key in the same way as the TR-31 defined usage attributes are bound to it.

The TR-31 key block can be used to export keys for encrypting and decrypting data, and transport keys (key encrypting keys). Only DES and triple DES keys can be transported in TR-31 key blocks. There is no support for transporting AES keys in TR-31 key blocks.

The keys that are used to protect a TR-31 key block are transport keys. For information about transport keys, see Symmetric transport keys (key encrypting keys) .

Exporting (wrapping) a key in a TR-31 Key Block

The following sample illustrates the simplest case for exporting a key in a TR-31 key block. A triple DES key named tdesDataKey, used for encrypting and decrypting data, is wrapped by using an EXPORTER transport key named tdesExporterKey.
// obtain a cipher with the "DESedeTR31KeyWrap" cipher algorithm
//
Cipher wrapCipher = Cipher.getInstance("DESedeTR31KeyWrap", "IBMJCECCA");

// initialize the cipher, specifying WRAP_MODE and the transport key
//
wrapCipher.init(Cipher.WRAP_MODE, tdesExporterKey);

// create the TR-31 key block
//
byte[] keyBlock = wrapCipher.wrap(tdesDataKey);
The following sample illustrates creating a TR-31 key block to export an operational triple DES IMPORTER transport key, tdesImporterKey2Wrap, by using an EXPORTER transport key named tdesExporterKey.
// obtain a cipher with the "DESedeTR31KeyWrap" cipher algorithm
//
Cipher wrapCipher = Cipher.getInstance("DESedeTR31KeyWrap", "IBMJCECCA");

// because the key being exported is a transport key, it is necessary
// to create a parameterSpec object to specify its usage
//
CCAAlgorithmParameterSpec ccaParmSpec = new CCAAlgorithmParameterSpec();
ccaParmSpec.setKeyUsage(KeyUsage.OP_IMPORTER);

// initialize the cipher by specifying WRAP_MODE, the transport key,
// and the parameterSpec
//
wrapCipher.init(Cipher.WRAP_MODE, tdesExporterKey, ccaParmSpec);

// create the TR-31 key block
//
byte[] keyBlock = wrapCipher.wrap(tdesImporterKey2Wrap);
The following sample illustrates creating a TR-31 key block to export an operational triple DES EXPORTER transport key tdesExporterKey2Wrap using an EXPORTER transport key named tdesExporterKey.
// obtain a cipher with the "DESedeTR31KeyWrap" cipher algorithm
//
Cipher wrapCipher = Cipher.getInstance("DESedeTR31KeyWrap", "IBMJCECCA");

// because the key being exported is a transport key, it is necessary
// to create a parameterSpec object to specify its usage
//
CCAAlgorithmParameterSpec ccaParmSpec = new CCAAlgorithmParameterSpec();
ccaParmSpec.setKeyUsage(KeyUsage.OP_EXPORTER);

// initialize the cipher by specifying WRAP_MODE, the transport key,
// and the parameterSpec
//
wrapCipher.init(Cipher.WRAP_MODE, tdesExporterKey, ccaParmSpec);

// create the TR-31 key block
//
byte[] keyBlock = wrapCipher.wrap(tdesExporterKey2Wrap);
The following sample illustrates creating a TR-31 key block to export a key and specifying optional data blocks.
// define some data for the optional data blocks
//
String optData_ID1 = "AB";
String optData_1 = "Some data required by the receiving system";
String optData_ID2 = "CD";
String optData_2 = "Some information to make this block self-documenting";

// create an array of optional data blocks 
//
TR31OptionalDataBlock[] optDatablocks = new TR31OptionalDataBlock[2];
optDataBlocks[0] = new TR31OptionalDataBlock(optData_ID1, optData_1);
optDataBlocks[1] = new TR31OptionalDataBlock(optData_ID2, optData_2);

// Store the optional data blocks in a ParameterSpec
//
// (If the key being exported were a transport key it would also
// be necessary to call ccaParmSpec.setKeyUsage() as shown in 
// the previous samples.)
//
CCAAlgorithmParameterSpec ccaParmSpec = new CCAAlgorithmParameterSpec();
ccaParmSpec.setTR31OptionalDataBlocks(optDatBlocks);

// obtain a cipher with the "DESedeTR31KeyWrap" cipher algorithm
//
Cipher wrapCipher = Cipher.getInstance("DESedeTR31KeyWrap", "IBMJCECCA");

// initialize the cipher, specifying WRAP_MODE, the transport key,
// and the ParameterSpec
//
wrapCipher.init(Cipher.WRAP_MODE, tdesExporterKey, ccaParmSpec);

// create the TR-31 key block
//
byte[] keyBlock = wrapCipher.wrap(tdesDataKey);

Importing (unwrapping) a key from a TR-31 key block

The following sample illustrates the simplest case for importing a key from a TR-31 key block. A DES key named desDataKey, used for encrypting and decrypting data, is unwrapped from a TR-31 key block, tr31KeyBlock, by using an IMPORTER transport key named tdesImporterKey, and is stored in the CCA key storage area.
// obtain a cipher with the "DESedeTR31KeyWrap" cipher algorithm
//
Cipher unwrapCipher = Cipher.getInstance("DESedeTR31KeyWrap", "IBMJCECCA");

// initialize the cipher, specifying UNWRAP_MODE and the transport key
//
unwrapCipher.init(Cipher.UNWRAP_MODE, tdesImporterKey);

// extract the key from the TR-31 key block
//
DESKey desDataKey = unwrapCipher.unwrap(tr31KeyBlock, "DES", Cipher.SECRETKEY);
The following sample illustrates importing a triple DES key named tdesDataKey, used for encrypting and decrypting data from a TR-31 key block tr31KeyBlock, by using an IMPORTER transport key named tdesImporterKey. The resulting key is encoded by using the host primary key.
// obtain a cipher with the "DESedeTR31KeyWrap" cipher algorithm
//
Cipher unwrapCipher = Cipher.getInstance("DESedeTR31KeyWrap", "IBMJCECCA");

// specify that the unwrapped key is to be enciphered under the local primary key
//
CCAAlgorithmParameterSpec ccaParmSpec = new CCAAlgorithmParameterSpec(CCAAlgorithmParameterSpec.SECURE_INTERNAL_TOKEN);

// initialize the cipher, specifying UNWRAP_MODE, the transport key, and the parameterSpec
//
unwrapCipher.init(Cipher.UNWRAP_MODE, tdesImporterKey, ccaParmSpec);

// extract the key from the TR-31 key block
//
DESedeKey tdesDataKey = unwrapCipher.unwrap(tr31KeyBlock, "DESede", Cipher.SECRETKEY);

The following sample illustrates importing a triple DES IMPORTER transport key named tdesImporterKey2Import from a TR-31 key block tr31KeyBlock by using an IMPORTER transport key named tdesImporterKey. The resulting key is encoded by using the host primary key.

// obtain a cipher with the "DESedeTR31KeyWrap" cipher algorithm
//
Cipher unwrapCipher = Cipher.getInstance("DESedeTR31KeyWrap", "IBMJCECCA");

// specify that the unwrapped key is to be enciphered under the local primary key
//
CCAAlgorithmParameterSpec ccaParmSpec = new CCAAlgorithmParameterSpec(CCAAlgorithmParameterSpec.SECURE_INTERNAL_TOKEN);

// specify that the key being imported is an IMPORTER transport key
//
ccaParmSpec.setKeyUsage( KeyUsage.OP_IMPORTER );

// initialize the cipher, specifying UNWRAP_MODE, the transport key, and the parameterSpec
//
unwrapCipher.init(Cipher.UNWRAP_MODE, tdesImporterKey, ccaParmSpec);

// extract the key from the TR-31 key block
//
DESedeKey tdesImporterKey2Import = unwrapCipher.unwrap(tr31KeyBlock, "DESede", Cipher.SECRETKEY);

Extracting non-key data from a TR-31 Key Block

A TR-31 key block is a byte array and as such, it is not easily parsed. So, a convenience class, TR31KeyBlock, is provided for extracting the non-key information from a TR-31 key block. The toString() method formats the data for printing. When possible, fields in the TR-31 header are displayed with their encoded value and a string that interprets them. In addition, methods are provided to enable extracting data programmatically.

The following sample illustrates printing the non-key data in a TR-31 key block.
//
// a TR-31 key block is a byte[]
// For this illustration we assume that tr31Block is a TR-31 key block created using 
// one of the previous examples.
//

// Get an instance of the TR31KeyBlock convenience class, passing the 
// actual TR-31 key block to the constructor.
//
TR31KeyBlock kblk = new TR31KeyBlock( tr31Block );

// call toString()
//
System.out.println( kblk.toString() );
If the key that is wrapped in the TR-31 key block is a data encrypting and decrypting key and the TR-31 key block contains 2 optional data blocks, the resulting output might look like the following:
TR31KeyBlock: 
    keyBlockVersion:   A
    keyBlockLength:    128
    keyUsage:          D0 ( DataEncryptionKey )
    keyAlgorithm:      T  ( Triple-DES )
    keyMode:           B  ( EncryptDecrypt )
    keyVersion:        00
    keyExportablility: S  ( Exportable under any key )
    numOptDataBlocks:     2
        TR31OptionalDataBlock: 
            OptDataID: 01
            OptData:   Now is the time for all good men
        TR31OptionalDataBlock: 
            OptDataID: PB
            OptData:   
If the key that is wrapped in the TR-31 key block is an EXPORTER transport key and the TR-31 key block contains no optional data blocks, the resulting output might look like the following:
TR31KeyBlock: 
    keyBlockVersion:   A
    keyBlockLength:    88
    keyUsage:          K0 ( KeyEcryptingKey )
    keyAlgorithm:      T  ( Triple-DES )
    keyMode:           E  ( Exporter )
    keyVersion:        00
    keyExportablility: S  ( Exportable under any key )
    numOptDataBlocks:     0
The following sample illustrates programatically extracting the non-key data from a TR-31 key block.
//
// a TR-31 key block is a byte[]
// For this illustration we assume that tr31Block is a TR-31 key block created using 
// one of the previous examples.
//

// Get an instance of the TR31KeyBlock convenience class, passing the 
// actual TR-31 key block to the constructor.
//
TR31KeyBlock kblk = new TR31KeyBlock( tr31Block );

// Extract the data as stored in the TR-31 key block
//
String keyBlkVersion = kblk.getKeyBlockVersion();
int keyBlkLength = kblk.getKeyBlockLength();
String keyUsage = kblk.getKeyUsage();
String keyAlgorithm = kblk.getKeyAlgorithm();
String keyMode = kblk.getKeyMode();
String keyVersion = kblk.getKeyVersion();
String keyExportability = kblk.getKeyExportability();
int numOptionalBlocks = kblk.getNumOptDataBlocks();

// Get descriptions of the data stored in the TR-31 key block
//
String keyUsageDescript = kblk.getKeyUsageAsString();
String keyAlgorithmDescript = kblk.getKeyAlgorithmAsString();
String keyModeDescript = kblk.getKeyModeAsString();
String keyExportabilityDescript = kblk.getKeyExportabilityAsString();

// Extract the contents of the optional data blocks,
// if any
//
TR31OptionalDataBlock[] optblks = kblk.getOptDataBlocks();
String[] optionalDataIDs = new String[numOptionalBlocks];
String[] optionalData = new String[numOptionalBlocks];
for( int idx=0; idx < numOptionalBlocks; idx++ ) {
    optionalDataIDs[idx] = optblks[idx].getOptDataID();
    optionalData[idx] = optblks[idx].getOptData();
} // end for