// --- BEGIN COPYRIGHT BLOCK --- // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; version 2 of the License. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // (C) 2007 Red Hat, Inc. // All rights reserved. // --- END COPYRIGHT BLOCK --- package com.netscape.cmsutil.util; import java.security.MessageDigest; /** * This class implements the HMAC algorithm specified in RFC 2104 using * any MessageDigest. * * @author mikep * @version $Revision$, $Date$ * @see java.security.MessageDigest */ public class HMACDigest implements Cloneable { public static final int PAD_BYTES = 64; public static final int IPAD = 0x36; public static final int OPAD = 0x5C; /** * inner padding - key XORd with ipad */ private byte[] mKeyIpad = new byte[PAD_BYTES]; /** * outer padding - key XORd with opad */ private byte[] mKeyOpad = new byte[PAD_BYTES]; /** * The real MessageDigest */ private MessageDigest mMD = null; /** * Creates an HMACDigest * * @param md The MessageDigest to be used for the HMAC calculation. It * must be clonable. */ public HMACDigest(MessageDigest md) { mMD = md; } /** * Creates an HMACDigest and initializes the HMAC function * with the given key. * * @param md The MessageDigest to be used for the HMAC calculation. It * must be clonable. * @param key The key value to be used in the HMAC calculation */ public HMACDigest(MessageDigest md, byte[] key) { this(md); init(key); } /** * Return the MessageDigest used for this HMAC */ public MessageDigest getMessageDigest() { return mMD; } /** * Initialize the HMAC function * * The HMAC transform looks like: * * hash(key XOR opad, hash(key XOR ipad, text)) * * where key 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 * * This routine must be called after every reset. * * @param key The password used to protect the hash value */ public void init(byte[] key) { int i; reset(); // If the key is longer than 64 bytes, just hash it down if (key.length > 64) { key = mMD.digest(key); mMD.reset(); // Redundant? } // Copy the key. Truncate if key is too long for (i = 0; i < key.length && i < PAD_BYTES; i++) { mKeyIpad[i] = key[i]; mKeyOpad[i] = key[i]; } // XOR in the pads for (i = 0; i < PAD_BYTES; i++) { mKeyIpad[i] ^= IPAD; mKeyOpad[i] ^= OPAD; } mMD.update(mKeyIpad); // Hmmm, we really shouldn't key Opad around in memory for so // long, but it would just force the user to key their key around // until digest() time. Oh well, at least clear the key and Ipad for (i = 0; i < PAD_BYTES; i++) { mKeyIpad[i] = 0; } for (i = 0; i < key.length; i++) { key[0] = 0; } } /** * Updates the digest using the specified array of bytes. * * @param input the array of bytes. */ public void update(byte[] input) { mMD.update(input); } /** * Completes the HMAC computation with the outer pad * The digest is reset after this call is made. * * @return the array of bytes for the resulting hash value. */ public byte[] digest() { byte[] finalDigest; byte[] innerDigest = mMD.digest(); mMD.reset(); // Redundant? mMD.update(mKeyOpad); mMD.update(innerDigest); finalDigest = mMD.digest(); reset(); // Clear pad arrays return finalDigest; } /** * Resets the digest for further use. */ public void reset() { int i; mMD.reset(); // Clear out the pads for (i = 0; i < PAD_BYTES; i++) { mKeyIpad[i] = 0; mKeyOpad[i] = 0; } } /** * Clone the HMACDigest * * @return a clone if the implementation is cloneable. * @exception CloneNotSupportedException if this is called on a * MessageDigest implementation that does not support Cloneable. */ public Object clone() throws CloneNotSupportedException { int i; HMACDigest hd = (HMACDigest) super.clone(); hd.mKeyOpad = new byte[PAD_BYTES]; hd.mKeyIpad = new byte[PAD_BYTES]; for (i = 0; i < PAD_BYTES; i++) { hd.mKeyOpad[i] = mKeyOpad[i]; hd.mKeyIpad[i] = mKeyIpad[i]; } hd.mMD = (MessageDigest) mMD.clone(); return hd; } }