The RIM Crypto API was designed in such a way that a developer
can easily use digests and MACs (Message
Authentication Codes) and access their contents. Unlike many
of the encryption topics covered so far in this tutorial, they are
relatively straightforward and easy to understand. This lesson describes
the digests and MACs found in the API and
provides code examples that show how to use them effectively.

Digests

Digests are used to convert an arbitrary long piece of input data to a fixed size hash or digest. Strong digest algorithms make it hard to find two inputs that hash
to the same output, and make it hard to find an input that hashes to a particular digest value.

The most commonly used hash function in North America
is SHA-1, whose digest length is 160 bits. Recently, the NSA has developed
a successor to SHA-1 called SHA-2. SHA-2 has variable digest bit lengths of 256, 384 and 512 bits.
These lengths match the security level of the upcoming release of the AES
candidate (which is to replace DES). We have implemented SHA-1 (in the class
SHA1Digest), as well as all three variants of SHA-2 (in the classes
SHA256Digest, SHA384Digest and SHA512Digest).
The following example demonstrates how to use these classes.

// Instantiate any of the different types of SHA
SHA1Digest digest160 = new SHA1Digest();
SHA256Digest digest256 = new SHA256Digest();
SHA384Digest digest384 = new SHA384Digest();
SHA512Digest digest512 = new SHA512Digest();
// Now we will simply use the SHA-1 algorithm to illustrate
// how the rest of the functions work.
byte[] data = new byte[128];
RandomSource.getBytes( data );
// Update the contents of the hash function
// (This is the data that gets hashed)
digest160.update( data );
digest160.update( data, 10, 15 );
// Now get the hash value.
byte[] digestValue = digest160.getDigest();

There is another, perhaps even easier way to use digests. This involves the use
of the streaming support provided by DigestInputStream and DigestOutputStream.
Simply create an instance of a digest input or output stream, and pass an instance of
a digest algorithm into the constructor:

// Create a SHA digest with the default length (160-bit).
SHA1Digest digest = new SHA1Digest();
// Now create a new DigestOutputStream. Passing in a null value
// for the OutputStream parameter means that this stream will
// not pass the data written to it into another output stream; we
// simply want to use the digest functionality.
DigestOutputStream out = new DigestOutputStream( digest, null );
// A call to write will also update the digest (assuming data exists
// and contains the message)
out.write( data );
// Now get the hash value.
byte[] digestValue = digest.getDigest();

This same method can be used for DigestInputStream, except that the data read from the
input stream (passed into DigestInputStream) is passed through the digest algorithm.

Support for Other Digests

The interface for all of the other digests in the API is the same.
While SHA is the most common, the Crypto API supports
the following hash algorithms:

MACs are essentially keyed hash functions. That is, a key
is required for the creation of the hash value as well as for the
verification of that particular hash value. The MAC functionality in
the Crypto API builds upon the digest interface, providing this
additional functionality.

The HMAC class implements the MAC interface and allows for easy use of
keyed hash functions. The code required to use an HMAC is very similar
to that for digests, except that a key is required to generate the hash:

// We need to create an HMAC key, which can be an array
// of data of any length. In most cases, a length equal
// to the bit size of the digest is recommended. Random
// data will be used here.
byte[] keyData = new byte[ 20 ];
RandomSource.getBytes( keyData );
// Create the key
HMACKey key = new HMACKey( keyData );
// Create the SHA digest
SHA1Digest digest = new SHA1Digest();
// Now an HMAC can be created, passing in the key and the
// SHA digest. Any instance of a digest can be used here.
HMAC hMac = new HMAC( key, digest );
// The HMAC can be updated much like a digest
hMac.update( data );
hMac.update( data, 10, 15 );
// Now get the MAC value.
byte[] macValue = hMac.getMAC();

There is also streaming support for HMACs. Assuming the HMAC object above has been created,
a MACOutputStream could be used:

// Create the MAC output stream, once again passing in null for
// the OutputStream in order to just use the MAC functionality
MACOutputStream out = new MACOutputStream( hMac, null );
// A call to write will also update the MAC (assuming data exists
// and contains the message)
out.write( data );
// Now get the MAC value.
byte[] macValue = hMac.getMAC();

Once again, a MACInputStream class exists, which can be used in a similar fashion.