• 0

[Java] A little help with some basics


Question

Hi,

I've just started a new course in Java Network Programming, and consequently, I'm reading the book with the same title. Now somewhere around pages 70 to 90 the book brings up Digest Stream and Encryption. However, I just don't understand the difference. I get what encryption is, but when I read about Digest Stream it just strikes me as pretty much the same thing. To scramble the stream so that it cannot be read by another. So basically I would like a little explanation about the differences between these, and what they are used for.

Also, while I'm on the topic. I actually haven't touched up on my Java since about 2006-2007, and even then I was mostly working with relatively basic stuff. And unfortunately, by now, I've forgotten a great big part of what I once knew. In the book, on a page a bit further ahead that deals with Treads, there is a bit of code that brings up a few things that I recall using years ago but forgot what they do. Like toString(), append(). And coincidentally, it also uses Digest, so I thought it would be a good code to have explained to me. So, if anyone would be so kind as to give me a line for line explanation of what the code bellow does.

import.java.io.*;
import java.security.*;

public class DigestThread extends Thread{

	private File input;

	public DigestThread(File input){
		this.input = input;
	}

	public void run(){
		try{
			FileInputStream in = new FileInputStream(input);
			MessageDigest sha = new MessageDigest.getInstance("SHA");
			DigestInputStream din = new DigestInputStream(in, sha);

			int b;
			while ((b = din.read()) != -1);
			din.close();

			byte[] digest = sha.digest();
			StringBuffer result = new StringBuffer(input.toString());
			result.append(": ");

			for(int i = 0; i < digest.length; i++){
				result.append(digest[i] + " ");
			}

			System.out.println(result);
		}catch (IOException ex){
			System.err.println(ex);
		}catch (NoSuchAlgorithmException ex){
			System.err.println(ex);
		}
	}

	public static void main(String[] args){
		for(int i = 0; i < args.length; i++){
			File f = new File(args[i]);
			Thread t = new DigestThread(f);
			t.start();
		}
	}
}

Thanks in advance,

Raz

Link to comment
Share on other sites

4 answers to this question

Recommended Posts

  • 0

A digest is a value calculated using the whole input, you use it to check that the content hasn't been changed - if it is the digest will be different. Same as a checksum (CRC etc).

toString(anything) converts anything to a String, useful for printing etc. Works for any kind of Object

append(string) is a StringBuffer method that appends (doh!) the string to the current contents of the StrngBuffer.

Link to comment
Share on other sites

  • 0

Oh, sorry, English isn't my first language so I had to look up the word "Append" :p.

Thanks for that explanation, but I'm still kinda confused about Digest. I guess I just don't get how it is used. Is there basic example code I could look at? I mean I see in the book how you get a digest stream, but I don't get how it's used and exactly what it's used for.

I would still highly appreciate an explanation of the entire code though. I'm especially confused about this part:

int b;

while((b = din.read()) != -1) ;

what's the point of this? As far as I can tell, the whole thing just reads the Digest integer into the variable b. But then b isn't used again anywhere in the code, so what was the point?

Link to comment
Share on other sites

  • 0

A message digest is a way to determining the validaty of data we have received. This involves a few things

1. Calculate a hash of the data (this is a numerical representation of the data).

2. Send the data.

3. Send the hash.

and on the client:

1. Read the data.

2. Calulate a hash of the data (this will be the hash of what we have received).

2. Read the original hash and compare.

If the data was sent without being corrupted, the two hash values should be the same.

I found an example @ http://docstore.mik.ua/orelly/java-ent/security/ch09_02.htm which I've fleshed out for you here:

public class Server
{
  public static void main(String args[]) 
  {
	try
	{
	  /* We're going to write out objects to a file called myTestFile.txt. */
	  FileOutputStream file = new FileOutputStream("myTestFile.txt");

	  /* Let's use SHA as our hashing algorithm. */
	  MessageDigest digest = MessageDigest.getInstance("SHA");
	  /* We create our Digest stream which will allow us to write to the underlying stream,
	   * whilst calculating our digest (hash). */
	  DigestOutputStream digestStream = new DigestOutputStream(file, digestStream);

	  /* The object output stream allows us to write objects to the underlying stream
	   * so we're gonna write POJO (plain old java objects) to the digestStream. */
	  ObjectOutputStream objectStream = new ObjectOutputStream(digestStream);

	  String message = "Hello this is my message!";
	  objectStream.writeObject(message);

	  /* We turn digest off (on(false)) so we can write out hash to the stream as well,
	   * without it recalculating the hash itself. */
	  digest.on(false);
	  objectStream.writeObject(digest.digest());
	}
	catch (Exception ex)
	{
	  System.out.println(ex);
	}
  }
}

