20060623 java hmacs

From s5h.net

Jump to: navigation, search

Java seems to take an awfully long time to generate hmacshas. I've no idea why. I would have thought that there would be all sorts of JNI code going on in the background with algorithms for this sort of work. As it turns out, it can take around half a second using the Mac class to generate a HmacSha, and only a fraction of the time on my test box.

import java.io.*;
import java.security.*;
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;

public class HmacSha {
	// HmacSha 0.1 - E Neville, 20060623
	//  
	// produce a hmacsha1 of given data bytes based on given key
	// bytes slightly faster than Mac class

	private static int SHA_DIGESTSIZE = 20;
	private static int SHA_BLOCKSIZE = 64;

	public HmacSha() {
	}

	private String hexString( byte a[] ) {
		String hexChars = "0123465789abcdef";
		StringBuffer retVal = new StringBuffer();
		byte b,c;

		for( int i=0 ; i<a.length ; i++ ) {
			b = (byte)(( a[i] & 0xf0 ) >> 4 );
			c = (byte)( a[i] & 0×0f );
			if( retVal.length() > 0 ) {
				retVal.append( " " );
			}
			retVal.append( hexChars.charAt(b) + "" +
					hexChars.charAt(c) );
		}
		return( retVal.toString() );
	}

	public byte[] hmacShaBytes( byte k[], byte d[] ) {
		try {
			byte buf[] = new byte[SHA_BLOCKSIZE];
			MessageDigest ictx =
				MessageDigest.getInstance( "SHA-1″ );
			MessageDigest octx =
				MessageDigest.getInstance( "SHA-1″ );

			if( k.length > SHA_BLOCKSIZE ) {
				MessageDigest kctx =
					MessageDigest.getInstance(
							"SHA-1″ );
				kctx.update( k );
				k = kctx.digest();
			}

			// inner digest
			for( int i=0 ; i<k.length ; i++ ) {
				buf[i] = (byte)( k[i] ^ 0×36 );
			}

			for( int i=k.length ; i<SHA_BLOCKSIZE ; i++ ) {
				buf[i] = 0×36;
			}

			ictx.update( buf );
			ictx.update( d );
			// byte isha[] = ictx.digest();

			// outter digest
			for( int i=0 ; i<k.length ; i++ ) {
				buf[i] = (byte)( k[i] ^ 0×5c );
			}

			for( int i=k.length ; i<SHA_BLOCKSIZE ; i++ ) {
				buf[i] = 0×5c;
			}

			octx.update( buf );
			octx.update( ictx.digest() );
			buf = octx.digest();
			return( buf );
		}
		catch( java.security.NoSuchAlgorithmException nsae ) {
		}

		return( null );

	}

	public static void main( String []args ) {
		HmacSha hs = new HmacSha();
		byte a[] = { 0, 1, 2, 3, 4, 5, 20 };
		String key = "Jefe", data = "what do ya want for nothing?";

		Date ihmacstart = new Date();
		System.out.println(
				hs.hexString(
					hs.hmacShaBytes( key.getBytes(), data.getBytes() ) ) );
		Date ihmacstop = new Date();

		Date jhmacstart = new Date();
		try {
			SecretKey sk = new SecretKeySpec(
					key.getBytes(),
					"HmacSha1″ );

			Mac m = Mac.getInstance( sk.getAlgorithm() );
			m.init( sk );
			m.update( data.getBytes() );
			byte hmac[] = m.doFinal();
			System.out.println( hs.hexString( hmac ) );
			Date jhmacstop = new Date();

			System.out.println( "Generating manually: " +
					( ( ihmacstop.getTime()
					    - ihmacstart.getTime() )
					)+ "\n" +
					"Generating using JRE: " +
					( ( jhmacstop.getTime()
					    - jhmacstart.getTime() )
					) + "\n" );
		}
		catch( java.security.NoSuchAlgorithmException nsae ) {
		}
		catch( java.security.InvalidKeyException ike ) {
		}

	}
}

When run, this is quite apparent:

$ /usr/local/jdk1.5.0_06/bin/javac ./HmacSha.java  && /usr/local/jdk1.5.0_06/bin/java HmacSha
ef fc df 5a e6 eb 2f a2 d2 74 15 d6 f1 84 df 9c 26 9a 7c 79
ef fc df 5a e6 eb 2f a2 d2 74 15 d6 f1 84 df 9c 26 9a 7c 79
Generating manually: 33
Generating using JRE: 474

What bothers me is that a library problem like this is not handled, surely someone who works for Sun would have pointed this out. I know relatively little compared to the authors of this, why didn't someone there prevent this, or at least let it be known in the documentation, and provide the snippet that I have pasted here, it's just a short re-write of RFC2202 that I did by chance.

Personal tools
Google AdSense