public byte[] doFinal public byte[] doFinalbyte[] input
public void doFinalbyte[] output, int offset Calculate and return the MAC. The MAC is either returned directly or in the last case stored in the
given array. The last two methods allow you to specify the last data to include in the MAC. In order to calculate the size of the output array for the third method, use the
getMacLength method.
In addition, before calling the update
or doFinal
methods, a MAC must be initialized by calling this method:
public void initSecretKey sk public void initSecretKey sk, AlgorithmParameterSpec aps
Initialize the MAC for use. Failure to call this method will cause the update
or doFinal
method to throw an IllegalStateException
. The remainder of the methods of the
Mac class mimic those in the
MessageDigest class.
Mac objects can be reused any number of times; they are reset after each call to the
doFinal method.
They may be reused with a different key by calling the init
method with the new key. This modification of our last example saves a MAC instead of a simple digest:
package javasec.samples.ch11; import java.io.;
import java.security.; import javax.crypto.;
import javasec.samples.ch10.KeyStoreHandler; public class SendMac {
public static void mainString args[] { try {
FileOutputStream fos = new FileOutputStreamtest; Mac mac = Mac.getInstanceHmacSHA1;
KeyStoreHandler ksh = new KeyStoreHandlernull; KeyStore ks = ksh.getKeyStore ;
mac.initSecretKey ks.getKeyargs[0], args[1].toCharArray ;
ObjectOutputStream oos = new ObjectOutputStreamfos; String data = This have I thought good to deliver thee, +
that thou mightst not lose the dues of rejoicing + by being ignorant of what greatness is promised thee.;
byte buf[] = data.getBytes ; mac.updatebuf;
oos.writeObjectdata; oos.writeObjectmac.doFinal ;
} catch Exception e { System.out.printlne;
} }
}
Using standard key management techniques, we look up a secret key in the default keystore and use that key to initialize the
Mac object. Otherwise, the code is very similar to what weve seen before. You can make
similar changes to the Receive
class so that it can read the MAC; we wont bother to show the code here though it is included with the online examples. Note that you must have a secret key in your keystore to run
this example; use the StoreKey
example from Chapter 10 to create such a key.
11.2.2 Calculating Your Own MAC
A second way to create a MAC is to calculate one directly. This requires that both the sender and receiver of the data have a shared passphrase that they have kept secret, though thats often easier than sharing a secret
key.
Using this passphrase, calculating a MAC requires that we: Calculate the message digest of the secret passphrase concatenated with the data:
MessageDigest md = MessageDigest.getInstanceSHA; String data = This have I thought good to deliver thee, +
that thou mightst not lose the dues of rejoicing + by being ignorant of what greatness is promised thee.;
String passphrase = Sleep no more; byte dataBytes[] = data.getBytes ;
byte passBytes[] = passphrase.getBytes ; md.updatepassBytes;
md.updatedataBytes; byte digest1[] = md.digest ;
1.
Calculate the message digest of the secret passphrase concatenated with the just−calculated digest:
md.updatepassBytes; md.updatedigest1;
byte mac[] = md.digest ;
2.
We can substitute this code in our original Send
example, writing out the data string and the MAC to the file. Note that we can use the same message digest object to calculate both digests since the object is reset after a
call to the digest
method. Also note that the first digest we calculate is not saved to the file: we save only the data and the MAC. Of course, we must make similar changes to the
Receive example; if the MACs
are equal, the data was not modified in transit. As long as we use exactly the same data for the passphrase in both the transmitting and receiving class, the
message digests that is, the MACs still compare as equal. That gives a certain level of security to the message digest, but it requires that the sender and the receiver agree on what data to use for the passphrase;
the passphrase cannot be transmitted along with the text. In this case, the security of the message digest depends upon the security of the passphrase. Normally, of course, you would prompt for that passphrase
rather than hardcoding into the source as weve done above. In addition, a good passphrase would not be a well−known string such as weve selected; it would be random bytes and hence indistinguishable from a
secret key.
11.3 Message Digest Streams
The interface to the message digest class requires that you supply the data for the digest as a series of single bytes or byte arrays. As we mentioned earlier, this is not always the most convenient way to process data,
which may be coming from a file or other input stream. This brings us to the message digest stream classes. These classes implement the standard input and output filter stream semantics of Java streams so that data can
be written to a digest stream that will calculate the digest as the data itself is written or the reverse operation for reading data.
Unfortunately, because the Mac
class does not extend the MessageDigest
class, these streams work only with standard message digests.
11.3.1 The DigestOutputStream Class
The first of these classes well examine is the DigestOutputStream
class java.security.DigestOutputStream
. This class allows us to write data to a particular output stream and calculate the message digest of that data transparently as the data passes through the stream:
public class DigestOutputStream extends FilterOutputStream Provide a stream that can calculate the message digest of data that is passed through the stream. A
digest output stream holds two components internally: the output stream that is the ultimate destination of the data and a message digest object that computes the data of the stream written to the
destination.
The digest output stream is constructed as follows: public DigestOutputStreamOutputStream os, MessageDigest md
Construct a digest output stream that associates the given output stream with the given message digest. Data that is written to the stream is automatically passed to the
update method of the
message digest. In addition to the standard methods available to all output streams, a message digest output stream provides
the following interface:
public MessageDigest getMessageDigest Return the message digest associated with this output stream.
public void setMessageDigestMessageDigest md Associate the given message digest with this output stream. The internal reference to the original
message digest is lost, but the original message digest is otherwise unaffected i.e., if you still hold a reference to the original message digest object, you can still calculate the digest of the data that was
written to the stream while that digest was in place.
public void writeint b public void writebyte b[], int off, int len
Write the given byte or array of bytes to the underlying output stream, and also update the internal message digest with the given data if the digest stream is marked as on. These methods may throw
an IOException
from the underlying stream. public void onboolean on
Turn the message digest stream on or off. When data is written to a stream that is off, the data will be passed to the underlying output stream, but the message digest will not be updated.
Note that this last method does not affect the underlying output stream at all; data is still sent to the underlying stream even if the digest output stream is marked as off. The onoff state only affects whether the
update method of the message digest will be called as the data is written.
We can use this class to simplify the example we used earlier:
package javasec.samples.ch11; import java.io.;
import java.security.; public class SendStream {
public static void mainString args[] { try {
FileOutputStream fos = new FileOutputStreamtest; MessageDigest md = MessageDigest.getInstanceSHA;
DigestOutputStream dos = new DigestOutputStreamfos, md; ObjectOutputStream oos = new ObjectOutputStreamdos;
String data = This have I thought good to deliver thee, + that thou mightst not lose the dues of rejoicing +
by being ignorant of what greatness is promised thee.; oos.writeObjectdata;
dos.onfalse; oos.writeObjectmd.digest ;
} catch Exception e { System.out.printlne;
} }
}
The big change is in constructing the object output stream −− we now want to wrap it around the digest output stream so that as each object is written to the file, the message digest will include those bytes. We also want to
make sure that we turn off the message digest calculation before we send the digest itself to the file. Turning off the digest isnt strictly necessary in this case since we dont use the digest object once weve calculated a
single digest in this example, but its good practice to keep the digest on only when strictly required.
Note that there is a subtle difference between the digest produced in this example and our first example. In the first example, the digest was calculated over just the bytes of the string that we saved to the file. In the second
example, the digest was calculated over the serialized string object itself −− which includes some information regarding the class definition in addition to the bytes of the string.
11.3.2 The DigestInputStream Class
The symmetric operation to the digest output stream is the DigestInputStream
class java.security.DigestInputStream
: public class DigestInputStream extends FilterInputStream
Create an input stream that is associated with a message digest. When data is read from the input stream, it is also sent to the
update method of the streams associated message digest.
The digest input stream has essentially the same interface as the digest output stream with writing replaced by reading. There is a single constructor for the class:
public DigestInputStreamInputStream is, MessageDigest md Construct a digest input stream that associates the given input stream with the given message digest.
Data that is read from the stream will also automatically be passed to the update
method of the message digest.
The interface provided by the digest input stream is symmetric to the digest output stream: 214