Richard G Baldwin (512) 223-4758, baldwin@austin.cc.tx.us, http://www2.austin.cc.tx.us/baldwin/

Security, Message Digest Streams

Java Programming, Lecture Notes # 712, Revised 5/13/99.


Preface

Students in Prof. Baldwin's Advanced Java Programming classes at ACC will be responsible for knowing and understanding all of the material in this lesson beginning with the spring semester of 1999.

This lesson was originally written on April 24, 1999 and has been revised several times since then.

The programs in this lesson were tested using JDK 1.2 under Win95

Disclaimer

I claim absolutely no expertise in the area of security. I am simply a college professor attempting to gather information about Java on one hand and present it to my students on the other. I disclaim any responsibility for any security problems that may occur as a result of anyone using any of the material in any of my tutorial lessons.

You are responsible for your own actions. With regard to security, you should study not only the material that I will present, but also material provided by others who possess expertise in the security area. Hopefully my material will be useful in getting you started in that direction.

Two good books on security published by O'Reilly & Associates are:

I highly recommend both of these books.

Introduction

Previous lessons introduced you to message digests and the protection of message digests using a secret passphrase. This lesson will show you how to exchange passphrase-protected message digests using message digest streams.

Writing the protected file

Two sample programs are presented. The first program shows you how to use a message digest output stream to create a message digest combining a message and a passphrase. The message and the passphrase-protected digest value are written into an output file as serialized objects.

Reading the protected file

The second program shows how to use a message digest input stream, the received message, and the secret passphrase to confirm the integrity of the received message.

Objective of the lesson

The primary objective of this lesson is to illustrate message digest streams and not passphrase protection.

Therefore, a program was not provided to show that corruption of the message during transmission will cause the second program to fail to confirm the integrity of the message.

However, that could easily be accomplished by modifying and using the program named Security07C from an earlier program to corrupt the message after the file is written by Security08A and before it is read by Security08B.

Discussion

An earlier lesson suggested that when exchanging data electronically, the parties to the communication might be interested in the following three aspects of that communication:

This lesson deals with only the first and third items: authentication and integrity. Generally, some type of encryption is required to achieve confidentiality. Encryption is the topic of a different lesson.

Program Security08A

This program works in conjunction with Security08B. These two programs illustrate passphrase protected message digests using message digest streams.

Operational procedures

A file is written to the disk containing two serialized objects:

The digest value for the message is computed on the fly as the message is serialized and written into the disk file.

The program named Security08B is designed to read the two serialized objects and to use the digest value along with the secret passphrase to determine if the message in the serialized message object was corrupted after the original digest value was computed and placed in the file.

No need to invoke the update() method

Note that the update() method of the MessageDigest class is never used in this program. The message digest stream effectively invokes the update() method on the MessageDigest object as each byte of the serialized message object is written into the output stream.

The program was tested using JDK 1.2 under Win95.

Program output

When the two programs were run in succession, the output produced by the two programs was as shown in the following box:

Security08A
The Message:
ABCDEFG
The Digest:
Ntkdyp5IdghfsFNZk0VjxArq/30=

Security08B
The Message:
ABCDEFG
Original Digest:
Ntkdyp5IdghfsFNZk0VjxArq/30=
New Digest:
Ntkdyp5IdghfsFNZk0VjxArq/30=
Valid message

As you can see from the boldface output, when the procedures of Security08B were applied to the original message and digest produced by Security08A, the integrity of the message was verified.

The digest values are also shown in Base 64 format so that you can see what the verification process has to work with.

Security08A Code Fragments

The first fragment shows the beginning of the main() method which includes the generation of a passphrase (The Pass Phrase) and the message (ABCDEFG).

Keeping it simple

Both the passphrase and the message were hard coded into the program to keep it simple. In a real program, the passphrase would be provided as a user input and the message would most likely be extracted from a file on the disk.

class Security08A {
  public static void main(String[] args) {
    try{
      System.out.println("Security08A");

      byte[] passPhrase = "The Pass Phrase".getBytes();
      String message = "ABCDEFG";//create message
      System.out.println("The Message:\n" + message);

Get an ObjectOutputStream object

The next fragment prepares an ObjectOutputStream. The stream will be used for writing the message as a serialized object and computing a digest value for the serialized message object on the fly as the serialized object is written to the output stream.

The DigestOutputStream

The new material here is the statement showing the instantiation of the DigestOutputStream object, which takes a FileOutputStream and a MessageDigest as parameters to the constructor.

The DigestOutputStream object is then wrapped in an ObjectOutputStream to produce the stream object that writes a serialized object and computes a digest value on the serialized object as it is written.

      FileOutputStream fileOut = 
                    new FileOutputStream("Security08.obj");
      MessageDigest messageDigest = 
                          MessageDigest.getInstance("SHA");
      DigestOutputStream digestOut = 
             new DigestOutputStream(fileOut,messageDigest);
      ObjectOutputStream objectOut = 
                         new ObjectOutputStream(digestOut);

Write the message

The next fragment serializes the message and writes it to the output stream, computing a digest value on the fly.

      objectOut.writeObject(message);

Can be turned on or off

The process of computing the digest value on the fly can be turned on and off by invoking the on() method of the DigestOutputStream with a boolean parameter (it is on by default when the output stream is instantiated).

Here is what the JDK 1.2 documentation has to say about the on() method.

Turns the digest function on or off. The default is on. When it is on, a call to one of the write methods results in an update on the message digest. But when it is off, the message digest is not updated.

Turn it off

The following fragment turns digestion off after the serialized message object has been fed into the MessageDigest object.

      digestOut.on(false);

If we were not providing passphrase protection, we could simply serialize the MessageDigest object and write the serialized object into the output file at this point.

Incorporating the passphrase

However, since we are protecting the process using a secret passphrase, we need to update the MessageDigest object with the passphrase. This is accomplished in the next fragment.

Note that this fragment uses an overloaded version of the digest() method to update the digest value and return the final digest value in a single statement (rather than using separate calls to the update() and digest() methods).

      byte[] theDigest = messageDigest.digest(passPhrase);

Serialize and write the digest value

The next fragment serializes the byte array containing the final digest value and writes the serialized object into the output file. The fragment also displays the digest value in Base 64.

The method to display the data in Base 64 can be viewed in the listing of the program that is provided near the end of the lesson.

      objectOut.writeObject(theDigest);
      displayBase64("The Digest:",theDigest);

File contains two serialized objects

At this point, an output file has been written containing two serialized objects. One object is a serialized version of the message. The other object is a serialized byte array containing a digest value produced using a combination of the serialized message object and a secret passphrase. 

Program Security08B

This program works in conjunction with Security08A. These two programs together illustrate passphrase protected message digests using message digest streams.

Operational procedures

The program reads a file produced by Security08A, extracting two serialized objects:

The program uses the message, the digest value, and a secret passphrase to determine if the message has been corrupted since the digest value was computed and placed in the file along with the message.

The update() method is not required

Note that the update() method of the MessageDigest class is not used in this program. The contents of the MessageDigest object are automatically updated as the bytes comprising the serialized message object are read from the disk file.

Verification of the integrity of the message is accomplished by computing a new digest value combining the incoming message and the known passphrase, and then comparing the new digest value with the original digest value extracted from the file.

If the two are the same, this is a very strong indication that either the original message has not been corrupted, or that the passphrase has been compromised. (Another person who has gained knowledge of the passphrase could corrupt the original message and use the passphrase along with the corrupted message to compute a new digest value that would pass the test.)

Therefore, the security of the process depends on the ability of the parties involved to keep the passphrase secret.

The program was tested using JDK 1.2 under Win95.

Security08B Code Fragments

The first fragment shows the beginning of the main method and includes a hard-coded passphrase. As explained earlier, the passphrase would normally be provided as a user input in a real program. It was hard coded in this sample program just to keep the program simple.

The fragment also declares a byte array that will be used later in the program.

class Security08B {
  public static void main(String[] args) {
    System.out.println("Security08B");
    try{
      byte[] passPhrase = "The Pass Phrase".getBytes();
      byte[] originalDigest = null;

Get an ObjectInputStream

The next fragment uses a DigestInputStream to prepare an ObjectInputStream that will be used to read the serialized message object from the disk file and compute the digest value for that object on the fly.

Using the DigestInputStream class

The new material here is the statement that instantiates the DigestInputStream object, passing a FileInputStream object and a MessageDigest object as parameters to the constructor.

This DigestInputStream object is then wrapped in an ObjectInputStream to produce the reader that will be used to read the serialized message object.

      FileInputStream fileIn = 
                     new FileInputStream("Security08.obj");
      MessageDigest messageDigest = 
                          MessageDigest.getInstance("SHA");
      DigestInputStream digestIn = 
               new DigestInputStream(fileIn,messageDigest);
      ObjectInputStream objIn = 
                           new ObjectInputStream(digestIn);

Compute the message digest on the fly

The next fragment reads the serialized message object while updating the MessageDigest object on the fly.

Then the digestion process is turned off to prevent the data contained in the MessageDigest object from being contaminated during the reading of the serialized object containing the original digest value that follows.

      String message = (String)objIn.readObject();
      digestIn.on(false);

Get the original message digest

The next fragment reads the serialized object containing the original digest value and saves the original digest value in a byte array. Then it closes the input stream and displays the received message and the original digest value.

The original digest value is displayed in Base 64. You can view the method used to display in Base 64 in the listing of the program near the end of the lesson.

      originalDigest = (byte[])objIn.readObject();
      objIn.close();

      System.out.println("The Message:\n" + message);
      displayBase64("Original Digest:",originalDigest);

Incorporate the passphrase

Because the process is passphrase protected, it is necessary to update the MessageDigest object to include the passphrase. This is accomplished with a single call to the digest() method passing a byte array containing the passphrase as a parameter.

The newly computed digest value is also displayed in Base 64.

      byte[] theDigest = messageDigest.digest(passPhrase);
      displayBase64("New Digest:",theDigest);

Compare the two message digests

The following fragment uses the isEqual() method of the MessageDigest class to compare the newly computed digest value against the original digest value extracted earlier from the file.

The comparison is made to determine if the message has been corrupted since the original digest value was written into the file.

      if(MessageDigest.isEqual(theDigest,originalDigest)){
        System.out.println("Valid message");
      }else{
        System.out.println("Corrupted message");
      }//end else

    }catch(Exception e){System.out.println(e);}
  }//end main

Program Listings

Complete listings of both programs are contained in this section.

/*File Security08A.java Copyright 1999, R.G.Baldwin
Rev 4/24/99

Message Digest Streams

This program works in conjunction with Security08B.

These two programs illustrate passphrase protected message
digests using digest streams.

A file is written to the disk containing two serialized
objects: a message and a digest value corresponding to the
message plus a secret passphrase

The program named Security08B is designed to read the
two serialized objects and to use the digest value along 
with the secret passphrase to determine if the message in 
the object was corrupted after the original digest value 
was computed and placed in the file.

Note that the update() method of the MessageDigest class
is never used in this program.

Tested using JDK 1.2 under Win95.

When the two programs were run in succession, the
output produced by the two programs was:
  
Security08A
The Message:
ABCDEFG
The Digest:
Ntkdyp5IdghfsFNZk0VjxArq/30=

Security08B
The Message:
ABCDEFG
Original Digest:
Ntkdyp5IdghfsFNZk0VjxArq/30=
New Digest:
Ntkdyp5IdghfsFNZk0VjxArq/30=
Valid message 
**********************************************************/

import java.io.*;
import java.security.*;
import sun.misc.*;

class Security08A {

  public static void main(String[] args) {
    try{
      
      System.out.println("Security08A");
      //Hard code the pass phrase and the message to keep
      // the program simple
      byte[] passPhrase = "The Pass Phrase".getBytes();
      String message = "ABCDEFG";//create message
      System.out.println("The Message:\n" + message);

      //Write the message and a digest of the message plus
      // the pass phrase into a disk file as serialized 
      // objects.  Perform the digestion of the message on
      // the fly.
      FileOutputStream fileOut = 
                    new FileOutputStream("Security08.obj");
      MessageDigest messageDigest = 
                          MessageDigest.getInstance("SHA");
      DigestOutputStream digestOut = 
             new DigestOutputStream(fileOut,messageDigest);
      ObjectOutputStream objectOut = 
                         new ObjectOutputStream(digestOut);
    
      objectOut.writeObject(message);
      //Turn stream digestion off
      digestOut.on(false);
      //Digest the secret pass phrase into the digest of
      // the message.
      byte[] theDigest = messageDigest.digest(passPhrase);
      objectOut.writeObject(theDigest);
    
      displayBase64("The Digest:",theDigest);
    
    }catch(Exception e){System.out.println(e);}
  }//end main
  //-----------------------------------------------------//

  //Method to display an array of bytes in base 64 format
  static void displayBase64(String title,byte[] data){
    System.out.println(title);
    BASE64Encoder encoder = new BASE64Encoder();
    String encoded = encoder.encodeBuffer(data);
    System.out.print(encoded);
  }//end base64Display()
  //-----------------------------------------------------//
}//end class Security08A

.

/*File Security08B.java Copyright 1999, R.G.Baldwin
Rev 4/24/99
Message digest streams

This program works in conjunction with Security08A.

These program illustrate passphrase protected message
digests using digest streams.

The program reads a file produced by Security08A, 
extracting two serialized objects: a message and a digest 
value,

The program uses the message, the digest value, and a
secret passphrase to determine if the message has become
corrupted since the digest value was computed and placed
in the object along with the message.

Note that the update() method of the MessageDigest class
is never used in this program.

Tested using JDK 1.2 under Win95.
**********************************************************/

import java.io.*;
import java.security.*;
import sun.misc.*;

class Security08B {

  public static void main(String[] args) {
    System.out.println("Security08B");
    try{
      //Hard code the pass phrase to keep the 
      // program simple
      byte[] passPhrase = "The Pass Phrase".getBytes();
      byte[] originalDigest = null;

      //Read the two serialized objects from the file, 
      // digesting the message object on the fly to create
      // a verification digest for the message.
      FileInputStream fileIn = 
                     new FileInputStream("Security08.obj");
      MessageDigest messageDigest = 
                          MessageDigest.getInstance("SHA");
      DigestInputStream digestIn = 
               new DigestInputStream(fileIn,messageDigest);
      ObjectInputStream objIn = 
                           new ObjectInputStream(digestIn);
                             
      String message = (String)objIn.readObject();
      //Turn stream digestion off
      digestIn.on(false);
      originalDigest = (byte[])objIn.readObject();
      objIn.close();

      System.out.println("The Message:\n" + message);
      displayBase64("Original Digest:",originalDigest);

      //Digest the secret pass phrase into the digest
      // of the incoming message.
      byte[] theDigest = messageDigest.digest(passPhrase);
      displayBase64("New Digest:",theDigest);
    
      //Test the newly computed digest value against the
      // original digest value to determine if the message
      // has become corrupted.
      if(MessageDigest.isEqual(theDigest,originalDigest)){
        System.out.println("Valid message");
      }else{
        System.out.println("Corrupted message");
      }//end else

    }catch(Exception e){System.out.println(e);}

  }//end main
  //-----------------------------------------------------//

  //Method to display an array of bytes in base 64 format
  static void displayBase64(String title,byte[] data){
    System.out.println(title);
    BASE64Encoder encoder = new BASE64Encoder();
    String encoded = encoder.encodeBuffer(data);
    System.out.print(encoded);
  }//end base64Display()
  //-----------------------------------------------------//
}//end class Security08B

-end-