What the above class is doing, is simply writing a String object to a file, and then its hash value to the end of the file. Typically you wouldn't do this, as you've provide the hash seperately, but it works nicely in this example. The server class first creates a FileOutputStream stream, this is where ultimately the data will end up. Layered on top of this, is the DigestOutputStream, which will transparently calculate our hash as we write data to it. Layered on top of that is our ObjectOutputStream, which will enable us to write plain old java objects (POJOs) to the file. So think of it like this:

ObjectOutputStream -- <writes object to> --> DigestOutputStream -- <writes data to> --> FileOutputStream

In the above example, we're writing a short message "Hello this is my message" to the file. The DigestOutputStream calculates the hash of the String object while it is being written. Once that is done, we disable the hashing (digest) function, because we don't wait to modify the hash now it satisfies our object data.

Doing this the other way is pretty much the same, but in reverse:

public class Client
{
  public static void main(String args[]) 
  {
	try
	{
	  /* We'll read from out input file. */
	  FileInputStream file = new FileInputStream("myTestFile.txt");

	  /* Again, we'll be using SHA as our hashing algorithm. */
	  MessageDigest digest = MessageDigest.getInstance("SHA"); 
	  DigestInputStream digestStream = new DigestInputStream(file, digest);

	  /* We want to be able to read POJO (plain old java objects) from our digest stream. */
	  ObjectInputStream objectStream = new ObjectInputStream(digestStream);

	  /* Read the first object from the object stream. */
	  Object obj = objectStream.readObject();
	  /* It should be our message string, so check the type. */
	  if (!(obj instanceof String))
	  {
		System.out.println("Unexpected data in file.");
		System.exit(-1);
	  }

	  /* It was a string, so lets cast our object and output its value. */
	  String message = (String)obj;
	  System.out.println(message);

	  /* Turn digest off, we want to read the next value without it recalculating the hash. */
	  digest.on(false);
	  obj = objectStream.readObject();
	  /* The next object should have been our digest value we wrote earlier. */
	  if (!(obj instanceof byte[]))
	  {
		System.out.println("Unexpected data in file.");
		System.exit(-1);
	  }

	  /* It was a byte[] array, so we can compare what the digest was when we sent the data, to what it is 
	   * now we've read the data.
	  byte[] originalDigest = (byte[])obj;
	  if (MessageDigest.isEqual(digest.digest(), originalDigest))
	  {
		System.out.println("Message is valid.");
	  }
	  else
	  {
		System.out.println("Message is corrupt.");
	  }
	}
	catch (Exception ex)
	{
	  System.out.println(ex);
	}
  }
}

We are doing the same thing, so creating a file stream (input this time, not output), FileInputStream, which will be used by the DigestInputStream, which in turn is used by the ObjectInputStream:

ObjectInputStream <-- <reads object from> -- DigestInputStream <-- <reads data from> -- FileInputStream.

The important part of all this, is how the Digest streams automatically calculate the hash values for you, so if we know both what the hash was originally, and what it is now, we can compare them to see if they match. If the hashes are both equal, it means the data was sent without being corrupted. If they are not equal, the data we have received does not faithfully represent what was sent by the server.

I hope that helps you out a bit more, info seems a little scarce on examples!

Oh, and this line:

int b;
while ((b = din.read()) != -1);

Is simply reading to the end of the input stream and assigning the read result to b. It does seem a little convoluted as they don't seem to be using the variable b; anywhere else, and its pretty pointless anyway, because it will eventually always equal -1 (the end of the input stream).

while (din.read() != -1);

That would accomplish the same thing.

Link to comment
Share on other sites

  • 0

Thanks for that very detailed explanation :)

I think I get the gist of it. I used to program in PHP, so it seems that Digest is kind of like an MD5, only since PHP doesn't have streams, its a bit more complicated. I'll have to look at a few more tutorials :p. But thanks again for your explanation it helps a long way :)

Link to comment
Share on other sites

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.