Google Answers Logo
View Question
 
Q: Java function for HMAC-MD5 hash. ( Answered 5 out of 5 stars,   1 Comment )
Question  
Subject: Java function for HMAC-MD5 hash.
Category: Computers > Security
Asked by: three57-ga
List Price: $10.00
Posted: 19 Jan 2003 11:13 PST
Expires: 18 Feb 2003 11:13 PST
Question ID: 145583
I need a small program/function to generate a HMAC-MD5 hash signature
value in
Java.  I have a secret private key value that I need to use to
hash several fields represented as one long string concatenated
together with ^

Given a key similar to "HyP8N54REmgEX33V" (I made this up, but its of
that length) I need to generate a hash of a string similar to
"loginid^55^UTCtimeinsecondssince1970^9.50^"

The ^ are to divide
the fields and are part of the hash.

The function should return a String representation of the hashed
value.
An example in PHP is:

function InsertFP ($loginid, $txnkey, $amount, $sequence, $currency =
"")
{

$tstamp = time ();

$fingerprint = hmac ($txnkey, $loginid . "^" . $sequence . "^" .
$tstamp . "^" . $amount . "^" . $currency);

The function skeleton could be:

public static String getKey(String loginID, String sequence,String
time,String amount){

}

Thanks
-Brad
Answer  
Subject: Re: Java function for HMAC-MD5 hash.
Answered By: answerguru-ga on 19 Jan 2003 11:45 PST
Rated:5 out of 5 stars
 
Hi Brad,

The HMAC-MD5 has been provided by Michael Lecuyer as freely available
source at:

http://www.theorem.com/java/Free.htm#HMAC-MD5

The full source code from the above resource has been reproduced here
for your convenience, but you can alternatively just download the java
source file available at the above URL.


/* BEGIN SOURCE CODE */

package com.theorem.misc;

import com.theorem.misc.MD5;

/**
 * JAVA translation of the hmac_md5() function from RFC 2104.
 * <P>
 * HMAC: Keyed-Hashing for Message Authentication.
 * <P>
 * Copyright (C) Michael Lecuyer (1999).  All Rights Reserved.
 * <P>
 * This source code and extensions of it may be used freely as long as
the
 * copyright notice above and this paragraph are included in all
copies and
 * derivative works.
 * <P>
 * A notice about the original C code:
 * Copyright (C) The Internet Society (1999).  All Rights Reserved.
 * <P>
 * This document and translations of it may be copied and furnished to
 * others, and derivative works that comment on or otherwise explain
it
 * or assist in its implementation may be prepared, copied, published
 * and distributed, in whole or in part, without restriction of any
 * kind, provided that the above copyright notice and this paragraph
are
 * included on all such copies and derivative works. However, this
 * document itself may not be modified in any way, such as by removing
 * the copyright notice or references to the Internet Society or other
 * Internet organizations, except as needed for the purpose of
 * developing Internet standards in which case the procedures for
 * copyrights defined in the Internet Standards process must be
 * followed, or as required to translate it into languages other than
 * English. The limited permissions granted above are perpetual and
will
 * not be revoked by the Internet Society or its successors or
assigns.
 * This document and the information contained herein is provided on
an
 * "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
 * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
 * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE."
 * <P>
 *
 * @author Michael Lecuyer <mjl@theorem.com>.
 * @version 1.0 December 26, 1999
 */
public class HMAC_MD5
{
   /**
   * Run standard tests from the RFC:
   */
   public static void main(String arg[])
   {
      String expectedHash;
      byte digest[];
      HMAC_MD5 hm;

      System.out.println("Test Vectors from RFC 2104 - HMAC:
Keyed-Hashing for Message Authentication");
      System.out.println("This test uses HMAC-MD5.");

      System.out.println();
      System.out.println("Test #1:");
      // Test #1:
      byte key1[] = {
         (byte)0x0b, (byte)0x0b, (byte)0x0b, (byte)0x0b, (byte)0x0b,
(byte)0x0b, (byte)0x0b, (byte)0x0b,
         (byte)0x0b, (byte)0x0b, (byte)0x0b, (byte)0x0b, (byte)0x0b,
(byte)0x0b, (byte)0x0b, (byte)0x0b
      };
      String text1 = "Hi There";
      expectedHash = "0X9294727A3638BB1C13F48EF8158BFC9D";

      hm = new HMAC_MD5(key1);
      hm.addData(text1.getBytes());
      digest = hm.sign();

      System.out.println("Calculated hash 0X" + hm);
      System.out.println("  Expected hash " + expectedHash);

      // Test #2
      System.out.println();
      System.out.println("Test #2:");

      byte key2[] = "Jefe".getBytes();
      String text2 = "what do ya want for nothing?";
      expectedHash = "0X750C783E6AB0B503EAA86E310A5DB738";

      hm = new HMAC_MD5(key2);
      hm.addData(text2.getBytes());
      digest = hm.sign();
      System.out.println("Calculated hash 0X" + hm);
      System.out.println("  Expected hash " + expectedHash);

      // Test #3
      System.out.println();
      System.out.println("Test #3:");

      byte key3[] = {
         (byte)0xaa, (byte)0xaa, (byte)0xaa, (byte)0xaa, (byte)0xaa,
(byte)0xaa, (byte)0xaa, (byte)0xaa,
         (byte)0xaa, (byte)0xaa, (byte)0xaa, (byte)0xaa, (byte)0xaa,
(byte)0xaa, (byte)0xaa, (byte)0xaa
      };

      byte text3[] = {
         (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd,
(byte)0xdd, (byte)0xdd, (byte)0xdd,
         (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd,
(byte)0xdd, (byte)0xdd, (byte)0xdd,
         (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd,
(byte)0xdd, (byte)0xdd, (byte)0xdd,
         (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd,
(byte)0xdd, (byte)0xdd, (byte)0xdd,
         (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd,
(byte)0xdd, (byte)0xdd, (byte)0xdd,
         (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd, (byte)0xdd,
(byte)0xdd, (byte)0xdd, (byte)0xdd,
         (byte)0xdd, (byte)0xdd
      };

      expectedHash = "0X56BE34521D144C88DBB8C733F0E8B3F6";
      byte eh[] = {
         (byte)0x56, (byte)0xBE, (byte)0x34, (byte)0x52, (byte)0x1D,
(byte)0x14, (byte)0x4C, (byte)0x88,
         (byte)0xDB, (byte)0xB8, (byte)0xC7, (byte)0x33, (byte)0xF0,
(byte)0xE8, (byte)0xB3, (byte)0xF6
      };

      hm = new HMAC_MD5(key3);
      hm.addData(text3);
      digest = hm.sign();
      System.out.println("Calculated hash 0X" + hm);
      System.out.println("  Expected hash " + expectedHash);
      System.out.println("Signature Verification: " + hm.verify(eh));
   }

   /**
   * Digest to be returned upon completion of the HMAC_MD5.
   */
   private byte digest[];

   /**
   * Inner Padding.
   */
   private byte kIpad[];

   /**
   * Outer Padding.
   */
   private byte kOpad[];

   /**
   * Inner MD5 object.
   */
   private MD5 innerMD5;

   /**
   * Constructor
   */
   HMAC_MD5(byte key[])
   {
      int kLen = key.length;

      // if key is longer than 64 bytes reset it to key=MD5(key)
      if (kLen > 64)
      {
         MD5 md5 = new MD5();
         md5.update(key);
         key = md5.digest();
      }

      kIpad = new byte[64];   // inner padding - key XORd with ipad

      kOpad = new byte[64];   // outer padding - key XORd with opad

      // start out by storing key in pads
      System.arraycopy(key, 0, kIpad, 0, kLen);
      System.arraycopy(key, 0, kOpad, 0, kLen);

      // XOR key with ipad and opad values
      for (int i = 0; i < 64; i++)
      {
         kIpad[i] ^= 0x36;
         kOpad[i] ^= 0x5c;
      }

      clear(); // Initialize the first digest.
   }


   /**
   * Clear the HMAC_MD5 object.
   */
   public void clear()
   {
      innerMD5 = new MD5();
      innerMD5.update(kIpad); // Intialize the inner pad.

      digest = null;          // mark the digest as incomplete.
   }

   /**
   * HMAC_MD5 function.
   *
   * @param text Text to process
   *
   * @param key Key to use for HMAC hash.
   *
   * @return hash
   */
   public void addData(byte text[])
   {
      addData(text, 0, text.length);
   }

   /**
   * HMAC_MD5 function.
   *
   * @param text Text to process
   *
   * @param textStart   Start position of text in text buffer.
   * @param textLen Length of text to use from text buffer.
   * @param key Key to use for HMAC hash.
   *
   * @return hash
   */
   public void addData(byte text[], int textStart, int textLen)
   {
      innerMD5.update(text, textStart, textLen);   // then text of
datagram.
   }

   public byte [] sign()
   {
      MD5 md5;

      /*
      * the HMAC_MD5 transform looks like:
      *
      * MD5(K XOR opad, MD5(K XOR ipad, text))
      *
      * where K is an n byte key
      * ipad is the byte 0x36 repeated 64 times
      * opad is the byte 0x5c repeated 64 times
      * and text is the data being protected
      */

      // Perform inner MD5

      digest = innerMD5.digest();            // finish up 1st pass.

       // Perform outer MD5

      md5 = new MD5();                       // Init md5 for 2nd pass.
      md5.update(kOpad);                     // Use outer pad.
      md5.update(digest);                    // Use results of first
pass.
      digest = md5.digest();                 // Final result.

      return digest;
   }

   /**
   * Validate a signature against the current digest.
   * Compares the hash against the signature.
   *
   * @param signature
   *
   * @return True if the signature matches the calculated hash.
   */
   public boolean verify(byte signature[])
   {
      // The digest may not have been calculated.  If it's null, force
a calculation.
      if (digest == null)
         sign();

      int sigLen = signature.length;
      int digLen = digest.length;

      if (sigLen != digLen)
         return false;  // Different lengths, not a good sign.

      for (int i = 0; i < sigLen; i++)
         if (signature[i] != digest[i])
            return false;  // Mismatch. Misfortune.

      return true;   // Signatures matched. Perseverance furthers.
   }

   /**
   *  Return the digest as a HEX string.
   *
   * @return a hex representation of the MD5 digest.
   */
   public String toString()
   {
      // If not already calculated, do so.
      if (digest == null)
         sign();

      StringBuffer r = new StringBuffer();
      final String hex = "0123456789ABCDEF";
      byte b[] = digest;

      for (int i = 0; i < 16; i++)
      {
         int c = ((b[i]) >>> 4) & 0xf;
         r.append(hex.charAt(c));
         c = ((int)b[i] & 0xf);
         r.append(hex.charAt(c));
      }

      return r.toString();
   }
}


/* END SOURCE CODE */

The class listing above goes beyond your initial request in that it is
able to generate an encrypted key as well as convert it back to the
fields initially input into the function. I believe you will require
this as well in order to decrypt your data at a later point. The
main() program gives you an idea of how to use the enclosed methods to
encrypt/decrypt your information.

Hope that serves your purpose....if you require any clarification,
please do not hesitate to ask :)

------------------------------------
Search Terms (Google):

"HMAC-MD5" hash function algorithm
------------------------------------

Cheers!

answerguru-ga

Clarification of Answer by answerguru-ga on 19 Jan 2003 13:42 PST
I thought you would also find the follow English description of the
HMAC-MD5 algorithm:

http://www.ietf.org/rfc/rfc1321.txt

answerguru-ga
three57-ga rated this answer:5 out of 5 stars
That was enough information to get me where I wanted to go.  Thanks!

Comments  
Subject: Re: Java function for HMAC-MD5 hash.
From: richardbondi-ga on 19 Jan 2003 13:22 PST
 
It is not a good idea to use cryptographic source code that is just
lying around somewhere on the web. Instead of using what Google
suggests here, consider instead:

- The Java Cryptography Extensions from Sun, which include HMAC-MD5.
Just search java.sun.com for "cryptography".
- The Cryptix Open Source library at www.cryptix.org. One especially
nice feature is that they include the authoritative input/output tests
for whether an algorithm has been implemented properly.

If you need help implementing cryptography in Java, there is good on
it from O'Reilly press.

Best,
r:b:
www.cryptovb.com

Important Disclaimer: Answers and comments provided on Google Answers are general information, and are not intended to substitute for informed professional medical, psychiatric, psychological, tax, legal, investment, accounting, or other professional advice. Google does not endorse, and expressly disclaims liability for any product, manufacturer, distributor, service or service provider mentioned or any opinion expressed in answers or comments. Please read carefully the Google Answers Terms of Service.

If you feel that you have found inappropriate content, please let us know by emailing us at answers-support@google.com with the question ID listed above. Thank you.
Search Google Answers for
Google Answers  


Google Home - Answers FAQ - Terms of Service - Privacy Policy