📄 hmac.java
字号:
// $Id: HMAC.java,v 1.3 1998/01/28 05:58:10 hopwood Exp $//// $Log: HMAC.java,v $// Revision 1.3 1998/01/28 05:58:10 hopwood// + Documentation changes.//// Revision 1.2 1998/01/12 04:17:51 hopwood// + HMAC rewrite.//// Revision 1.1.1.1.1 1998/01/12 hopwood// + Rewritten as a MessageDigest.//// Revision 1.1.1.1 1998/01/04 03:27:05 hopwood// + Imported to CVS (tagged as 'start').//// Revision 0.1.0 1998/01/03 hopwood// + Original version.//// $Endlog$/* * Copyright (c) 1997 Systemics Ltd * on behalf of the Cryptix Development Team. All rights reserved. */package cryptix.provider.mac;import cryptix.CryptixException;import cryptix.util.core.Debug;import cryptix.util.core.Hex;import cryptix.util.core.ArrayUtil;import java.io.PrintWriter;import java.security.MessageDigest;import java.security.Security;import java.security.Parameterized;import java.security.InvalidParameterException;/** * A class to implement the HMAC message authentication code, as described in * RFC 2104. * <p> * The definition of HMAC-X for a given message digest algorithm X depends * on that algorithm's internal block size, which is passed into the constructor * when this class is used directly. Normally, this class will be obtained * indirectly via the JCA API, e.g. <code>MessageDigest.getInstance("HMAC-SHA-1")</code> * if SHA-1 is to be used as the digest algorithm. * <p> * The key can be set as a byte array using the "key" parameter. It is <em>not</em> * reset after the MAC has been returned (this enables successive MACs using the * same key to be calculated efficiently). For example: * <pre> * import java.security.Parameterized; * <br> * MessageDigest hmac = MessageDigest.getInstance("HMAC-SHA-1"); * byte[] key = ..., input1 = ..., input2 = ...; * ((Parameterized) mac).setParameter("key", key); * byte[] mac1 = hmac.digest(input1); * byte[] mac2 = hmac.digest(input2); * </pre> * <p> * Parameters other than "key" are passed through to the MessageDigest object, * but this can only be done when the key is not set. An explicit call to * <code>reset()</code> will 'unset' the key. * <p> * This implementation does not support truncation of the output MAC. If * truncation is desired, the caller should use only the relevant initial bits * of the output array. * <p> * Note that although this class implements <samp>Cloneable</samp>, its * <code>clone()</code> method will throw a <samp>CloneNotSupportedException</samp> * if the underlying MessageDigest cannot be cloned. This should not be the * case for any of the Cryptix-supported digest algorithms. * <p> * HMAC is similar, but <em>not</em> identical to the MAC used by Secure Sockets * Layer version 3. The JCA standard algorithm name of the variant used in SSL v3 * is "SSL3MAC-X", for a message digest name X. * <p> * <b>References:</b> * <ol> * <li> RFC 2104, <cite>HMAC: Keyed-Hashing for Message Authentication</cite> * <li> RFC 2202, <cite>Test Cases for HMAC-MD5 and HMAC-SHA-1</cite> * </ol> * <p> * <b>Copyright</b> © 1997 * <a href="http://www.systemics.com/">Systemics Ltd</a> on behalf of the * <a href="http://www.systemics.com/docs/cryptix/">Cryptix Development Team</a>. * <br>All rights reserved. * <p> * <b>$Revision: 1.3 $</b> * @author David Hopwood * @since Cryptix 3.0.3 */public class HMACextends MessageDigestimplements Parameterized, Cloneable{// Debugging methods and vars.//........................................................................... private static final boolean DEBUG = Debug.GLOBAL_DEBUG; private static final int debuglevel = DEBUG ? Debug.getLevel("HMAC") : 0; private static final PrintWriter err = DEBUG ? Debug.getOutput() : null; private static void debug(String s) { err.println("HMAC: " + s); }// Constants and variables//........................................................................... private MessageDigest md; // precomputation not implemented yet. // private MessageDigest precompInner; // private MessageDigest precompOuter; private int blockSize; private int length; private byte[] key; // defaults to null// Constructors//........................................................................... /** * Constructs an HMAC object for the given MD algorithm name and block * size. * <p> * Do not rely on being able to use this constructor to obtain a subclass * of MessageDigest; when Cryptix is changed to use the Java 1.2 provider * architecture, it will be a subclass of MessageDigestSpi instead. * * @param mdAlgorithm the standard JCA algorithm name of the message * digest to be used. * @param mdBlockSize the internal block size of the digest algorithm. */ public HMAC(String mdAlgorithm, int mdBlockSize) { super("HMAC-" + mdAlgorithm); try { md = MessageDigest.getInstance(mdAlgorithm, "Cryptix"); } catch (Exception e) { try { md = MessageDigest.getInstance(mdAlgorithm); } catch (Exception e2) { throw new CryptixException(getAlgorithm() + ": Unable to instantiate the " + mdAlgorithm + " MessageDigest\n" + e2); } } blockSize = mdBlockSize; length = md.digest().length; // in Java 1.2, use "length = md.getDigestLength();" key = new byte[mdBlockSize]; } /** This constructor is here to implement cloneability of this class. */ private HMAC(HMAC mac) throws CloneNotSupportedException { super(mac.getAlgorithm()); md = (MessageDigest) (mac.md.clone()); blockSize = mac.blockSize; length = mac.length; byte[] _key = mac.key; key = (_key == null) ? null : (byte[]) (_key.clone()); }// Cloneable method implementation//........................................................................... /** * Returns a copy of this HMAC object. * * @throws CloneNotSupportedException if the underlying MessageDigest is not * cloneable. */ public Object clone() throws CloneNotSupportedException { return new HMAC(this); }// SPI methods//........................................................................... /** * Resets this object disregarding any temporary data present at the * time of the invocation of this call. */ protected void engineReset() { md.reset(); key = null; } /** Continues an HMAC digest using the input byte. */ protected void engineUpdate(byte input) { if (key == null) throw new IllegalStateException(getAlgorithm() + ": Key has not been set"); md.update(input); } /** * Hashes a byte array from a given offset for a specified length. * * @param input byte array from which data is to be hashed. * @param offset start index of bytes to hash in input. * @param len number of bytes to hash. */ protected void engineUpdate(byte[] input, int offset, int len) { if (key == null) throw new IllegalStateException(getAlgorithm() + ": Key has not been set"); md.update(input, offset, len); } /** * Calculates the final MAC. * <p> * RFC 2104 describes HMAC as follows: * <p> * The definition of HMAC requires a cryptographic hash function, which * we denote by H, and a secret key K. We assume H to be a cryptographic * hash function where data is hashed by iterating a basic compression * function on blocks of data. We denote by B the byte-length of such * blocks (B=64 for all the above mentioned examples of hash functions), * and by L the byte-length of hash outputs (L=16 for MD5, L=20 for * SHA-1). The authentication key K can be of any length up to B, the * block length of the hash function. Applications that use keys longer * than B bytes will first hash the key using H and then use the * resultant L byte string as the actual key to HMAC. In any case the * minimal recommended length for K is L bytes (as the hash output * length). See section 3 for more information on keys. * <p> * We define two fixed and different strings <i>ipad</i> and <i>opad</i> * as follows (the 'i' and 'o' are mnemonics for inner and outer): * <blockquote> * <i>ipad</i> = the byte 0x36 repeated B times <br> * <i>opad</i> = the byte 0x5C repeated B times. * </blockquote> * <p> * To compute HMAC over the data 'text' we perform * <blockquote> * H(K XOR <i>opad</i>, H(K XOR <i>ipad</i>, text)) * </blockquote> * <p> * Namely, * <ol> * <li> append zeros to the end of K to create a B byte string * (e.g., if K is of length 20 bytes and B=64, then K will be * appended with 44 zero bytes 0x00) * <li> XOR (bitwise exclusive-OR) the B byte string computed in step * (1) with <i>ipad</i> * <li> append the stream of data 'text' to the B byte string resulting * from step (2) * <li> apply H to the stream generated in step (3) * <li> XOR (bitwise exclusive-OR) the B byte string computed in * step (1) with <i>opad</i> * <li> append the H result from step (4) to the B byte string * resulting from step (5) * <li> apply H to the stream generated in step (6) and output * the result * </ol> */ protected byte[] engineDigest() {if (DEBUG && debuglevel >= 7) debug("engineDigest()"); if (key == null) throw new IllegalStateException(getAlgorithm() + ": Key has not been set"); byte[] innerhash = md.digest(); byte[] xorblock = new byte[blockSize]; for (int i = 0; i < key.length; i++) xorblock[i] = (byte) (0x5C ^ key[i]); for (int i = key.length; i < blockSize; i++) xorblock[i] = (byte) 0x5C; md.update(xorblock); byte[] result = md.digest(innerhash);if (DEBUG && debuglevel >= 7) debug("... = <" + Hex.toString(result) + ">"); return result; } /** <b>SPI</b>: Returns the digest length in bytes. */ protected int engineGetDigestLength() { return length; } public void setParameter(String param, Object value) throws InvalidParameterException { engineSetParameter(param, value); } public Object getParameter(String param) throws InvalidParameterException { return engineGetParameter(param); } protected void engineSetParameter(String param, Object value) throws InvalidParameterException { try { if (param.equalsIgnoreCase("key")) { setKey((byte[]) value); return; } // don't allow setting hash parameters after the key has been initialised // (allowing this would inhibit planned optimisations). if (key != null) throw new InvalidParameterException(getAlgorithm() + ": Can't set parameter after key has been initialised"); ((Parameterized) md).setParameter(param, value); } catch (Exception e) { throw new InvalidParameterException(e.getMessage()); } } protected Object engineGetParameter(String param) throws InvalidParameterException { try { return ((Parameterized) md).getParameter(param); } catch (Exception e) { throw new InvalidParameterException(e.getMessage()); } }// Own methods//........................................................................... /** * Sets the key value. If the MessageDigest is cloneable, it is possible to * do precomputations for the inner and outer digests (but this is not * implemented yet). * * @param userkey the bytes of the key. */ private void setKey(byte[] userkey) {if (DEBUG && debuglevel >= 7) debug("setKey(<" + Hex.toString(userkey) + ">)"); if (key != null) md.reset(); // junk incomplete input if (userkey.length > blockSize) key = md.digest(userkey); else key = (byte[]) (userkey.clone()); byte[] xorblock = new byte[blockSize]; for (int i = 0; i < key.length; i++) xorblock[i] = (byte) (0x36 ^ key[i]); for (int i = key.length; i < blockSize; i++) xorblock[i] = (byte) 0x36; md.update(xorblock); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -